obsidian-agent-fleet 0.10.0 → 0.12.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/README.md +63 -16
- package/package.json +1 -1
- package/plugin/main.js +286 -115
- package/plugin/manifest.json +1 -1
- package/plugin/styles.css +207 -0
package/plugin/main.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
"use strict";var Xi=Object.create;var bs=Object.defineProperty;var Qi=Object.getOwnPropertyDescriptor;var Zi=Object.getOwnPropertyNames;var er=Object.getPrototypeOf,tr=Object.prototype.hasOwnProperty;var Ve=(r,t)=>()=>(t||r((t={exports:{}}).exports,t),t.exports),sr=(r,t)=>{for(var e in t)bs(r,e,{get:t[e],enumerable:!0})},Xa=(r,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of Zi(t))!tr.call(r,a)&&a!==e&&bs(r,a,{get:()=>t[a],enumerable:!(s=Qi(t,a))||s.enumerable});return r};var Ye=(r,t,e)=>(e=r!=null?Xi(er(r)):{},Xa(t||!r||!r.__esModule?bs(e,"default",{value:r,enumerable:!0}):e,r)),ar=r=>Xa(bs({},"__esModule",{value:!0}),r);var mt=Ve((yc,Ln)=>{"use strict";var Rn=["nodebuffer","arraybuffer","fragments"],In=typeof Blob<"u";In&&Rn.push("blob");Ln.exports={BINARY_TYPES:Rn,CLOSE_TIMEOUT:3e4,EMPTY_BUFFER:Buffer.alloc(0),GUID:"258EAFA5-E914-47DA-95CA-C5AB0DC85B11",hasBlob:In,kForOnEventAttribute:Symbol("kIsForOnEventAttribute"),kListener:Symbol("kListener"),kStatusCode:Symbol("status-code"),kWebSocket:Symbol("websocket"),NOOP:()=>{}}});var rs=Ve((vc,js)=>{"use strict";var{EMPTY_BUFFER:Dr}=mt(),Ca=Buffer[Symbol.species];function Pr(r,t){if(r.length===0)return Dr;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 Ca(e.buffer,e.byteOffset,s):e}function Mn(r,t,e,s,a){for(let n=0;n<a;n++)e[s+n]=r[n]^t[n&3]}function Fn(r,t){for(let e=0;e<r.length;e++)r[e]^=t[e&3]}function Rr(r){return r.length===r.buffer.byteLength?r.buffer:r.buffer.slice(r.byteOffset,r.byteOffset+r.length)}function _a(r){if(_a.readOnly=!0,Buffer.isBuffer(r))return r;let t;return r instanceof ArrayBuffer?t=new Ca(r):ArrayBuffer.isView(r)?t=new Ca(r.buffer,r.byteOffset,r.byteLength):(t=Buffer.from(r),_a.readOnly=!1),t}js.exports={concat:Pr,mask:Mn,toArrayBuffer:Rr,toBuffer:_a,unmask:Fn};if(!process.env.WS_NO_BUFFER_UTIL)try{let r=require("bufferutil");js.exports.mask=function(t,e,s,a,n){n<48?Mn(t,e,s,a,n):r.mask(t,e,s,a,n)},js.exports.unmask=function(t,e){t.length<32?Fn(t,e):r.unmask(t,e)}}catch{}});var Bn=Ve((bc,Nn)=>{"use strict";var On=Symbol("kDone"),Aa=Symbol("kRun"),Ea=class{constructor(t){this[On]=()=>{this.pending--,this[Aa]()},this.concurrency=t||1/0,this.jobs=[],this.pending=0}add(t){this.jobs.push(t),this[Aa]()}[Aa](){if(this.pending!==this.concurrency&&this.jobs.length){let t=this.jobs.shift();this.pending++,t(this[On])}}};Nn.exports=Ea});var Ht=Ve((wc,Hn)=>{"use strict";var os=require("zlib"),Un=rs(),Ir=Bn(),{kStatusCode:$n}=mt(),Lr=Buffer[Symbol.species],Mr=Buffer.from([0,0,255,255]),qs=Symbol("permessage-deflate"),ft=Symbol("total-length"),$t=Symbol("callback"),wt=Symbol("buffers"),jt=Symbol("error"),Hs,Da=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,!Hs){let e=this._options.concurrencyLimit!==void 0?this._options.concurrencyLimit:10;Hs=new Ir(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[$t];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){Hs.add(a=>{this._decompress(t,e,(n,i)=>{a(),s(n,i)})})}compress(t,e,s){Hs.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"?os.Z_DEFAULT_WINDOWBITS:this.params[n];this._inflate=os.createInflateRaw({...this._options.zlibInflateOptions,windowBits:i}),this._inflate[qs]=this,this._inflate[ft]=0,this._inflate[wt]=[],this._inflate.on("error",Or),this._inflate.on("data",jn)}this._inflate[$t]=s,this._inflate.write(t),e&&this._inflate.write(Mr),this._inflate.flush(()=>{let n=this._inflate[jt];if(n){this._inflate.close(),this._inflate=null,s(n);return}let i=Un.concat(this._inflate[wt],this._inflate[ft]);this._inflate._readableState.endEmitted?(this._inflate.close(),this._inflate=null):(this._inflate[ft]=0,this._inflate[wt]=[],e&&this.params[`${a}_no_context_takeover`]&&this._inflate.reset()),s(null,i)})}_compress(t,e,s){let a=this._isServer?"server":"client";if(!this._deflate){let n=`${a}_max_window_bits`,i=typeof this.params[n]!="number"?os.Z_DEFAULT_WINDOWBITS:this.params[n];this._deflate=os.createDeflateRaw({...this._options.zlibDeflateOptions,windowBits:i}),this._deflate[ft]=0,this._deflate[wt]=[],this._deflate.on("data",Fr)}this._deflate[$t]=s,this._deflate.write(t),this._deflate.flush(os.Z_SYNC_FLUSH,()=>{if(!this._deflate)return;let n=Un.concat(this._deflate[wt],this._deflate[ft]);e&&(n=new Lr(n.buffer,n.byteOffset,n.length-4)),this._deflate[$t]=null,this._deflate[ft]=0,this._deflate[wt]=[],e&&this.params[`${a}_no_context_takeover`]&&this._deflate.reset(),s(null,n)})}};Hn.exports=Da;function Fr(r){this[wt].push(r),this[ft]+=r.length}function jn(r){if(this[ft]+=r.length,this[qs]._maxPayload<1||this[ft]<=this[qs]._maxPayload){this[wt].push(r);return}this[jt]=new RangeError("Max payload size exceeded"),this[jt].code="WS_ERR_UNSUPPORTED_MESSAGE_LENGTH",this[jt][$n]=1009,this.removeListener("data",jn),this.reset()}function Or(r){if(this[qs]._inflate=null,this[jt]){this[$t](this[jt]);return}r[$n]=1007,this[$t](r)}});var qt=Ve((kc,Ws)=>{"use strict";var{isUtf8:qn}=require("buffer"),{hasBlob:Nr}=mt(),Br=[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 Ur(r){return r>=1e3&&r<=1014&&r!==1004&&r!==1005&&r!==1006||r>=3e3&&r<=4999}function Pa(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 $r(r){return Nr&&typeof r=="object"&&typeof r.arrayBuffer=="function"&&typeof r.type=="string"&&typeof r.stream=="function"&&(r[Symbol.toStringTag]==="Blob"||r[Symbol.toStringTag]==="File")}Ws.exports={isBlob:$r,isValidStatusCode:Ur,isValidUTF8:Pa,tokenChars:Br};if(qn)Ws.exports.isValidUTF8=function(r){return r.length<24?Pa(r):qn(r)};else if(!process.env.WS_NO_UTF_8_VALIDATE)try{let r=require("utf-8-validate");Ws.exports.isValidUTF8=function(t){return t.length<32?Pa(t):r(t)}}catch{}});var Fa=Ve((xc,Jn)=>{"use strict";var{Writable:jr}=require("stream"),Wn=Ht(),{BINARY_TYPES:Hr,EMPTY_BUFFER:zn,kStatusCode:qr,kWebSocket:Wr}=mt(),{concat:Ra,toArrayBuffer:zr,unmask:Gr}=rs(),{isValidStatusCode:Vr,isValidUTF8:Gn}=qt(),zs=Buffer[Symbol.species],st=0,Vn=1,Yn=2,Kn=3,Ia=4,La=5,Gs=6,Ma=class extends jr{constructor(t={}){super(),this._allowSynchronousEvents=t.allowSynchronousEvents!==void 0?t.allowSynchronousEvents:!0,this._binaryType=t.binaryType||Hr[0],this._extensions=t.extensions||{},this._isServer=!!t.isServer,this._maxPayload=t.maxPayload|0,this._skipUTF8Validation=!!t.skipUTF8Validation,this[Wr]=void 0,this._bufferedBytes=0,this._buffers=[],this._compressed=!1,this._payloadLength=0,this._mask=void 0,this._fragmented=0,this._masked=!1,this._fin=!1,this._opcode=0,this._totalPayloadLength=0,this._messageLength=0,this._fragments=[],this._errored=!1,this._loop=!1,this._state=st}_write(t,e,s){if(this._opcode===8&&this._state==st)return s();this._bufferedBytes+=t.length,this._buffers.push(t),this.startLoop(s)}consume(t){if(this._bufferedBytes-=t,t===this._buffers[0].length)return this._buffers.shift();if(t<this._buffers[0].length){let s=this._buffers[0];return this._buffers[0]=new zs(s.buffer,s.byteOffset+t,s.length-t),new zs(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 zs(s.buffer,s.byteOffset+t,s.length-t)),t-=s.length}while(t>0);return e}startLoop(t){this._loop=!0;do switch(this._state){case st:this.getInfo(t);break;case Vn:this.getPayloadLength16(t);break;case Yn:this.getPayloadLength64(t);break;case Kn:this.getMask();break;case Ia:this.getData(t);break;case La:case Gs: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[Wn.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=Vn:this._payloadLength===127?this._state=Yn: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=Kn:this._state=Ia}getMask(){if(this._bufferedBytes<4){this._loop=!1;return}this._mask=this.consume(4),this._state=Ia}getData(t){let e=zn;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&&Gr(e,this._mask)}if(this._opcode>7){this.controlMessage(e,t);return}if(this._compressed){this._state=La,this.decompress(e,t);return}e.length&&(this._messageLength=this._totalPayloadLength,this._fragments.push(e)),this.dataMessage(t)}decompress(t,e){this._extensions[Wn.extensionName].decompress(t,this._fin,(a,n)=>{if(a)return e(a);if(n.length){if(this._messageLength+=n.length,this._messageLength>this._maxPayload&&this._maxPayload>0){let i=this.createError(RangeError,"Max payload size exceeded",!1,1009,"WS_ERR_UNSUPPORTED_MESSAGE_LENGTH");e(i);return}this._fragments.push(n)}this.dataMessage(e),this._state===st&&this.startLoop(e)})}dataMessage(t){if(!this._fin){this._state=st;return}let e=this._messageLength,s=this._fragments;if(this._totalPayloadLength=0,this._messageLength=0,this._fragmented=0,this._fragments=[],this._opcode===2){let a;this._binaryType==="nodebuffer"?a=Ra(s,e):this._binaryType==="arraybuffer"?a=zr(Ra(s,e)):this._binaryType==="blob"?a=new Blob(s):a=s,this._allowSynchronousEvents?(this.emit("message",a,!0),this._state=st):(this._state=Gs,setImmediate(()=>{this.emit("message",a,!0),this._state=st,this.startLoop(t)}))}else{let a=Ra(s,e);if(!this._skipUTF8Validation&&!Gn(a)){let n=this.createError(Error,"invalid UTF-8 sequence",!0,1007,"WS_ERR_INVALID_UTF8");t(n);return}this._state===La||this._allowSynchronousEvents?(this.emit("message",a,!1),this._state=st):(this._state=Gs,setImmediate(()=>{this.emit("message",a,!1),this._state=st,this.startLoop(t)}))}}controlMessage(t,e){if(this._opcode===8){if(t.length===0)this._loop=!1,this.emit("conclude",1005,zn),this.end();else{let s=t.readUInt16BE(0);if(!Vr(s)){let n=this.createError(RangeError,`invalid status code ${s}`,!0,1002,"WS_ERR_INVALID_CLOSE_CODE");e(n);return}let a=new zs(t.buffer,t.byteOffset+2,t.length-2);if(!this._skipUTF8Validation&&!Gn(a)){let n=this.createError(Error,"invalid UTF-8 sequence",!0,1007,"WS_ERR_INVALID_UTF8");e(n);return}this._loop=!1,this.emit("conclude",s,a),this.end()}this._state=st;return}this._allowSynchronousEvents?(this.emit(this._opcode===9?"ping":"pong",t),this._state=st):(this._state=Gs,setImmediate(()=>{this.emit(this._opcode===9?"ping":"pong",t),this._state=st,this.startLoop(e)}))}createError(t,e,s,a,n){this._loop=!1,this._errored=!0;let i=new t(s?`Invalid WebSocket frame: ${e}`:e);return Error.captureStackTrace(i,this.createError),i.code=n,i[qr]=a,i}};Jn.exports=Ma});var Ba=Ve((Tc,Zn)=>{"use strict";var{Duplex:Sc}=require("stream"),{randomFillSync:Yr}=require("crypto"),Xn=Ht(),{EMPTY_BUFFER:Kr,kWebSocket:Jr,NOOP:Xr}=mt(),{isBlob:Wt,isValidStatusCode:Qr}=qt(),{mask:Qn,toBuffer:Dt}=rs(),at=Symbol("kByteLength"),Zr=Buffer.alloc(4),Vs=8*1024,Pt,zt=Vs,it=0,eo=1,to=2,Oa=class r{constructor(t,e,s){this._extensions=e||{},s&&(this._generateMask=s,this._maskBuffer=Buffer.alloc(4)),this._socket=t,this._firstFragment=!0,this._compress=!1,this._bufferedBytes=0,this._queue=[],this._state=it,this.onerror=Xr,this[Jr]=void 0}static frame(t,e){let s,a=!1,n=2,i=!1;e.mask&&(s=e.maskBuffer||Zr,e.generateMask?e.generateMask(s):(zt===Vs&&(Pt===void 0&&(Pt=Buffer.alloc(Vs)),Yr(Pt,0,Vs),zt=0),s[0]=Pt[zt++],s[1]=Pt[zt++],s[2]=Pt[zt++],s[3]=Pt[zt++]),i=(s[0]|s[1]|s[2]|s[3])===0,n=6);let o;typeof t=="string"?(!e.mask||i)&&e[at]!==void 0?o=e[at]:(t=Buffer.from(t),o=t.length):(o=t.length,a=e.mask&&e.readOnly&&!i);let l=o;o>=65536?(n+=8,l=127):o>125&&(n+=2,l=126);let c=Buffer.allocUnsafe(a?o+n:n);return c[0]=e.fin?e.opcode|128:e.opcode,e.rsv1&&(c[0]|=64),c[1]=l,l===126?c.writeUInt16BE(o,2):l===127&&(c[2]=c[3]=0,c.writeUIntBE(o,4,6)),e.mask?(c[1]|=128,c[n-4]=s[0],c[n-3]=s[1],c[n-2]=s[2],c[n-1]=s[3],i?[c,t]:a?(Qn(t,s,c,n,o),[c]):(Qn(t,s,t,0,o),[c,t])):[c,t]}close(t,e,s,a){let n;if(t===void 0)n=Kr;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={[at]:n.length,fin:!0,generateMask:this._generateMask,mask:s,maskBuffer:this._maskBuffer,opcode:8,readOnly:!1,rsv1:!1};this._state!==it?this.enqueue([this.dispatch,n,!1,i,a]):this.sendFrame(r.frame(n,i),a)}ping(t,e,s){let a,n;if(typeof t=="string"?(a=Buffer.byteLength(t),n=!1):Wt(t)?(a=t.size,n=!1):(t=Dt(t),a=t.length,n=Dt.readOnly),a>125)throw new RangeError("The data size must not be greater than 125 bytes");let i={[at]:a,fin:!0,generateMask:this._generateMask,mask:e,maskBuffer:this._maskBuffer,opcode:9,readOnly:n,rsv1:!1};Wt(t)?this._state!==it?this.enqueue([this.getBlobData,t,!1,i,s]):this.getBlobData(t,!1,i,s):this._state!==it?this.enqueue([this.dispatch,t,!1,i,s]):this.sendFrame(r.frame(t,i),s)}pong(t,e,s){let a,n;if(typeof t=="string"?(a=Buffer.byteLength(t),n=!1):Wt(t)?(a=t.size,n=!1):(t=Dt(t),a=t.length,n=Dt.readOnly),a>125)throw new RangeError("The data size must not be greater than 125 bytes");let i={[at]:a,fin:!0,generateMask:this._generateMask,mask:e,maskBuffer:this._maskBuffer,opcode:10,readOnly:n,rsv1:!1};Wt(t)?this._state!==it?this.enqueue([this.getBlobData,t,!1,i,s]):this.getBlobData(t,!1,i,s):this._state!==it?this.enqueue([this.dispatch,t,!1,i,s]):this.sendFrame(r.frame(t,i),s)}send(t,e,s){let a=this._extensions[Xn.extensionName],n=e.binary?2:1,i=e.compress,o,l;typeof t=="string"?(o=Buffer.byteLength(t),l=!1):Wt(t)?(o=t.size,l=!1):(t=Dt(t),o=t.length,l=Dt.readOnly),this._firstFragment?(this._firstFragment=!1,i&&a&&a.params[a._isServer?"server_no_context_takeover":"client_no_context_takeover"]&&(i=o>=a._threshold),this._compress=i):(i=!1,n=0),e.fin&&(this._firstFragment=!0);let c={[at]:o,fin:e.fin,generateMask:this._generateMask,mask:e.mask,maskBuffer:this._maskBuffer,opcode:n,readOnly:l,rsv1:i};Wt(t)?this._state!==it?this.enqueue([this.getBlobData,t,this._compress,c,s]):this.getBlobData(t,this._compress,c,s):this._state!==it?this.enqueue([this.dispatch,t,this._compress,c,s]):this.dispatch(t,this._compress,c,s)}getBlobData(t,e,s,a){this._bufferedBytes+=s[at],this._state=to,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[at];let i=Dt(n);e?this.dispatch(i,e,s,a):(this._state=it,this.sendFrame(r.frame(i,s),a),this.dequeue())}).catch(n=>{process.nextTick(so,this,n,a)})}dispatch(t,e,s,a){if(!e){this.sendFrame(r.frame(t,s),a);return}let n=this._extensions[Xn.extensionName];this._bufferedBytes+=s[at],this._state=eo,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[at],this._state=it,s.readOnly=!1,this.sendFrame(r.frame(o,s),a),this.dequeue()})}dequeue(){for(;this._state===it&&this._queue.length;){let t=this._queue.shift();this._bufferedBytes-=t[3][at],Reflect.apply(t[0],this,t.slice(1))}}enqueue(t){this._bufferedBytes+=t[3][at],this._queue.push(t)}sendFrame(t,e){t.length===2?(this._socket.cork(),this._socket.write(t[0]),this._socket.write(t[1],e),this._socket.uncork()):this._socket.write(t[0],e)}};Zn.exports=Oa;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 so(r,t,e){Na(r,t,e),r.onerror(t)}});var li=Ve((Cc,oi)=>{"use strict";var{kForOnEventAttribute:ls,kListener:Ua}=mt(),ei=Symbol("kCode"),ti=Symbol("kData"),si=Symbol("kError"),ai=Symbol("kMessage"),ni=Symbol("kReason"),Gt=Symbol("kTarget"),ii=Symbol("kType"),ri=Symbol("kWasClean"),gt=class{constructor(t){this[Gt]=null,this[ii]=t}get target(){return this[Gt]}get type(){return this[ii]}};Object.defineProperty(gt.prototype,"target",{enumerable:!0});Object.defineProperty(gt.prototype,"type",{enumerable:!0});var Rt=class extends gt{constructor(t,e={}){super(t),this[ei]=e.code===void 0?0:e.code,this[ni]=e.reason===void 0?"":e.reason,this[ri]=e.wasClean===void 0?!1:e.wasClean}get code(){return this[ei]}get reason(){return this[ni]}get wasClean(){return this[ri]}};Object.defineProperty(Rt.prototype,"code",{enumerable:!0});Object.defineProperty(Rt.prototype,"reason",{enumerable:!0});Object.defineProperty(Rt.prototype,"wasClean",{enumerable:!0});var Vt=class extends gt{constructor(t,e={}){super(t),this[si]=e.error===void 0?null:e.error,this[ai]=e.message===void 0?"":e.message}get error(){return this[si]}get message(){return this[ai]}};Object.defineProperty(Vt.prototype,"error",{enumerable:!0});Object.defineProperty(Vt.prototype,"message",{enumerable:!0});var cs=class extends gt{constructor(t,e={}){super(t),this[ti]=e.data===void 0?null:e.data}get data(){return this[ti]}};Object.defineProperty(cs.prototype,"data",{enumerable:!0});var ao={addEventListener(r,t,e={}){for(let a of this.listeners(r))if(!e[ls]&&a[Ua]===t&&!a[ls])return;let s;if(r==="message")s=function(n,i){let o=new cs("message",{data:i?n:n.toString()});o[Gt]=this,Ys(t,this,o)};else if(r==="close")s=function(n,i){let o=new Rt("close",{code:n,reason:i.toString(),wasClean:this._closeFrameReceived&&this._closeFrameSent});o[Gt]=this,Ys(t,this,o)};else if(r==="error")s=function(n){let i=new Vt("error",{error:n,message:n.message});i[Gt]=this,Ys(t,this,i)};else if(r==="open")s=function(){let n=new gt("open");n[Gt]=this,Ys(t,this,n)};else return;s[ls]=!!e[ls],s[Ua]=t,e.once?this.once(r,s):this.on(r,s)},removeEventListener(r,t){for(let e of this.listeners(r))if(e[Ua]===t&&!e[ls]){this.removeListener(r,e);break}}};oi.exports={CloseEvent:Rt,ErrorEvent:Vt,Event:gt,EventTarget:ao,MessageEvent:cs};function Ys(r,t,e){typeof r=="object"&&r.handleEvent?r.handleEvent.call(r,e):r.call(t,e)}});var Ks=Ve((_c,ci)=>{"use strict";var{tokenChars:ds}=qt();function dt(r,t,e){r[t]===void 0?r[t]=[e]:r[t].push(e)}function no(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&&ds[c]===1)l===-1&&(l=h);else if(h!==0&&(c===32||c===9))d===-1&&l!==-1&&(d=h);else if(c===59||c===44){if(l===-1)throw new SyntaxError(`Unexpected character at index ${h}`);d===-1&&(d=h);let p=r.slice(l,d);c===44?(dt(t,p,e),e=Object.create(null)):i=p,l=d=-1}else throw new SyntaxError(`Unexpected character at index ${h}`);else if(o===void 0)if(d===-1&&ds[c]===1)l===-1&&(l=h);else if(c===32||c===9)d===-1&&l!==-1&&(d=h);else if(c===59||c===44){if(l===-1)throw new SyntaxError(`Unexpected character at index ${h}`);d===-1&&(d=h),dt(e,r.slice(l,d),!0),c===44&&(dt(t,i,e),e=Object.create(null),i=void 0),l=d=-1}else if(c===61&&l!==-1&&d===-1)o=r.slice(l,h),l=d=-1;else throw new SyntaxError(`Unexpected character at index ${h}`);else if(a){if(ds[c]!==1)throw new SyntaxError(`Unexpected character at index ${h}`);l===-1?l=h:s||(s=!0),a=!1}else if(n)if(ds[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&&ds[c]===1)l===-1&&(l=h);else if(l!==-1&&(c===32||c===9))d===-1&&(d=h);else if(c===59||c===44){if(l===-1)throw new SyntaxError(`Unexpected character at index ${h}`);d===-1&&(d=h);let p=r.slice(l,d);s&&(p=p.replace(/\\/g,""),s=!1),dt(e,o,p),c===44&&(dt(t,i,e),e=Object.create(null),i=void 0),o=void 0,l=d=-1}else throw new SyntaxError(`Unexpected character at index ${h}`);if(l===-1||n||c===32||c===9)throw new SyntaxError("Unexpected end of input");d===-1&&(d=h);let u=r.slice(l,d);return i===void 0?dt(t,u,e):(o===void 0?dt(e,u,!0):s?dt(e,o,u.replace(/\\/g,"")):dt(e,o,u),dt(t,i,e)),t}function io(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(", ")}ci.exports={format:io,parse:no}});var Zs=Ve((Dc,ki)=>{"use strict";var ro=require("events"),oo=require("https"),lo=require("http"),ui=require("net"),co=require("tls"),{randomBytes:ho,createHash:uo}=require("crypto"),{Duplex:Ac,Readable:Ec}=require("stream"),{URL:$a}=require("url"),kt=Ht(),po=Fa(),mo=Ba(),{isBlob:fo}=qt(),{BINARY_TYPES:di,CLOSE_TIMEOUT:go,EMPTY_BUFFER:Js,GUID:yo,kForOnEventAttribute:ja,kListener:vo,kStatusCode:bo,kWebSocket:Ie,NOOP:pi}=mt(),{EventTarget:{addEventListener:wo,removeEventListener:ko}}=li(),{format:xo,parse:So}=Ks(),{toBuffer:To}=rs(),mi=Symbol("kAborted"),Ha=[8,13],yt=["CONNECTING","OPEN","CLOSING","CLOSED"],Co=/^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/,ve=class r extends ro{constructor(t,e,s){super(),this._binaryType=di[0],this._closeCode=1006,this._closeFrameReceived=!1,this._closeFrameSent=!1,this._closeMessage=Js,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]),fi(this,t,e,s)):(this._autoPong=s.autoPong,this._closeTimeout=s.closeTimeout,this._isServer=!0)}get binaryType(){return this._binaryType}set binaryType(t){di.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 po({allowSynchronousEvents:s.allowSynchronousEvents,binaryType:this.binaryType,extensions:this._extensions,isServer:this._isServer,maxPayload:s.maxPayload,skipUTF8Validation:s.skipUTF8Validation}),n=new mo(t,this._extensions,s.generateMask);this._receiver=a,this._sender=n,this._socket=t,a[Ie]=this,n[Ie]=this,t[Ie]=this,a.on("conclude",Eo),a.on("drain",Do),a.on("error",Po),a.on("message",Ro),a.on("ping",Io),a.on("pong",Lo),n.onerror=Mo,t.setTimeout&&t.setTimeout(0),t.setNoDelay&&t.setNoDelay(),e.length>0&&t.unshift(e),t.on("close",vi),t.on("data",Qs),t.on("end",bi),t.on("error",wi),this._readyState=r.OPEN,this.emit("open")}emitClose(){if(!this._socket){this._readyState=r.CLOSED,this.emit("close",this._closeCode,this._closeMessage);return}this._extensions[kt.extensionName]&&this._extensions[kt.extensionName].cleanup(),this._receiver.removeAllListeners(),this._readyState=r.CLOSED,this.emit("close",this._closeCode,this._closeMessage)}close(t,e){if(this.readyState!==r.CLOSED){if(this.readyState===r.CONNECTING){Xe(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())}),yi(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){qa(this,t,s);return}e===void 0&&(e=!this._isServer),this._sender.ping(t||Js,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){qa(this,t,s);return}e===void 0&&(e=!this._isServer),this._sender.pong(t||Js,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){qa(this,t,s);return}let a={binary:typeof t!="string",mask:!this._isServer,compress:!0,fin:!0,...e};this._extensions[kt.extensionName]||(a.compress=!1),this._sender.send(t||Js,a,s)}terminate(){if(this.readyState!==r.CLOSED){if(this.readyState===r.CONNECTING){Xe(this,this._req,"WebSocket was closed before the connection was established");return}this._socket&&(this._readyState=r.CLOSING,this._socket.destroy())}}};Object.defineProperty(ve,"CONNECTING",{enumerable:!0,value:yt.indexOf("CONNECTING")});Object.defineProperty(ve.prototype,"CONNECTING",{enumerable:!0,value:yt.indexOf("CONNECTING")});Object.defineProperty(ve,"OPEN",{enumerable:!0,value:yt.indexOf("OPEN")});Object.defineProperty(ve.prototype,"OPEN",{enumerable:!0,value:yt.indexOf("OPEN")});Object.defineProperty(ve,"CLOSING",{enumerable:!0,value:yt.indexOf("CLOSING")});Object.defineProperty(ve.prototype,"CLOSING",{enumerable:!0,value:yt.indexOf("CLOSING")});Object.defineProperty(ve,"CLOSED",{enumerable:!0,value:yt.indexOf("CLOSED")});Object.defineProperty(ve.prototype,"CLOSED",{enumerable:!0,value:yt.indexOf("CLOSED")});["binaryType","bufferedAmount","extensions","isPaused","protocol","readyState","url"].forEach(r=>{Object.defineProperty(ve.prototype,r,{enumerable:!0})});["open","error","close","message"].forEach(r=>{Object.defineProperty(ve.prototype,`on${r}`,{enumerable:!0,get(){for(let t of this.listeners(r))if(t[ja])return t[vo];return null},set(t){for(let e of this.listeners(r))if(e[ja]){this.removeListener(r,e);break}typeof t=="function"&&this.addEventListener(r,t,{[ja]:!0})}})});ve.prototype.addEventListener=wo;ve.prototype.removeEventListener=ko;ki.exports=ve;function fi(r,t,e,s){let a={allowSynchronousEvents:!0,autoPong:!0,closeTimeout:go,protocolVersion:Ha[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,!Ha.includes(a.protocolVersion))throw new RangeError(`Unsupported protocol version: ${a.protocolVersion} (supported versions: ${Ha.join(", ")})`);let n;if(t instanceof $a)n=t;else try{n=new $a(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;Xs(r,m);return}let c=i?443:80,d=ho(16).toString("base64"),h=i?oo.request:lo.request,u=new Set,p;if(a.createConnection=a.createConnection||(i?Ao:_o),a.defaultPort=a.defaultPort||c,a.port=n.port||c,a.host=n.hostname.startsWith("[")?n.hostname.slice(1,-1):n.hostname,a.headers={...a.headers,"Sec-WebSocket-Version":a.protocolVersion,"Sec-WebSocket-Key":d,Connection:"Upgrade",Upgrade:"websocket"},a.path=n.pathname+n.search,a.timeout=a.handshakeTimeout,a.perMessageDeflate&&(p=new kt({...a.perMessageDeflate,isServer:!1,maxPayload:a.maxPayload}),a.headers["Sec-WebSocket-Extensions"]=xo({[kt.extensionName]:p.offer()})),e.length){for(let m of e){if(typeof m!="string"||!Co.test(m)||u.has(m))throw new SyntaxError("An invalid or duplicated subprotocol was specified");u.add(m)}a.headers["Sec-WebSocket-Protocol"]=e.join(",")}if(a.origin&&(a.protocolVersion<13?a.headers["Sec-WebSocket-Origin"]=a.origin:a.headers.Origin=a.origin),(n.username||n.password)&&(a.auth=`${n.username}:${n.password}`),o){let m=a.path.split(":");a.socketPath=m[0],a.path=m[1]}let f;if(a.followRedirects){if(r._redirects===0){r._originalIpc=o,r._originalSecure=i,r._originalHostOrSocketPath=o?a.socketPath:n.host;let m=s&&s.headers;if(s={...s,headers:{}},m)for(let[g,k]of Object.entries(m))s.headers[g.toLowerCase()]=k}else if(r.listenerCount("redirect")===0){let m=o?r._originalIpc?a.socketPath===r._originalHostOrSocketPath:!1:r._originalIpc?!1:n.host===r._originalHostOrSocketPath;(!m||r._originalSecure&&!i)&&(delete a.headers.authorization,delete a.headers.cookie,m||delete a.headers.host,a.auth=void 0)}a.auth&&!s.headers.authorization&&(s.headers.authorization="Basic "+Buffer.from(a.auth).toString("base64")),f=r._req=h(a),r._redirects&&r.emit("redirect",r.url,f)}else f=r._req=h(a);a.timeout&&f.on("timeout",()=>{Xe(r,f,"Opening handshake has timed out")}),f.on("error",m=>{f===null||f[mi]||(f=r._req=null,Xs(r,m))}),f.on("response",m=>{let g=m.headers.location,k=m.statusCode;if(g&&a.followRedirects&&k>=300&&k<400){if(++r._redirects>a.maxRedirects){Xe(r,f,"Maximum redirects exceeded");return}f.abort();let b;try{b=new $a(g,t)}catch{let y=new SyntaxError(`Invalid URL: ${g}`);Xs(r,y);return}fi(r,b,e,s)}else r.emit("unexpected-response",f,m)||Xe(r,f,`Unexpected server response: ${m.statusCode}`)}),f.on("upgrade",(m,g,k)=>{if(r.emit("upgrade",m),r.readyState!==ve.CONNECTING)return;f=r._req=null;let b=m.headers.upgrade;if(b===void 0||b.toLowerCase()!=="websocket"){Xe(r,g,"Invalid Upgrade header");return}let v=uo("sha1").update(d+yo).digest("base64");if(m.headers["sec-websocket-accept"]!==v){Xe(r,g,"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){Xe(r,g,x);return}y&&(r._protocol=y);let T=m.headers["sec-websocket-extensions"];if(T!==void 0){if(!p){Xe(r,g,"Server sent a Sec-WebSocket-Extensions header but no extension was requested");return}let _;try{_=So(T)}catch{Xe(r,g,"Invalid Sec-WebSocket-Extensions header");return}let R=Object.keys(_);if(R.length!==1||R[0]!==kt.extensionName){Xe(r,g,"Server indicated an extension that was not requested");return}try{p.accept(_[kt.extensionName])}catch{Xe(r,g,"Invalid Sec-WebSocket-Extensions header");return}r._extensions[kt.extensionName]=p}r.setSocket(g,k,{allowSynchronousEvents:a.allowSynchronousEvents,generateMask:a.generateMask,maxPayload:a.maxPayload,skipUTF8Validation:a.skipUTF8Validation})}),a.finishRequest?a.finishRequest(f,r):f.end()}function Xs(r,t){r._readyState=ve.CLOSING,r._errorEmitted=!0,r.emit("error",t),r.emitClose()}function _o(r){return r.path=r.socketPath,ui.connect(r)}function Ao(r){return r.path=void 0,!r.servername&&r.servername!==""&&(r.servername=ui.isIP(r.host)?"":r.host),co.connect(r)}function Xe(r,t,e){r._readyState=ve.CLOSING;let s=new Error(e);Error.captureStackTrace(s,Xe),t.setHeader?(t[mi]=!0,t.abort(),t.socket&&!t.socket.destroyed&&t.socket.destroy(),process.nextTick(Xs,r,s)):(t.destroy(s),t.once("error",r.emit.bind(r,"error")),t.once("close",r.emitClose.bind(r)))}function qa(r,t,e){if(t){let s=fo(t)?t.size:To(t).length;r._socket?r._sender._bufferedBytes+=s:r._bufferedAmount+=s}if(e){let s=new Error(`WebSocket is not open: readyState ${r.readyState} (${yt[r.readyState]})`);process.nextTick(e,s)}}function Eo(r,t){let e=this[Ie];e._closeFrameReceived=!0,e._closeMessage=t,e._closeCode=r,e._socket[Ie]!==void 0&&(e._socket.removeListener("data",Qs),process.nextTick(gi,e._socket),r===1005?e.close():e.close(r,t))}function Do(){let r=this[Ie];r.isPaused||r._socket.resume()}function Po(r){let t=this[Ie];t._socket[Ie]!==void 0&&(t._socket.removeListener("data",Qs),process.nextTick(gi,t._socket),t.close(r[bo])),t._errorEmitted||(t._errorEmitted=!0,t.emit("error",r))}function hi(){this[Ie].emitClose()}function Ro(r,t){this[Ie].emit("message",r,t)}function Io(r){let t=this[Ie];t._autoPong&&t.pong(r,!this._isServer,pi),t.emit("ping",r)}function Lo(r){this[Ie].emit("pong",r)}function gi(r){r.resume()}function Mo(r){let t=this[Ie];t.readyState!==ve.CLOSED&&(t.readyState===ve.OPEN&&(t._readyState=ve.CLOSING,yi(t)),this._socket.end(),t._errorEmitted||(t._errorEmitted=!0,t.emit("error",r)))}function yi(r){r._closeTimer=setTimeout(r._socket.destroy.bind(r._socket),r._closeTimeout)}function vi(){let r=this[Ie];if(this.removeListener("close",vi),this.removeListener("data",Qs),this.removeListener("end",bi),r._readyState=ve.CLOSING,!this._readableState.endEmitted&&!r._closeFrameReceived&&!r._receiver._writableState.errorEmitted&&this._readableState.length!==0){let t=this.read(this._readableState.length);r._receiver.write(t)}r._receiver.end(),this[Ie]=void 0,clearTimeout(r._closeTimer),r._receiver._writableState.finished||r._receiver._writableState.errorEmitted?r.emitClose():(r._receiver.on("error",hi),r._receiver.on("finish",hi))}function Qs(r){this[Ie]._receiver.write(r)||this.pause()}function bi(){let r=this[Ie];r._readyState=ve.CLOSING,r._receiver.end(),this.end()}function wi(){let r=this[Ie];this.removeListener("error",wi),this.on("error",pi),r&&(r._readyState=ve.CLOSING,this.destroy())}});var Ci=Ve((Rc,Ti)=>{"use strict";var Pc=Zs(),{Duplex:Fo}=require("stream");function xi(r){r.emit("close")}function Oo(){!this.destroyed&&this._writableState.finished&&this.destroy()}function Si(r){this.removeListener("error",Si),this.destroy(),this.listenerCount("error")===0&&this.emit("error",r)}function No(r,t){let e=!0,s=new Fo({...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(xi,s);return}let i=!1;r.once("error",function(l){i=!0,n(l)}),r.once("close",function(){i||n(a),process.nextTick(xi,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",Oo),s.on("error",Si),s}Ti.exports=No});var Wa=Ve((Ic,_i)=>{"use strict";var{tokenChars:Bo}=qt();function Uo(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&&Bo[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}_i.exports={parse:Uo}});var Li=Ve((Mc,Ii)=>{"use strict";var $o=require("events"),ea=require("http"),{Duplex:Lc}=require("stream"),{createHash:jo}=require("crypto"),Ai=Ks(),It=Ht(),Ho=Wa(),qo=Zs(),{CLOSE_TIMEOUT:Wo,GUID:zo,kWebSocket:Go}=mt(),Vo=/^[+/0-9A-Za-z]{22}==$/,Ei=0,Di=1,Ri=2,za=class extends $o{constructor(t,e){if(super(),t={allowSynchronousEvents:!0,autoPong:!0,maxPayload:100*1024*1024,skipUTF8Validation:!1,perMessageDeflate:!1,handleProtocols:null,clientTracking:!0,closeTimeout:Wo,verifyClient:null,noServer:!1,backlog:null,server:null,host:null,path:null,port:null,WebSocket:qo,...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=ea.createServer((s,a)=>{let n=ea.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=Yo(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=Ei}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===Ri){t&&this.once("close",()=>{t(new Error("The server is not running"))}),process.nextTick(hs,this);return}if(t&&this.once("close",t),this._state!==Di)if(this._state=Di,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(hs,this):process.nextTick(hs,this);else{let e=this._server;this._removeListeners(),this._removeListeners=this._server=null,e.close(()=>{hs(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",Pi);let n=t.headers["sec-websocket-key"],i=t.headers.upgrade,o=+t.headers["sec-websocket-version"];if(t.method!=="GET"){Lt(this,t,e,405,"Invalid HTTP method");return}if(i===void 0||i.toLowerCase()!=="websocket"){Lt(this,t,e,400,"Invalid Upgrade header");return}if(n===void 0||!Vo.test(n)){Lt(this,t,e,400,"Missing or invalid Sec-WebSocket-Key header");return}if(o!==13&&o!==8){Lt(this,t,e,400,"Missing or invalid Sec-WebSocket-Version header",{"Sec-WebSocket-Version":"13, 8"});return}if(!this.shouldHandle(t)){us(e,400);return}let l=t.headers["sec-websocket-protocol"],c=new Set;if(l!==void 0)try{c=Ho.parse(l)}catch{Lt(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 It({...this.options.perMessageDeflate,isServer:!0,maxPayload:this.options.maxPayload});try{let p=Ai.parse(d);p[It.extensionName]&&(u.accept(p[It.extensionName]),h[It.extensionName]=u)}catch{Lt(this,t,e,400,"Invalid or unacceptable Sec-WebSocket-Extensions header");return}}if(this.options.verifyClient){let u={origin:t.headers[`${o===8?"sec-websocket-origin":"origin"}`],secure:!!(t.socket.authorized||t.socket.encrypted),req:t};if(this.options.verifyClient.length===2){this.options.verifyClient(u,(p,f,m,g)=>{if(!p)return us(e,f||401,m,g);this.completeUpgrade(h,n,c,t,e,s,a)});return}if(!this.options.verifyClient(u))return us(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[Go])throw new Error("server.handleUpgrade() was called more than once with the same socket, possibly due to a misconfiguration");if(this._state>Ei)return us(n,503);let c=["HTTP/1.1 101 Switching Protocols","Upgrade: websocket","Connection: Upgrade",`Sec-WebSocket-Accept: ${jo("sha1").update(e+zo).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[It.extensionName]){let h=t[It.extensionName].params,u=Ai.format({[It.extensionName]:[h]});c.push(`Sec-WebSocket-Extensions: ${u}`),d._extensions=t}this.emit("headers",c,a),n.write(c.concat(`\r
|
|
1
|
+
"use strict";var Po=Object.create;var Ws=Object.defineProperty;var Ro=Object.getOwnPropertyDescriptor;var Do=Object.getOwnPropertyNames;var Io=Object.getPrototypeOf,Mo=Object.prototype.hasOwnProperty;var Ye=(i,t)=>()=>(t||i((t={exports:{}}).exports,t),t.exports),Lo=(i,t)=>{for(var e in t)Ws(i,e,{get:t[e],enumerable:!0})},qa=(i,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of Do(t))!Mo.call(i,n)&&n!==e&&Ws(i,n,{get:()=>t[n],enumerable:!(s=Ro(t,n))||s.enumerable});return i};var Ke=(i,t,e)=>(e=i!=null?Po(Io(i)):{},qa(t||!i||!i.__esModule?Ws(e,"default",{value:i,enumerable:!0}):e,i)),Fo=i=>qa(Ws({},"__esModule",{value:!0}),i);var bt=Ye((pu,ir)=>{"use strict";var nr=["nodebuffer","arraybuffer","fragments"],ar=typeof Blob<"u";ar&&nr.push("blob");ir.exports={BINARY_TYPES:nr,CLOSE_TIMEOUT:3e4,EMPTY_BUFFER:Buffer.alloc(0),GUID:"258EAFA5-E914-47DA-95CA-C5AB0DC85B11",hasBlob:ar,kForOnEventAttribute:Symbol("kIsForOnEventAttribute"),kListener:Symbol("kListener"),kStatusCode:Symbol("status-code"),kWebSocket:Symbol("websocket"),NOOP:()=>{}}});var As=Ye((mu,wn)=>{"use strict";var{EMPTY_BUFFER:Hl}=bt(),ya=Buffer[Symbol.species];function ql(i,t){if(i.length===0)return Hl;if(i.length===1)return i[0];let e=Buffer.allocUnsafe(t),s=0;for(let n=0;n<i.length;n++){let a=i[n];e.set(a,s),s+=a.length}return s<t?new ya(e.buffer,e.byteOffset,s):e}function rr(i,t,e,s,n){for(let a=0;a<n;a++)e[s+a]=i[a]^t[a&3]}function or(i,t){for(let e=0;e<i.length;e++)i[e]^=t[e&3]}function zl(i){return i.length===i.buffer.byteLength?i.buffer:i.buffer.slice(i.byteOffset,i.byteOffset+i.length)}function va(i){if(va.readOnly=!0,Buffer.isBuffer(i))return i;let t;return i instanceof ArrayBuffer?t=new ya(i):ArrayBuffer.isView(i)?t=new ya(i.buffer,i.byteOffset,i.byteLength):(t=Buffer.from(i),va.readOnly=!1),t}wn.exports={concat:ql,mask:rr,toArrayBuffer:zl,toBuffer:va,unmask:or};if(!process.env.WS_NO_BUFFER_UTIL)try{let i=require("bufferutil");wn.exports.mask=function(t,e,s,n,a){a<48?rr(t,e,s,n,a):i.mask(t,e,s,n,a)},wn.exports.unmask=function(t,e){t.length<32?or(t,e):i.unmask(t,e)}}catch{}});var dr=Ye((fu,cr)=>{"use strict";var lr=Symbol("kDone"),ba=Symbol("kRun"),wa=class{constructor(t){this[lr]=()=>{this.pending--,this[ba]()},this.concurrency=t||1/0,this.jobs=[],this.pending=0}add(t){this.jobs.push(t),this[ba]()}[ba](){if(this.pending!==this.concurrency&&this.jobs.length){let t=this.jobs.shift();this.pending++,t(this[lr])}}};cr.exports=wa});var ts=Ye((gu,mr)=>{"use strict";var Ps=require("zlib"),hr=As(),Gl=dr(),{kStatusCode:ur}=bt(),Vl=Buffer[Symbol.species],Yl=Buffer.from([0,0,255,255]),xn=Symbol("permessage-deflate"),wt=Symbol("total-length"),Zt=Symbol("callback"),Pt=Symbol("buffers"),es=Symbol("error"),kn,ka=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,!kn){let e=this._options.concurrencyLimit!==void 0?this._options.concurrencyLimit:10;kn=new Gl(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[Zt];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(n=>!(e.serverNoContextTakeover===!1&&n.server_no_context_takeover||n.server_max_window_bits&&(e.serverMaxWindowBits===!1||typeof e.serverMaxWindowBits=="number"&&e.serverMaxWindowBits>n.server_max_window_bits)||typeof e.clientMaxWindowBits=="number"&&!n.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 n=e[s];if(n.length>1)throw new Error(`Parameter "${s}" must have only a single value`);if(n=n[0],s==="client_max_window_bits"){if(n!==!0){let a=+n;if(!Number.isInteger(a)||a<8||a>15)throw new TypeError(`Invalid value for parameter "${s}": ${n}`);n=a}else if(!this._isServer)throw new TypeError(`Invalid value for parameter "${s}": ${n}`)}else if(s==="server_max_window_bits"){let a=+n;if(!Number.isInteger(a)||a<8||a>15)throw new TypeError(`Invalid value for parameter "${s}": ${n}`);n=a}else if(s==="client_no_context_takeover"||s==="server_no_context_takeover"){if(n!==!0)throw new TypeError(`Invalid value for parameter "${s}": ${n}`)}else throw new Error(`Unknown parameter "${s}"`);e[s]=n})}),t}decompress(t,e,s){kn.add(n=>{this._decompress(t,e,(a,r)=>{n(),s(a,r)})})}compress(t,e,s){kn.add(n=>{this._compress(t,e,(a,r)=>{n(),s(a,r)})})}_decompress(t,e,s){let n=this._isServer?"client":"server";if(!this._inflate){let a=`${n}_max_window_bits`,r=typeof this.params[a]!="number"?Ps.Z_DEFAULT_WINDOWBITS:this.params[a];this._inflate=Ps.createInflateRaw({...this._options.zlibInflateOptions,windowBits:r}),this._inflate[xn]=this,this._inflate[wt]=0,this._inflate[Pt]=[],this._inflate.on("error",Jl),this._inflate.on("data",pr)}this._inflate[Zt]=s,this._inflate.write(t),e&&this._inflate.write(Yl),this._inflate.flush(()=>{let a=this._inflate[es];if(a){this._inflate.close(),this._inflate=null,s(a);return}let r=hr.concat(this._inflate[Pt],this._inflate[wt]);this._inflate._readableState.endEmitted?(this._inflate.close(),this._inflate=null):(this._inflate[wt]=0,this._inflate[Pt]=[],e&&this.params[`${n}_no_context_takeover`]&&this._inflate.reset()),s(null,r)})}_compress(t,e,s){let n=this._isServer?"server":"client";if(!this._deflate){let a=`${n}_max_window_bits`,r=typeof this.params[a]!="number"?Ps.Z_DEFAULT_WINDOWBITS:this.params[a];this._deflate=Ps.createDeflateRaw({...this._options.zlibDeflateOptions,windowBits:r}),this._deflate[wt]=0,this._deflate[Pt]=[],this._deflate.on("data",Kl)}this._deflate[Zt]=s,this._deflate.write(t),this._deflate.flush(Ps.Z_SYNC_FLUSH,()=>{if(!this._deflate)return;let a=hr.concat(this._deflate[Pt],this._deflate[wt]);e&&(a=new Vl(a.buffer,a.byteOffset,a.length-4)),this._deflate[Zt]=null,this._deflate[wt]=0,this._deflate[Pt]=[],e&&this.params[`${n}_no_context_takeover`]&&this._deflate.reset(),s(null,a)})}};mr.exports=ka;function Kl(i){this[Pt].push(i),this[wt]+=i.length}function pr(i){if(this[wt]+=i.length,this[xn]._maxPayload<1||this[wt]<=this[xn]._maxPayload){this[Pt].push(i);return}this[es]=new RangeError("Max payload size exceeded"),this[es].code="WS_ERR_UNSUPPORTED_MESSAGE_LENGTH",this[es][ur]=1009,this.removeListener("data",pr),this.reset()}function Jl(i){if(this[xn]._inflate=null,this[es]){this[Zt](this[es]);return}i[ur]=1007,this[Zt](i)}});var ss=Ye((yu,Sn)=>{"use strict";var{isUtf8:fr}=require("buffer"),{hasBlob:Xl}=bt(),Ql=[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 Zl(i){return i>=1e3&&i<=1014&&i!==1004&&i!==1005&&i!==1006||i>=3e3&&i<=4999}function xa(i){let t=i.length,e=0;for(;e<t;)if((i[e]&128)===0)e++;else if((i[e]&224)===192){if(e+1===t||(i[e+1]&192)!==128||(i[e]&254)===192)return!1;e+=2}else if((i[e]&240)===224){if(e+2>=t||(i[e+1]&192)!==128||(i[e+2]&192)!==128||i[e]===224&&(i[e+1]&224)===128||i[e]===237&&(i[e+1]&224)===160)return!1;e+=3}else if((i[e]&248)===240){if(e+3>=t||(i[e+1]&192)!==128||(i[e+2]&192)!==128||(i[e+3]&192)!==128||i[e]===240&&(i[e+1]&240)===128||i[e]===244&&i[e+1]>143||i[e]>244)return!1;e+=4}else return!1;return!0}function ec(i){return Xl&&typeof i=="object"&&typeof i.arrayBuffer=="function"&&typeof i.type=="string"&&typeof i.stream=="function"&&(i[Symbol.toStringTag]==="Blob"||i[Symbol.toStringTag]==="File")}Sn.exports={isBlob:ec,isValidStatusCode:Zl,isValidUTF8:xa,tokenChars:Ql};if(fr)Sn.exports.isValidUTF8=function(i){return i.length<24?xa(i):fr(i)};else if(!process.env.WS_NO_UTF_8_VALIDATE)try{let i=require("utf-8-validate");Sn.exports.isValidUTF8=function(t){return t.length<32?xa(t):i(t)}}catch{}});var Ea=Ye((vu,xr)=>{"use strict";var{Writable:tc}=require("stream"),gr=ts(),{BINARY_TYPES:sc,EMPTY_BUFFER:yr,kStatusCode:nc,kWebSocket:ac}=bt(),{concat:Sa,toArrayBuffer:ic,unmask:rc}=As(),{isValidStatusCode:oc,isValidUTF8:vr}=ss(),Cn=Buffer[Symbol.species],lt=0,br=1,wr=2,kr=3,Ca=4,Ta=5,Tn=6,_a=class extends tc{constructor(t={}){super(),this._allowSynchronousEvents=t.allowSynchronousEvents!==void 0?t.allowSynchronousEvents:!0,this._binaryType=t.binaryType||sc[0],this._extensions=t.extensions||{},this._isServer=!!t.isServer,this._maxPayload=t.maxPayload|0,this._skipUTF8Validation=!!t.skipUTF8Validation,this[ac]=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=lt}_write(t,e,s){if(this._opcode===8&&this._state==lt)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 Cn(s.buffer,s.byteOffset+t,s.length-t),new Cn(s.buffer,s.byteOffset,t)}let e=Buffer.allocUnsafe(t);do{let s=this._buffers[0],n=e.length-t;t>=s.length?e.set(this._buffers.shift(),n):(e.set(new Uint8Array(s.buffer,s.byteOffset,t),n),this._buffers[0]=new Cn(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 lt:this.getInfo(t);break;case br:this.getPayloadLength16(t);break;case wr:this.getPayloadLength64(t);break;case kr:this.getMask();break;case Ca:this.getData(t);break;case Ta:case Tn: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 n=this.createError(RangeError,"RSV2 and RSV3 must be clear",!0,1002,"WS_ERR_UNEXPECTED_RSV_2_3");t(n);return}let s=(e[0]&64)===64;if(s&&!this._extensions[gr.extensionName]){let n=this.createError(RangeError,"RSV1 must be clear",!0,1002,"WS_ERR_UNEXPECTED_RSV_1");t(n);return}if(this._fin=(e[0]&128)===128,this._opcode=e[0]&15,this._payloadLength=e[1]&127,this._opcode===0){if(s){let n=this.createError(RangeError,"RSV1 must be clear",!0,1002,"WS_ERR_UNEXPECTED_RSV_1");t(n);return}if(!this._fragmented){let n=this.createError(RangeError,"invalid opcode 0",!0,1002,"WS_ERR_INVALID_OPCODE");t(n);return}this._opcode=this._fragmented}else if(this._opcode===1||this._opcode===2){if(this._fragmented){let n=this.createError(RangeError,`invalid opcode ${this._opcode}`,!0,1002,"WS_ERR_INVALID_OPCODE");t(n);return}this._compressed=s}else if(this._opcode>7&&this._opcode<11){if(!this._fin){let n=this.createError(RangeError,"FIN must be set",!0,1002,"WS_ERR_EXPECTED_FIN");t(n);return}if(s){let n=this.createError(RangeError,"RSV1 must be clear",!0,1002,"WS_ERR_UNEXPECTED_RSV_1");t(n);return}if(this._payloadLength>125||this._opcode===8&&this._payloadLength===1){let n=this.createError(RangeError,`invalid payload length ${this._payloadLength}`,!0,1002,"WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH");t(n);return}}else{let n=this.createError(RangeError,`invalid opcode ${this._opcode}`,!0,1002,"WS_ERR_INVALID_OPCODE");t(n);return}if(!this._fin&&!this._fragmented&&(this._fragmented=this._opcode),this._masked=(e[1]&128)===128,this._isServer){if(!this._masked){let n=this.createError(RangeError,"MASK must be set",!0,1002,"WS_ERR_EXPECTED_MASK");t(n);return}}else if(this._masked){let n=this.createError(RangeError,"MASK must be clear",!0,1002,"WS_ERR_UNEXPECTED_MASK");t(n);return}this._payloadLength===126?this._state=br:this._payloadLength===127?this._state=wr: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 n=this.createError(RangeError,"Unsupported WebSocket frame: payload length > 2^53 - 1",!1,1009,"WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH");t(n);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=kr:this._state=Ca}getMask(){if(this._bufferedBytes<4){this._loop=!1;return}this._mask=this.consume(4),this._state=Ca}getData(t){let e=yr;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&&rc(e,this._mask)}if(this._opcode>7){this.controlMessage(e,t);return}if(this._compressed){this._state=Ta,this.decompress(e,t);return}e.length&&(this._messageLength=this._totalPayloadLength,this._fragments.push(e)),this.dataMessage(t)}decompress(t,e){this._extensions[gr.extensionName].decompress(t,this._fin,(n,a)=>{if(n)return e(n);if(a.length){if(this._messageLength+=a.length,this._messageLength>this._maxPayload&&this._maxPayload>0){let r=this.createError(RangeError,"Max payload size exceeded",!1,1009,"WS_ERR_UNSUPPORTED_MESSAGE_LENGTH");e(r);return}this._fragments.push(a)}this.dataMessage(e),this._state===lt&&this.startLoop(e)})}dataMessage(t){if(!this._fin){this._state=lt;return}let e=this._messageLength,s=this._fragments;if(this._totalPayloadLength=0,this._messageLength=0,this._fragmented=0,this._fragments=[],this._opcode===2){let n;this._binaryType==="nodebuffer"?n=Sa(s,e):this._binaryType==="arraybuffer"?n=ic(Sa(s,e)):this._binaryType==="blob"?n=new Blob(s):n=s,this._allowSynchronousEvents?(this.emit("message",n,!0),this._state=lt):(this._state=Tn,setImmediate(()=>{this.emit("message",n,!0),this._state=lt,this.startLoop(t)}))}else{let n=Sa(s,e);if(!this._skipUTF8Validation&&!vr(n)){let a=this.createError(Error,"invalid UTF-8 sequence",!0,1007,"WS_ERR_INVALID_UTF8");t(a);return}this._state===Ta||this._allowSynchronousEvents?(this.emit("message",n,!1),this._state=lt):(this._state=Tn,setImmediate(()=>{this.emit("message",n,!1),this._state=lt,this.startLoop(t)}))}}controlMessage(t,e){if(this._opcode===8){if(t.length===0)this._loop=!1,this.emit("conclude",1005,yr),this.end();else{let s=t.readUInt16BE(0);if(!oc(s)){let a=this.createError(RangeError,`invalid status code ${s}`,!0,1002,"WS_ERR_INVALID_CLOSE_CODE");e(a);return}let n=new Cn(t.buffer,t.byteOffset+2,t.length-2);if(!this._skipUTF8Validation&&!vr(n)){let a=this.createError(Error,"invalid UTF-8 sequence",!0,1007,"WS_ERR_INVALID_UTF8");e(a);return}this._loop=!1,this.emit("conclude",s,n),this.end()}this._state=lt;return}this._allowSynchronousEvents?(this.emit(this._opcode===9?"ping":"pong",t),this._state=lt):(this._state=Tn,setImmediate(()=>{this.emit(this._opcode===9?"ping":"pong",t),this._state=lt,this.startLoop(e)}))}createError(t,e,s,n,a){this._loop=!1,this._errored=!0;let r=new t(s?`Invalid WebSocket frame: ${e}`:e);return Error.captureStackTrace(r,this.createError),r.code=a,r[nc]=n,r}};xr.exports=_a});var Ra=Ye((wu,Tr)=>{"use strict";var{Duplex:bu}=require("stream"),{randomFillSync:lc}=require("crypto"),Sr=ts(),{EMPTY_BUFFER:cc,kWebSocket:dc,NOOP:hc}=bt(),{isBlob:ns,isValidStatusCode:uc}=ss(),{mask:Cr,toBuffer:jt}=As(),ct=Symbol("kByteLength"),pc=Buffer.alloc(4),_n=8*1024,Wt,as=_n,ht=0,mc=1,fc=2,Aa=class i{constructor(t,e,s){this._extensions=e||{},s&&(this._generateMask=s,this._maskBuffer=Buffer.alloc(4)),this._socket=t,this._firstFragment=!0,this._compress=!1,this._bufferedBytes=0,this._queue=[],this._state=ht,this.onerror=hc,this[dc]=void 0}static frame(t,e){let s,n=!1,a=2,r=!1;e.mask&&(s=e.maskBuffer||pc,e.generateMask?e.generateMask(s):(as===_n&&(Wt===void 0&&(Wt=Buffer.alloc(_n)),lc(Wt,0,_n),as=0),s[0]=Wt[as++],s[1]=Wt[as++],s[2]=Wt[as++],s[3]=Wt[as++]),r=(s[0]|s[1]|s[2]|s[3])===0,a=6);let o;typeof t=="string"?(!e.mask||r)&&e[ct]!==void 0?o=e[ct]:(t=Buffer.from(t),o=t.length):(o=t.length,n=e.mask&&e.readOnly&&!r);let c=o;o>=65536?(a+=8,c=127):o>125&&(a+=2,c=126);let l=Buffer.allocUnsafe(n?o+a:a);return l[0]=e.fin?e.opcode|128:e.opcode,e.rsv1&&(l[0]|=64),l[1]=c,c===126?l.writeUInt16BE(o,2):c===127&&(l[2]=l[3]=0,l.writeUIntBE(o,4,6)),e.mask?(l[1]|=128,l[a-4]=s[0],l[a-3]=s[1],l[a-2]=s[2],l[a-1]=s[3],r?[l,t]:n?(Cr(t,s,l,a,o),[l]):(Cr(t,s,t,0,o),[l,t])):[l,t]}close(t,e,s,n){let a;if(t===void 0)a=cc;else{if(typeof t!="number"||!uc(t))throw new TypeError("First argument must be a valid error code number");if(e===void 0||!e.length)a=Buffer.allocUnsafe(2),a.writeUInt16BE(t,0);else{let o=Buffer.byteLength(e);if(o>123)throw new RangeError("The message must not be greater than 123 bytes");a=Buffer.allocUnsafe(2+o),a.writeUInt16BE(t,0),typeof e=="string"?a.write(e,2):a.set(e,2)}}let r={[ct]:a.length,fin:!0,generateMask:this._generateMask,mask:s,maskBuffer:this._maskBuffer,opcode:8,readOnly:!1,rsv1:!1};this._state!==ht?this.enqueue([this.dispatch,a,!1,r,n]):this.sendFrame(i.frame(a,r),n)}ping(t,e,s){let n,a;if(typeof t=="string"?(n=Buffer.byteLength(t),a=!1):ns(t)?(n=t.size,a=!1):(t=jt(t),n=t.length,a=jt.readOnly),n>125)throw new RangeError("The data size must not be greater than 125 bytes");let r={[ct]:n,fin:!0,generateMask:this._generateMask,mask:e,maskBuffer:this._maskBuffer,opcode:9,readOnly:a,rsv1:!1};ns(t)?this._state!==ht?this.enqueue([this.getBlobData,t,!1,r,s]):this.getBlobData(t,!1,r,s):this._state!==ht?this.enqueue([this.dispatch,t,!1,r,s]):this.sendFrame(i.frame(t,r),s)}pong(t,e,s){let n,a;if(typeof t=="string"?(n=Buffer.byteLength(t),a=!1):ns(t)?(n=t.size,a=!1):(t=jt(t),n=t.length,a=jt.readOnly),n>125)throw new RangeError("The data size must not be greater than 125 bytes");let r={[ct]:n,fin:!0,generateMask:this._generateMask,mask:e,maskBuffer:this._maskBuffer,opcode:10,readOnly:a,rsv1:!1};ns(t)?this._state!==ht?this.enqueue([this.getBlobData,t,!1,r,s]):this.getBlobData(t,!1,r,s):this._state!==ht?this.enqueue([this.dispatch,t,!1,r,s]):this.sendFrame(i.frame(t,r),s)}send(t,e,s){let n=this._extensions[Sr.extensionName],a=e.binary?2:1,r=e.compress,o,c;typeof t=="string"?(o=Buffer.byteLength(t),c=!1):ns(t)?(o=t.size,c=!1):(t=jt(t),o=t.length,c=jt.readOnly),this._firstFragment?(this._firstFragment=!1,r&&n&&n.params[n._isServer?"server_no_context_takeover":"client_no_context_takeover"]&&(r=o>=n._threshold),this._compress=r):(r=!1,a=0),e.fin&&(this._firstFragment=!0);let l={[ct]:o,fin:e.fin,generateMask:this._generateMask,mask:e.mask,maskBuffer:this._maskBuffer,opcode:a,readOnly:c,rsv1:r};ns(t)?this._state!==ht?this.enqueue([this.getBlobData,t,this._compress,l,s]):this.getBlobData(t,this._compress,l,s):this._state!==ht?this.enqueue([this.dispatch,t,this._compress,l,s]):this.dispatch(t,this._compress,l,s)}getBlobData(t,e,s,n){this._bufferedBytes+=s[ct],this._state=fc,t.arrayBuffer().then(a=>{if(this._socket.destroyed){let o=new Error("The socket was closed while the blob was being read");process.nextTick(Pa,this,o,n);return}this._bufferedBytes-=s[ct];let r=jt(a);e?this.dispatch(r,e,s,n):(this._state=ht,this.sendFrame(i.frame(r,s),n),this.dequeue())}).catch(a=>{process.nextTick(gc,this,a,n)})}dispatch(t,e,s,n){if(!e){this.sendFrame(i.frame(t,s),n);return}let a=this._extensions[Sr.extensionName];this._bufferedBytes+=s[ct],this._state=mc,a.compress(t,s.fin,(r,o)=>{if(this._socket.destroyed){let c=new Error("The socket was closed while data was being compressed");Pa(this,c,n);return}this._bufferedBytes-=s[ct],this._state=ht,s.readOnly=!1,this.sendFrame(i.frame(o,s),n),this.dequeue()})}dequeue(){for(;this._state===ht&&this._queue.length;){let t=this._queue.shift();this._bufferedBytes-=t[3][ct],Reflect.apply(t[0],this,t.slice(1))}}enqueue(t){this._bufferedBytes+=t[3][ct],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)}};Tr.exports=Aa;function Pa(i,t,e){typeof e=="function"&&e(t);for(let s=0;s<i._queue.length;s++){let n=i._queue[s],a=n[n.length-1];typeof a=="function"&&a(t)}}function gc(i,t,e){Pa(i,t,e),i.onerror(t)}});var Lr=Ye((ku,Mr)=>{"use strict";var{kForOnEventAttribute:Rs,kListener:Da}=bt(),_r=Symbol("kCode"),Er=Symbol("kData"),Ar=Symbol("kError"),Pr=Symbol("kMessage"),Rr=Symbol("kReason"),is=Symbol("kTarget"),Dr=Symbol("kType"),Ir=Symbol("kWasClean"),kt=class{constructor(t){this[is]=null,this[Dr]=t}get target(){return this[is]}get type(){return this[Dr]}};Object.defineProperty(kt.prototype,"target",{enumerable:!0});Object.defineProperty(kt.prototype,"type",{enumerable:!0});var Ht=class extends kt{constructor(t,e={}){super(t),this[_r]=e.code===void 0?0:e.code,this[Rr]=e.reason===void 0?"":e.reason,this[Ir]=e.wasClean===void 0?!1:e.wasClean}get code(){return this[_r]}get reason(){return this[Rr]}get wasClean(){return this[Ir]}};Object.defineProperty(Ht.prototype,"code",{enumerable:!0});Object.defineProperty(Ht.prototype,"reason",{enumerable:!0});Object.defineProperty(Ht.prototype,"wasClean",{enumerable:!0});var rs=class extends kt{constructor(t,e={}){super(t),this[Ar]=e.error===void 0?null:e.error,this[Pr]=e.message===void 0?"":e.message}get error(){return this[Ar]}get message(){return this[Pr]}};Object.defineProperty(rs.prototype,"error",{enumerable:!0});Object.defineProperty(rs.prototype,"message",{enumerable:!0});var Ds=class extends kt{constructor(t,e={}){super(t),this[Er]=e.data===void 0?null:e.data}get data(){return this[Er]}};Object.defineProperty(Ds.prototype,"data",{enumerable:!0});var yc={addEventListener(i,t,e={}){for(let n of this.listeners(i))if(!e[Rs]&&n[Da]===t&&!n[Rs])return;let s;if(i==="message")s=function(a,r){let o=new Ds("message",{data:r?a:a.toString()});o[is]=this,En(t,this,o)};else if(i==="close")s=function(a,r){let o=new Ht("close",{code:a,reason:r.toString(),wasClean:this._closeFrameReceived&&this._closeFrameSent});o[is]=this,En(t,this,o)};else if(i==="error")s=function(a){let r=new rs("error",{error:a,message:a.message});r[is]=this,En(t,this,r)};else if(i==="open")s=function(){let a=new kt("open");a[is]=this,En(t,this,a)};else return;s[Rs]=!!e[Rs],s[Da]=t,e.once?this.once(i,s):this.on(i,s)},removeEventListener(i,t){for(let e of this.listeners(i))if(e[Da]===t&&!e[Rs]){this.removeListener(i,e);break}}};Mr.exports={CloseEvent:Ht,ErrorEvent:rs,Event:kt,EventTarget:yc,MessageEvent:Ds};function En(i,t,e){typeof i=="object"&&i.handleEvent?i.handleEvent.call(i,e):i.call(t,e)}});var An=Ye((xu,Fr)=>{"use strict";var{tokenChars:Is}=ss();function ft(i,t,e){i[t]===void 0?i[t]=[e]:i[t].push(e)}function vc(i){let t=Object.create(null),e=Object.create(null),s=!1,n=!1,a=!1,r,o,c=-1,l=-1,d=-1,h=0;for(;h<i.length;h++)if(l=i.charCodeAt(h),r===void 0)if(d===-1&&Is[l]===1)c===-1&&(c=h);else if(h!==0&&(l===32||l===9))d===-1&&c!==-1&&(d=h);else if(l===59||l===44){if(c===-1)throw new SyntaxError(`Unexpected character at index ${h}`);d===-1&&(d=h);let p=i.slice(c,d);l===44?(ft(t,p,e),e=Object.create(null)):r=p,c=d=-1}else throw new SyntaxError(`Unexpected character at index ${h}`);else if(o===void 0)if(d===-1&&Is[l]===1)c===-1&&(c=h);else if(l===32||l===9)d===-1&&c!==-1&&(d=h);else if(l===59||l===44){if(c===-1)throw new SyntaxError(`Unexpected character at index ${h}`);d===-1&&(d=h),ft(e,i.slice(c,d),!0),l===44&&(ft(t,r,e),e=Object.create(null),r=void 0),c=d=-1}else if(l===61&&c!==-1&&d===-1)o=i.slice(c,h),c=d=-1;else throw new SyntaxError(`Unexpected character at index ${h}`);else if(n){if(Is[l]!==1)throw new SyntaxError(`Unexpected character at index ${h}`);c===-1?c=h:s||(s=!0),n=!1}else if(a)if(Is[l]===1)c===-1&&(c=h);else if(l===34&&c!==-1)a=!1,d=h;else if(l===92)n=!0;else throw new SyntaxError(`Unexpected character at index ${h}`);else if(l===34&&i.charCodeAt(h-1)===61)a=!0;else if(d===-1&&Is[l]===1)c===-1&&(c=h);else if(c!==-1&&(l===32||l===9))d===-1&&(d=h);else if(l===59||l===44){if(c===-1)throw new SyntaxError(`Unexpected character at index ${h}`);d===-1&&(d=h);let p=i.slice(c,d);s&&(p=p.replace(/\\/g,""),s=!1),ft(e,o,p),l===44&&(ft(t,r,e),e=Object.create(null),r=void 0),o=void 0,c=d=-1}else throw new SyntaxError(`Unexpected character at index ${h}`);if(c===-1||a||l===32||l===9)throw new SyntaxError("Unexpected end of input");d===-1&&(d=h);let u=i.slice(c,d);return r===void 0?ft(t,u,e):(o===void 0?ft(e,u,!0):s?ft(e,o,u.replace(/\\/g,"")):ft(e,o,u),ft(t,r,e)),t}function bc(i){return Object.keys(i).map(t=>{let e=i[t];return Array.isArray(e)||(e=[e]),e.map(s=>[t].concat(Object.keys(s).map(n=>{let a=s[n];return Array.isArray(a)||(a=[a]),a.map(r=>r===!0?n:`${n}=${r}`).join("; ")})).join("; ")).join(", ")}).join(", ")}Fr.exports={format:bc,parse:vc}});var In=Ye((Tu,Vr)=>{"use strict";var wc=require("events"),kc=require("https"),xc=require("http"),Br=require("net"),Sc=require("tls"),{randomBytes:Cc,createHash:Tc}=require("crypto"),{Duplex:Su,Readable:Cu}=require("stream"),{URL:Ia}=require("url"),Rt=ts(),_c=Ea(),Ec=Ra(),{isBlob:Ac}=ss(),{BINARY_TYPES:Or,CLOSE_TIMEOUT:Pc,EMPTY_BUFFER:Pn,GUID:Rc,kForOnEventAttribute:Ma,kListener:Dc,kStatusCode:Ic,kWebSocket:Le,NOOP:Ur}=bt(),{EventTarget:{addEventListener:Mc,removeEventListener:Lc}}=Lr(),{format:Fc,parse:Oc}=An(),{toBuffer:Nc}=As(),$r=Symbol("kAborted"),La=[8,13],xt=["CONNECTING","OPEN","CLOSING","CLOSED"],Bc=/^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/,ke=class i extends wc{constructor(t,e,s){super(),this._binaryType=Or[0],this._closeCode=1006,this._closeFrameReceived=!1,this._closeFrameSent=!1,this._closeMessage=Pn,this._closeTimer=null,this._errorEmitted=!1,this._extensions={},this._paused=!1,this._protocol="",this._readyState=i.CONNECTING,this._receiver=null,this._sender=null,this._socket=null,t!==null?(this._bufferedAmount=0,this._isServer=!1,this._redirects=0,e===void 0?e=[]:Array.isArray(e)||(typeof e=="object"&&e!==null?(s=e,e=[]):e=[e]),jr(this,t,e,s)):(this._autoPong=s.autoPong,this._closeTimeout=s.closeTimeout,this._isServer=!0)}get binaryType(){return this._binaryType}set binaryType(t){Or.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 n=new _c({allowSynchronousEvents:s.allowSynchronousEvents,binaryType:this.binaryType,extensions:this._extensions,isServer:this._isServer,maxPayload:s.maxPayload,skipUTF8Validation:s.skipUTF8Validation}),a=new Ec(t,this._extensions,s.generateMask);this._receiver=n,this._sender=a,this._socket=t,n[Le]=this,a[Le]=this,t[Le]=this,n.on("conclude",jc),n.on("drain",Wc),n.on("error",Hc),n.on("message",qc),n.on("ping",zc),n.on("pong",Gc),a.onerror=Vc,t.setTimeout&&t.setTimeout(0),t.setNoDelay&&t.setNoDelay(),e.length>0&&t.unshift(e),t.on("close",qr),t.on("data",Dn),t.on("end",zr),t.on("error",Gr),this._readyState=i.OPEN,this.emit("open")}emitClose(){if(!this._socket){this._readyState=i.CLOSED,this.emit("close",this._closeCode,this._closeMessage);return}this._extensions[Rt.extensionName]&&this._extensions[Rt.extensionName].cleanup(),this._receiver.removeAllListeners(),this._readyState=i.CLOSED,this.emit("close",this._closeCode,this._closeMessage)}close(t,e){if(this.readyState!==i.CLOSED){if(this.readyState===i.CONNECTING){Ze(this,this._req,"WebSocket was closed before the connection was established");return}if(this.readyState===i.CLOSING){this._closeFrameSent&&(this._closeFrameReceived||this._receiver._writableState.errorEmitted)&&this._socket.end();return}this._readyState=i.CLOSING,this._sender.close(t,e,!this._isServer,s=>{s||(this._closeFrameSent=!0,(this._closeFrameReceived||this._receiver._writableState.errorEmitted)&&this._socket.end())}),Hr(this)}}pause(){this.readyState===i.CONNECTING||this.readyState===i.CLOSED||(this._paused=!0,this._socket.pause())}ping(t,e,s){if(this.readyState===i.CONNECTING)throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");if(typeof t=="function"?(s=t,t=e=void 0):typeof e=="function"&&(s=e,e=void 0),typeof t=="number"&&(t=t.toString()),this.readyState!==i.OPEN){Fa(this,t,s);return}e===void 0&&(e=!this._isServer),this._sender.ping(t||Pn,e,s)}pong(t,e,s){if(this.readyState===i.CONNECTING)throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");if(typeof t=="function"?(s=t,t=e=void 0):typeof e=="function"&&(s=e,e=void 0),typeof t=="number"&&(t=t.toString()),this.readyState!==i.OPEN){Fa(this,t,s);return}e===void 0&&(e=!this._isServer),this._sender.pong(t||Pn,e,s)}resume(){this.readyState===i.CONNECTING||this.readyState===i.CLOSED||(this._paused=!1,this._receiver._writableState.needDrain||this._socket.resume())}send(t,e,s){if(this.readyState===i.CONNECTING)throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");if(typeof e=="function"&&(s=e,e={}),typeof t=="number"&&(t=t.toString()),this.readyState!==i.OPEN){Fa(this,t,s);return}let n={binary:typeof t!="string",mask:!this._isServer,compress:!0,fin:!0,...e};this._extensions[Rt.extensionName]||(n.compress=!1),this._sender.send(t||Pn,n,s)}terminate(){if(this.readyState!==i.CLOSED){if(this.readyState===i.CONNECTING){Ze(this,this._req,"WebSocket was closed before the connection was established");return}this._socket&&(this._readyState=i.CLOSING,this._socket.destroy())}}};Object.defineProperty(ke,"CONNECTING",{enumerable:!0,value:xt.indexOf("CONNECTING")});Object.defineProperty(ke.prototype,"CONNECTING",{enumerable:!0,value:xt.indexOf("CONNECTING")});Object.defineProperty(ke,"OPEN",{enumerable:!0,value:xt.indexOf("OPEN")});Object.defineProperty(ke.prototype,"OPEN",{enumerable:!0,value:xt.indexOf("OPEN")});Object.defineProperty(ke,"CLOSING",{enumerable:!0,value:xt.indexOf("CLOSING")});Object.defineProperty(ke.prototype,"CLOSING",{enumerable:!0,value:xt.indexOf("CLOSING")});Object.defineProperty(ke,"CLOSED",{enumerable:!0,value:xt.indexOf("CLOSED")});Object.defineProperty(ke.prototype,"CLOSED",{enumerable:!0,value:xt.indexOf("CLOSED")});["binaryType","bufferedAmount","extensions","isPaused","protocol","readyState","url"].forEach(i=>{Object.defineProperty(ke.prototype,i,{enumerable:!0})});["open","error","close","message"].forEach(i=>{Object.defineProperty(ke.prototype,`on${i}`,{enumerable:!0,get(){for(let t of this.listeners(i))if(t[Ma])return t[Dc];return null},set(t){for(let e of this.listeners(i))if(e[Ma]){this.removeListener(i,e);break}typeof t=="function"&&this.addEventListener(i,t,{[Ma]:!0})}})});ke.prototype.addEventListener=Mc;ke.prototype.removeEventListener=Lc;Vr.exports=ke;function jr(i,t,e,s){let n={allowSynchronousEvents:!0,autoPong:!0,closeTimeout:Pc,protocolVersion:La[1],maxPayload:104857600,skipUTF8Validation:!1,perMessageDeflate:!0,followRedirects:!1,maxRedirects:10,...s,socketPath:void 0,hostname:void 0,protocol:void 0,timeout:void 0,method:"GET",host:void 0,path:void 0,port:void 0};if(i._autoPong=n.autoPong,i._closeTimeout=n.closeTimeout,!La.includes(n.protocolVersion))throw new RangeError(`Unsupported protocol version: ${n.protocolVersion} (supported versions: ${La.join(", ")})`);let a;if(t instanceof Ia)a=t;else try{a=new Ia(t)}catch{throw new SyntaxError(`Invalid URL: ${t}`)}a.protocol==="http:"?a.protocol="ws:":a.protocol==="https:"&&(a.protocol="wss:"),i._url=a.href;let r=a.protocol==="wss:",o=a.protocol==="ws+unix:",c;if(a.protocol!=="ws:"&&!r&&!o?c=`The URL's protocol must be one of "ws:", "wss:", "http:", "https:", or "ws+unix:"`:o&&!a.pathname?c="The URL's pathname is empty":a.hash&&(c="The URL contains a fragment identifier"),c){let f=new SyntaxError(c);if(i._redirects===0)throw f;Rn(i,f);return}let l=r?443:80,d=Cc(16).toString("base64"),h=r?kc.request:xc.request,u=new Set,p;if(n.createConnection=n.createConnection||(r?$c:Uc),n.defaultPort=n.defaultPort||l,n.port=a.port||l,n.host=a.hostname.startsWith("[")?a.hostname.slice(1,-1):a.hostname,n.headers={...n.headers,"Sec-WebSocket-Version":n.protocolVersion,"Sec-WebSocket-Key":d,Connection:"Upgrade",Upgrade:"websocket"},n.path=a.pathname+a.search,n.timeout=n.handshakeTimeout,n.perMessageDeflate&&(p=new Rt({...n.perMessageDeflate,isServer:!1,maxPayload:n.maxPayload}),n.headers["Sec-WebSocket-Extensions"]=Fc({[Rt.extensionName]:p.offer()})),e.length){for(let f of e){if(typeof f!="string"||!Bc.test(f)||u.has(f))throw new SyntaxError("An invalid or duplicated subprotocol was specified");u.add(f)}n.headers["Sec-WebSocket-Protocol"]=e.join(",")}if(n.origin&&(n.protocolVersion<13?n.headers["Sec-WebSocket-Origin"]=n.origin:n.headers.Origin=n.origin),(a.username||a.password)&&(n.auth=`${a.username}:${a.password}`),o){let f=n.path.split(":");n.socketPath=f[0],n.path=f[1]}let m;if(n.followRedirects){if(i._redirects===0){i._originalIpc=o,i._originalSecure=r,i._originalHostOrSocketPath=o?n.socketPath:a.host;let f=s&&s.headers;if(s={...s,headers:{}},f)for(let[y,w]of Object.entries(f))s.headers[y.toLowerCase()]=w}else if(i.listenerCount("redirect")===0){let f=o?i._originalIpc?n.socketPath===i._originalHostOrSocketPath:!1:i._originalIpc?!1:a.host===i._originalHostOrSocketPath;(!f||i._originalSecure&&!r)&&(delete n.headers.authorization,delete n.headers.cookie,f||delete n.headers.host,n.auth=void 0)}n.auth&&!s.headers.authorization&&(s.headers.authorization="Basic "+Buffer.from(n.auth).toString("base64")),m=i._req=h(n),i._redirects&&i.emit("redirect",i.url,m)}else m=i._req=h(n);n.timeout&&m.on("timeout",()=>{Ze(i,m,"Opening handshake has timed out")}),m.on("error",f=>{m===null||m[$r]||(m=i._req=null,Rn(i,f))}),m.on("response",f=>{let y=f.headers.location,w=f.statusCode;if(y&&n.followRedirects&&w>=300&&w<400){if(++i._redirects>n.maxRedirects){Ze(i,m,"Maximum redirects exceeded");return}m.abort();let k;try{k=new Ia(y,t)}catch{let v=new SyntaxError(`Invalid URL: ${y}`);Rn(i,v);return}jr(i,k,e,s)}else i.emit("unexpected-response",m,f)||Ze(i,m,`Unexpected server response: ${f.statusCode}`)}),m.on("upgrade",(f,y,w)=>{if(i.emit("upgrade",f),i.readyState!==ke.CONNECTING)return;m=i._req=null;let k=f.headers.upgrade;if(k===void 0||k.toLowerCase()!=="websocket"){Ze(i,y,"Invalid Upgrade header");return}let g=Tc("sha1").update(d+Rc).digest("base64");if(f.headers["sec-websocket-accept"]!==g){Ze(i,y,"Invalid Sec-WebSocket-Accept header");return}let v=f.headers["sec-websocket-protocol"],S;if(v!==void 0?u.size?u.has(v)||(S="Server sent an invalid subprotocol"):S="Server sent a subprotocol but none was requested":u.size&&(S="Server sent no subprotocol"),S){Ze(i,y,S);return}v&&(i._protocol=v);let T=f.headers["sec-websocket-extensions"];if(T!==void 0){if(!p){Ze(i,y,"Server sent a Sec-WebSocket-Extensions header but no extension was requested");return}let _;try{_=Oc(T)}catch{Ze(i,y,"Invalid Sec-WebSocket-Extensions header");return}let D=Object.keys(_);if(D.length!==1||D[0]!==Rt.extensionName){Ze(i,y,"Server indicated an extension that was not requested");return}try{p.accept(_[Rt.extensionName])}catch{Ze(i,y,"Invalid Sec-WebSocket-Extensions header");return}i._extensions[Rt.extensionName]=p}i.setSocket(y,w,{allowSynchronousEvents:n.allowSynchronousEvents,generateMask:n.generateMask,maxPayload:n.maxPayload,skipUTF8Validation:n.skipUTF8Validation})}),n.finishRequest?n.finishRequest(m,i):m.end()}function Rn(i,t){i._readyState=ke.CLOSING,i._errorEmitted=!0,i.emit("error",t),i.emitClose()}function Uc(i){return i.path=i.socketPath,Br.connect(i)}function $c(i){return i.path=void 0,!i.servername&&i.servername!==""&&(i.servername=Br.isIP(i.host)?"":i.host),Sc.connect(i)}function Ze(i,t,e){i._readyState=ke.CLOSING;let s=new Error(e);Error.captureStackTrace(s,Ze),t.setHeader?(t[$r]=!0,t.abort(),t.socket&&!t.socket.destroyed&&t.socket.destroy(),process.nextTick(Rn,i,s)):(t.destroy(s),t.once("error",i.emit.bind(i,"error")),t.once("close",i.emitClose.bind(i)))}function Fa(i,t,e){if(t){let s=Ac(t)?t.size:Nc(t).length;i._socket?i._sender._bufferedBytes+=s:i._bufferedAmount+=s}if(e){let s=new Error(`WebSocket is not open: readyState ${i.readyState} (${xt[i.readyState]})`);process.nextTick(e,s)}}function jc(i,t){let e=this[Le];e._closeFrameReceived=!0,e._closeMessage=t,e._closeCode=i,e._socket[Le]!==void 0&&(e._socket.removeListener("data",Dn),process.nextTick(Wr,e._socket),i===1005?e.close():e.close(i,t))}function Wc(){let i=this[Le];i.isPaused||i._socket.resume()}function Hc(i){let t=this[Le];t._socket[Le]!==void 0&&(t._socket.removeListener("data",Dn),process.nextTick(Wr,t._socket),t.close(i[Ic])),t._errorEmitted||(t._errorEmitted=!0,t.emit("error",i))}function Nr(){this[Le].emitClose()}function qc(i,t){this[Le].emit("message",i,t)}function zc(i){let t=this[Le];t._autoPong&&t.pong(i,!this._isServer,Ur),t.emit("ping",i)}function Gc(i){this[Le].emit("pong",i)}function Wr(i){i.resume()}function Vc(i){let t=this[Le];t.readyState!==ke.CLOSED&&(t.readyState===ke.OPEN&&(t._readyState=ke.CLOSING,Hr(t)),this._socket.end(),t._errorEmitted||(t._errorEmitted=!0,t.emit("error",i)))}function Hr(i){i._closeTimer=setTimeout(i._socket.destroy.bind(i._socket),i._closeTimeout)}function qr(){let i=this[Le];if(this.removeListener("close",qr),this.removeListener("data",Dn),this.removeListener("end",zr),i._readyState=ke.CLOSING,!this._readableState.endEmitted&&!i._closeFrameReceived&&!i._receiver._writableState.errorEmitted&&this._readableState.length!==0){let t=this.read(this._readableState.length);i._receiver.write(t)}i._receiver.end(),this[Le]=void 0,clearTimeout(i._closeTimer),i._receiver._writableState.finished||i._receiver._writableState.errorEmitted?i.emitClose():(i._receiver.on("error",Nr),i._receiver.on("finish",Nr))}function Dn(i){this[Le]._receiver.write(i)||this.pause()}function zr(){let i=this[Le];i._readyState=ke.CLOSING,i._receiver.end(),this.end()}function Gr(){let i=this[Le];this.removeListener("error",Gr),this.on("error",Ur),i&&(i._readyState=ke.CLOSING,this.destroy())}});var Xr=Ye((Eu,Jr)=>{"use strict";var _u=In(),{Duplex:Yc}=require("stream");function Yr(i){i.emit("close")}function Kc(){!this.destroyed&&this._writableState.finished&&this.destroy()}function Kr(i){this.removeListener("error",Kr),this.destroy(),this.listenerCount("error")===0&&this.emit("error",i)}function Jc(i,t){let e=!0,s=new Yc({...t,autoDestroy:!1,emitClose:!1,objectMode:!1,writableObjectMode:!1});return i.on("message",function(a,r){let o=!r&&s._readableState.objectMode?a.toString():a;s.push(o)||i.pause()}),i.once("error",function(a){s.destroyed||(e=!1,s.destroy(a))}),i.once("close",function(){s.destroyed||s.push(null)}),s._destroy=function(n,a){if(i.readyState===i.CLOSED){a(n),process.nextTick(Yr,s);return}let r=!1;i.once("error",function(c){r=!0,a(c)}),i.once("close",function(){r||a(n),process.nextTick(Yr,s)}),e&&i.terminate()},s._final=function(n){if(i.readyState===i.CONNECTING){i.once("open",function(){s._final(n)});return}i._socket!==null&&(i._socket._writableState.finished?(n(),s._readableState.endEmitted&&s.destroy()):(i._socket.once("finish",function(){n()}),i.close()))},s._read=function(){i.isPaused&&i.resume()},s._write=function(n,a,r){if(i.readyState===i.CONNECTING){i.once("open",function(){s._write(n,a,r)});return}i.send(n,r)},s.on("end",Kc),s.on("error",Kr),s}Jr.exports=Jc});var Oa=Ye((Au,Qr)=>{"use strict";var{tokenChars:Xc}=ss();function Qc(i){let t=new Set,e=-1,s=-1,n=0;for(n;n<i.length;n++){let r=i.charCodeAt(n);if(s===-1&&Xc[r]===1)e===-1&&(e=n);else if(n!==0&&(r===32||r===9))s===-1&&e!==-1&&(s=n);else if(r===44){if(e===-1)throw new SyntaxError(`Unexpected character at index ${n}`);s===-1&&(s=n);let o=i.slice(e,s);if(t.has(o))throw new SyntaxError(`The "${o}" subprotocol is duplicated`);t.add(o),e=s=-1}else throw new SyntaxError(`Unexpected character at index ${n}`)}if(e===-1||s!==-1)throw new SyntaxError("Unexpected end of input");let a=i.slice(e,n);if(t.has(a))throw new SyntaxError(`The "${a}" subprotocol is duplicated`);return t.add(a),t}Qr.exports={parse:Qc}});var io=Ye((Ru,ao)=>{"use strict";var Zc=require("events"),Mn=require("http"),{Duplex:Pu}=require("stream"),{createHash:ed}=require("crypto"),Zr=An(),qt=ts(),td=Oa(),sd=In(),{CLOSE_TIMEOUT:nd,GUID:ad,kWebSocket:id}=bt(),rd=/^[+/0-9A-Za-z]{22}==$/,eo=0,to=1,no=2,Na=class extends Zc{constructor(t,e){if(super(),t={allowSynchronousEvents:!0,autoPong:!0,maxPayload:100*1024*1024,skipUTF8Validation:!1,perMessageDeflate:!1,handleProtocols:null,clientTracking:!0,closeTimeout:nd,verifyClient:null,noServer:!1,backlog:null,server:null,host:null,path:null,port:null,WebSocket:sd,...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=Mn.createServer((s,n)=>{let a=Mn.STATUS_CODES[426];n.writeHead(426,{"Content-Length":a.length,"Content-Type":"text/plain"}),n.end(a)}),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=od(this._server,{listening:this.emit.bind(this,"listening"),error:this.emit.bind(this,"error"),upgrade:(n,a,r)=>{this.handleUpgrade(n,a,r,s)}})}t.perMessageDeflate===!0&&(t.perMessageDeflate={}),t.clientTracking&&(this.clients=new Set,this._shouldEmitClose=!1),this.options=t,this._state=eo}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===no){t&&this.once("close",()=>{t(new Error("The server is not running"))}),process.nextTick(Ms,this);return}if(t&&this.once("close",t),this._state!==to)if(this._state=to,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(Ms,this):process.nextTick(Ms,this);else{let e=this._server;this._removeListeners(),this._removeListeners=this._server=null,e.close(()=>{Ms(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,n){e.on("error",so);let a=t.headers["sec-websocket-key"],r=t.headers.upgrade,o=+t.headers["sec-websocket-version"];if(t.method!=="GET"){zt(this,t,e,405,"Invalid HTTP method");return}if(r===void 0||r.toLowerCase()!=="websocket"){zt(this,t,e,400,"Invalid Upgrade header");return}if(a===void 0||!rd.test(a)){zt(this,t,e,400,"Missing or invalid Sec-WebSocket-Key header");return}if(o!==13&&o!==8){zt(this,t,e,400,"Missing or invalid Sec-WebSocket-Version header",{"Sec-WebSocket-Version":"13, 8"});return}if(!this.shouldHandle(t)){Ls(e,400);return}let c=t.headers["sec-websocket-protocol"],l=new Set;if(c!==void 0)try{l=td.parse(c)}catch{zt(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 qt({...this.options.perMessageDeflate,isServer:!0,maxPayload:this.options.maxPayload});try{let p=Zr.parse(d);p[qt.extensionName]&&(u.accept(p[qt.extensionName]),h[qt.extensionName]=u)}catch{zt(this,t,e,400,"Invalid or unacceptable Sec-WebSocket-Extensions header");return}}if(this.options.verifyClient){let u={origin:t.headers[`${o===8?"sec-websocket-origin":"origin"}`],secure:!!(t.socket.authorized||t.socket.encrypted),req:t};if(this.options.verifyClient.length===2){this.options.verifyClient(u,(p,m,f,y)=>{if(!p)return Ls(e,m||401,f,y);this.completeUpgrade(h,a,l,t,e,s,n)});return}if(!this.options.verifyClient(u))return Ls(e,401)}this.completeUpgrade(h,a,l,t,e,s,n)}completeUpgrade(t,e,s,n,a,r,o){if(!a.readable||!a.writable)return a.destroy();if(a[id])throw new Error("server.handleUpgrade() was called more than once with the same socket, possibly due to a misconfiguration");if(this._state>eo)return Ls(a,503);let l=["HTTP/1.1 101 Switching Protocols","Upgrade: websocket","Connection: Upgrade",`Sec-WebSocket-Accept: ${ed("sha1").update(e+ad).digest("base64")}`],d=new this.options.WebSocket(null,void 0,this.options);if(s.size){let h=this.options.handleProtocols?this.options.handleProtocols(s,n):s.values().next().value;h&&(l.push(`Sec-WebSocket-Protocol: ${h}`),d._protocol=h)}if(t[qt.extensionName]){let h=t[qt.extensionName].params,u=Zr.format({[qt.extensionName]:[h]});l.push(`Sec-WebSocket-Extensions: ${u}`),d._extensions=t}this.emit("headers",l,n),a.write(l.concat(`\r
|
|
2
2
|
`).join(`\r
|
|
3
|
-
`)),
|
|
4
|
-
`+Object.keys(s).map(
|
|
3
|
+
`)),a.removeListener("error",so),d.setSocket(a,r,{allowSynchronousEvents:this.options.allowSynchronousEvents,maxPayload:this.options.maxPayload,skipUTF8Validation:this.options.skipUTF8Validation}),this.clients&&(this.clients.add(d),d.on("close",()=>{this.clients.delete(d),this._shouldEmitClose&&!this.clients.size&&process.nextTick(Ms,this)})),o(d,n)}};ao.exports=Na;function od(i,t){for(let e of Object.keys(t))i.on(e,t[e]);return function(){for(let s of Object.keys(t))i.removeListener(s,t[s])}}function Ms(i){i._state=no,i.emit("close")}function so(){this.destroy()}function Ls(i,t,e,s){e=e||Mn.STATUS_CODES[t],s={Connection:"close","Content-Type":"text/html","Content-Length":Buffer.byteLength(e),...s},i.once("finish",i.destroy),i.end(`HTTP/1.1 ${t} ${Mn.STATUS_CODES[t]}\r
|
|
4
|
+
`+Object.keys(s).map(n=>`${n}: ${s[n]}`).join(`\r
|
|
5
5
|
`)+`\r
|
|
6
6
|
\r
|
|
7
|
-
`+e)}function
|
|
7
|
+
`+e)}function zt(i,t,e,s,n,a){if(i.listenerCount("wsClientError")){let r=new Error(n);Error.captureStackTrace(r,zt),i.emit("wsClientError",r,e,t)}else Ls(e,s,n,a)}});var Ad={};Lo(Ad,{default:()=>Wn});module.exports=Fo(Ad);var _o=require("fs"),Ce=require("obsidian");var Lt="agent-fleet-agents";var Et="agent-fleet-dashboard",rt="agent-fleet-chat",dt={fleetFolder:"_fleet",claudeCliPath:"claude",codexCliPath:"codex",defaultModel:"default",awsRegion:"us-east-1",maxConcurrentRuns:2,runLogRetentionDays:30,catchUpMissedTasks:!0,notificationLevel:"all",showStatusBar:!0,mcpApiKeys:{},mcpTokens:{},channelCredentials:{},maxConcurrentChannelSessions:5,channelIdleTimeoutMinutes:15,channelRateLimitPerConversation:20,channelRateLimitWindowMinutes:5,chatWatchdogMinutes:10,defaultFileHashes:{}},za=["agents","skills","tasks","runs","memory","channels"],Hs=1500,Ga="0 3 * * *",Va=3;var gi=require("path"),x=require("obsidian");var zn=[{path:"agents/fleet-orchestrator/CONTEXT.md",content:`---
|
|
8
8
|
{}
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -456,6 +456,8 @@ Explains: "Auto-compact now kicks in at 70% context instead of 85%. Next time th
|
|
|
456
456
|
| plan | Read-only | Hard-blocked | Research/analysis |
|
|
457
457
|
| default | Prompts for permission | Hard-blocked | Not useful for headless |
|
|
458
458
|
|
|
459
|
+
These are the Claude Code permission modes. **Codex agents** use sandbox levels instead \u2014 \`workspace-write\` / \`read-only\` (and \`bypassPermissions\` maps to full access); the modes above are mapped to the nearest Codex equivalent. \`Bash(...)\` allow/deny rules are translated to Codex execpolicy where possible (command-prefix patterns only). See the "Agent Configuration" permissions notes in \`tools.md\` for the full Codex behavior. The "Claude Code CLI Flags" section below applies to \`claude-code\` agents only.
|
|
460
|
+
|
|
459
461
|
## Cron Expression Format
|
|
460
462
|
|
|
461
463
|
Five fields: \`minute hour day-of-month month day-of-week\`
|
|
@@ -558,7 +560,7 @@ When the CLI returns an error result (context overflow, rate limit, auth issue),
|
|
|
558
560
|
- Renders a red error bubble in the chat: \`Error: <reason>\`
|
|
559
561
|
- Flips \`isStreaming\` back to false so the stop button clears and the user can send another message
|
|
560
562
|
|
|
561
|
-
A
|
|
563
|
+
A configurable watchdog (default 10 minutes; tunable via plugin Settings \u2192 "Chat watchdog timeout") additionally protects against CLI subprocess hangs: if no stream events arrive while streaming, the turn is auto-rejected, the process is killed, and a timeout error is surfaced in the chat. This guarantees the chat never gets permanently stuck.
|
|
562
564
|
`},{path:"skills/agent-fleet-system/skill.md",content:`---
|
|
563
565
|
name: agent-fleet-system
|
|
564
566
|
description: Complete knowledge of the Agent Fleet plugin \u2014 file structures, schemas, configuration, and management operations
|
|
@@ -572,7 +574,7 @@ You are an expert on the Obsidian Agent Fleet plugin. This skill gives you compl
|
|
|
572
574
|
|
|
573
575
|
## System Overview
|
|
574
576
|
|
|
575
|
-
Agent Fleet manages AI agents through markdown files in a \`_fleet/\` folder inside an Obsidian vault. Everything is files \u2014 agents, skills, tasks, run logs, and memory. Agents execute via headless Claude Code
|
|
577
|
+
Agent Fleet manages AI agents through markdown files in a \`_fleet/\` folder inside an Obsidian vault. Everything is files \u2014 agents, skills, tasks, run logs, and memory. Agents execute via a headless CLI backend, selectable per agent: **Claude Code** (default) or **OpenAI Codex**.
|
|
576
578
|
|
|
577
579
|
## Core Principle
|
|
578
580
|
|
|
@@ -637,21 +639,29 @@ who it is, how it behaves, what it does.
|
|
|
637
639
|
### config.md \u2014 Runtime Configuration
|
|
638
640
|
\`\`\`yaml
|
|
639
641
|
---
|
|
640
|
-
model: opus #
|
|
642
|
+
model: opus # Claude aliases work everywhere: opus / sonnet / haiku / opusplan.
|
|
641
643
|
# Or "default" (let CLI pick), or a pinned ID like
|
|
642
644
|
# "claude-opus-4-7" (direct), "us.anthropic.claude-opus-4-7" (Bedrock),
|
|
643
|
-
# "claude-opus-4-7@20251101" (Vertex).
|
|
644
|
-
|
|
645
|
+
# "claude-opus-4-7@20251101" (Vertex). For Codex agents use slugs
|
|
646
|
+
# like "gpt-5.5". Backend-agnostic aliases are preferred.
|
|
647
|
+
adapter: claude-code # "claude-code" (default) or "codex" (OpenAI Codex CLI)
|
|
645
648
|
timeout: 300 # Seconds before kill
|
|
646
649
|
max_retries: 1
|
|
647
650
|
cwd: "" # Working directory (empty = vault root)
|
|
648
|
-
permission_mode: bypassPermissions # "bypassPermissions", "dontAsk", "acceptEdits", "plan"
|
|
651
|
+
permission_mode: bypassPermissions # Claude: "bypassPermissions", "dontAsk", "acceptEdits", "plan".
|
|
652
|
+
# Codex: "bypassPermissions" (no sandbox), "workspace-write", "read-only".
|
|
653
|
+
# Claude-style values map to the nearest Codex sandbox automatically.
|
|
649
654
|
effort: "" # Reasoning effort: "low", "medium", "high", "max", or "" for default
|
|
655
|
+
# (Codex maps "max" to its "xhigh" level)
|
|
650
656
|
approval_required: []
|
|
651
657
|
allowed_tools: []
|
|
652
658
|
blocked_tools: []
|
|
653
|
-
memory: true # Persist context across runs
|
|
654
|
-
|
|
659
|
+
memory: true # Persist context across runs (two-tier store)
|
|
660
|
+
memory_token_budget: 1500 # Working-memory size cap (replaces memory_max_entries)
|
|
661
|
+
reflection_enabled: false # Nightly consolidation ("dreaming")
|
|
662
|
+
reflection_schedule: "0 3 * * *" # When reflection runs (cron)
|
|
663
|
+
reflection_recurrence_threshold: 3 # Times a pattern must recur before a skill proposal
|
|
664
|
+
reflection_propose_skills: false # Promote recurring patterns to skill proposals
|
|
655
665
|
auto_compact_threshold: 85 # Auto-invoke /compact when context reaches N% of window.
|
|
656
666
|
# 0 disables. Default 85. Applies to chat sessions.
|
|
657
667
|
wiki_references: # Optional \u2014 read-access to Wiki Keeper scopes.
|
|
@@ -683,6 +693,15 @@ Rules use Claude Code's native format:
|
|
|
683
693
|
- With \`bypassPermissions\` mode: everything runs EXCEPT deny list
|
|
684
694
|
- With \`dontAsk\` mode: only allow list runs, everything else blocked
|
|
685
695
|
|
|
696
|
+
Both adapters enforce these rules. \`claude-code\` applies them directly. \`codex\`
|
|
697
|
+
translates \`Bash(cmd args *)\` command patterns into execpolicy rules (deny \u2192
|
|
698
|
+
forbidden, allow \u2192 allow) injected via a per-agent \`CODEX_HOME\` overlay. Codex
|
|
699
|
+
limitations: only command-prefix \`Bash(...)\` patterns translate \u2014 tool-name
|
|
700
|
+
rules (\`Read\`/\`Write\`/\`Edit\`) and mid-pattern wildcards are ignored, and file/
|
|
701
|
+
network access is governed by the sandbox level (\`permission_mode\`:
|
|
702
|
+
bypassPermissions / workspace-write / read-only) instead. Requires a Codex build
|
|
703
|
+
with execpolicy support; otherwise the agent falls back to sandbox-only.
|
|
704
|
+
|
|
686
705
|
### SKILLS.md \u2014 Agent-Specific Skills (optional)
|
|
687
706
|
\`\`\`markdown
|
|
688
707
|
Additional instructions specific to this agent that aren't reusable as shared skills.
|
|
@@ -898,7 +917,23 @@ Status values: \`success\`, \`failure\`, \`timeout\`, \`cancelled\`, \`pending_a
|
|
|
898
917
|
|
|
899
918
|
## Agent Memory
|
|
900
919
|
|
|
901
|
-
When \`memory: true\`, the agent
|
|
920
|
+
When \`memory: true\`, the agent has a two-tier memory store at \`_fleet/memory/<agent-name>/\`:
|
|
921
|
+
|
|
922
|
+
- \`working.md\` \u2014 curated, token-budgeted memory that is **injected into every run** (sections: \`Preferences\` (pinned), \`Procedures\`, \`Observations\`, \`Recent\`).
|
|
923
|
+
- \`raw/<YYYY-MM-DD>.md\` \u2014 append-only ground-truth log of everything captured (never injected).
|
|
924
|
+
- \`candidates.json\` \u2014 reflection's skill-pattern ledger; \`pending/\` \u2014 the capture inbox.
|
|
925
|
+
|
|
926
|
+
(Legacy single-file \`_fleet/memory/<agent>.md\` stores are auto-migrated to this layout, seeding \`raw/\` first.)
|
|
927
|
+
|
|
928
|
+
**How an agent records a memory (two channels, same sink):**
|
|
929
|
+
1. **\`remember\` MCP tool (preferred)** \u2014 call \`remember(fact, pin?, section?)\`. It's auto-allowed (\`mcp__remember\`) for memory-enabled agents on **both Claude and Codex** backends. Structured and reliable.
|
|
930
|
+
2. **\`[REMEMBER] \u2026 [/REMEMBER]\` text tag (fallback)** \u2014 emit the tag in your output; \`[REMEMBER:pin]\` for a standing preference, \`[REMEMBER:procedure]\` for a how-to. Tags are stripped from the visible reply.
|
|
931
|
+
|
|
932
|
+
Record only **durable, reusable** facts \u2014 one concise line each (long/multi-line facts are collapsed and capped). Captures land in \`working.md\` immediately, so the next run/turn sees them. Memory is agent-scoped (shared across all conversations, including channels).
|
|
933
|
+
|
|
934
|
+
**Reflection ("dreaming").** If \`reflection_enabled: true\`, a scheduled nightly run (\`reflection_schedule\`, default \`0 3 * * *\`) consolidates \`working.md\` from the raw log: dedup, resolve contradictions, re-file \`Recent\` entries, summarize from ground truth to stay within \`memory_token_budget\` (default 1500), and keep pinned \`Preferences\`. With \`reflection_propose_skills: true\`, recurring friction (\u2265 \`reflection_recurrence_threshold\`, default 3) becomes an approval-gated skill proposal in the Inbox. A failed/empty reflection never wipes memory. Trigger manually with "Reflect now" on the agent.
|
|
935
|
+
|
|
936
|
+
> \`memory_max_entries\` is deprecated (no longer enforced) \u2014 size is governed by \`memory_token_budget\`.
|
|
902
937
|
|
|
903
938
|
## Prompt Assembly Order
|
|
904
939
|
|
|
@@ -907,7 +942,7 @@ When a task, heartbeat, or channel message runs, the prompt is assembled in this
|
|
|
907
942
|
2. Shared skills (from skill.md + tools.md + references.md + examples.md)
|
|
908
943
|
3. Agent-specific skills (SKILLS.md body)
|
|
909
944
|
4. Agent context (CONTEXT.md body)
|
|
910
|
-
5. Agent memory (
|
|
945
|
+
5. Agent memory (\`working.md\` + capture instruction, if enabled)
|
|
911
946
|
6. Channel context (if the message came from a channel)
|
|
912
947
|
7. **Wiki Access block** (if \`wiki_references\` is set \u2014 lists accessible wiki scopes)
|
|
913
948
|
8. Task prompt / heartbeat instruction / user message
|
|
@@ -11659,19 +11694,44 @@ python scripts/office/validate.py <path> [--original <original_file>] [--auto-re
|
|
|
11659
11694
|
|
|
11660
11695
|
- \`paraId\`/\`durableId\` values that exceed OOXML limits
|
|
11661
11696
|
- Missing \`xml:space="preserve"\` on \`w:t\` elements with whitespace
|
|
11662
|
-
`}];var
|
|
11697
|
+
`}];var qs=require("obsidian");function Q(i){let t=i.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);if(!t)return{frontmatter:{},body:i.trim()};let e=t[1]??"",s=t[2]??"",n;try{n=(0,qs.parseYaml)(e)??{}}catch(a){console.warn("Agent Fleet: malformed YAML frontmatter, treating as empty",a),n={}}return{frontmatter:n,body:s.trim()}}function z(i,t){let e=(0,qs.stringifyYaml)(i).trim(),s=t.trim();return`---
|
|
11663
11698
|
${e}
|
|
11664
11699
|
---
|
|
11665
11700
|
|
|
11666
11701
|
${s}
|
|
11667
|
-
`}function xe(r){return r.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/(^-|-$)/g,"")}function Ft(r,t){return r.length<=t?r:`${r.slice(0,t-1)}\u2026`}var ht=require("child_process"),en=require("fs"),Xt=require("os"),Tt=require("path");function da(){return(0,Xt.homedir)()}function Qt(){return(0,Tt.join)((0,Xt.homedir)(),".claude")}function Zt(){return(0,Tt.join)((0,Xt.homedir)(),".claude.json")}function tn(){if(process.platform==="darwin")return"/bin/zsh";for(let r of["/bin/bash","/bin/zsh","/bin/sh"])if((0,en.existsSync)(r))return r;return"/bin/sh"}function nr(r){return`'${r.replace(/'/g,"'\\''")}'`}function ut(r,t,e){let s={cwd:e?.cwd,env:e?.env};if(process.platform==="win32")return(0,ht.spawn)(r,t,s);let a=tn(),n=[r,...t].map(nr).join(" ");return(0,ht.spawn)(a,["-l","-c",n],s)}function ha(r,t){let e={cwd:t?.cwd,env:t?.env,stdio:["pipe","pipe","pipe"]};if(process.platform==="win32")return(0,ht.spawn)(r,[],{...e,shell:!0});let s=tn();return(0,ht.spawn)(s,["-l","-c",r],e)}function sn(r){try{require("electron").shell.openExternal(r)}catch{switch(process.platform){case"darwin":(0,ht.spawn)("open",[r],{stdio:"ignore"});break;case"win32":(0,ht.spawn)("cmd.exe",["/c","start","",r.replace(/&/g,"^&")],{stdio:"ignore"});break;default:(0,ht.spawn)("xdg-open",[r],{stdio:"ignore"});break}}}function me(r){return r.split(/\r?\n/)}function an(r){let t=(0,Xt.homedir)();if(process.platform==="win32")return[r,(0,Tt.join)(process.env.APPDATA??"","Claude","claude.exe"),(0,Tt.join)(process.env.LOCALAPPDATA??"","Claude","claude.exe"),(0,Tt.join)(t,".local","bin","claude.exe"),"claude.exe","claude"].filter(s=>!!s&&Za(s));let e=[r,(0,Tt.join)(t,".local","bin","claude")];return process.platform==="darwin"&&e.push("/opt/homebrew/bin/claude"),e.push("/usr/local/bin/claude","/usr/bin/claude","claude"),e.filter(s=>!!s&&Za(s))}function Za(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 ua(r){return!!(r.includes("/")||r.includes("\\"))}function ks(r){return typeof r=="object"&&r!==null}function M(r){return typeof r=="string"?r:void 0}function Ke(r,t){return typeof r=="boolean"?r:t}function $e(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 nn(r){return(0,C.normalizePath)(r.replace(/\.md$/,".permissions.json"))}function rn(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 es=class{constructor(t,e){this.vault=t;this.settings=e}agents=new Map;skills=new Map;tasks=new Map;channels=new Map;validationIssues=new Map;channelCredentialGetter;warnedFolderAgentModelConflict=new Set;warnedLegacyPerms=new Set;setChannelCredentialGetter(t){this.channelCredentialGetter=t}getVaultBasePath(){let t=this.vault.adapter;return t instanceof 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 Qa)await this.ensureFolder(this.getSubfolder(s));return e}async ensureSamples(){let t=this.getFleetRoot();for(let e of ca){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 ca){let n=(0,C.normalizePath)(`${e}/${a.path}`),i=rn(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=rn(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);if(n)if("taskId"in n)this.tasks.set(e.path,n);else if("model"in n){if(!n.isFolder){let i=nn(e.path),o=this.vault.getAbstractFileByPath(i);if(o instanceof C.TFile)try{let l=await this.vault.cachedRead(o),c=JSON.parse(l);n.permissionRules={allow:ye(c.allow),deny:ye(c.deny)}}catch{}}this.agents.set(e.path,n)}else this.skills.set(e.path,n)}async reloadFolderAgentContaining(t){let e=`${this.getSubfolder("agents")}/`,a=t.slice(e.length).split("/")[0];if(!a)return;let n=(0,C.normalizePath)(`${e}${a}`),i=(0,C.normalizePath)(`${n}/agent.md`);if(this.agents.delete(i),!(this.vault.getAbstractFileByPath(n)instanceof C.TFolder))return;let 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}=ae(s),i=M(a.name);if(!i)return this.setIssue(e.path,"Folder skill skill.md requires string field `name`."),null;let o=async l=>{let c=(0,C.normalizePath)(`${t}/${l}`),d=this.vault.getAbstractFileByPath(c);if(!(d instanceof C.TFile))return"";let h=await this.vault.cachedRead(d);return ae(h).body};return{filePath:e.path,name:i,description:M(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}=ae(s),i=M(a.name);if(!i)return this.setIssue(e.path,"Folder agent agent.md requires string field `name`."),null;let o={},l=(0,C.normalizePath)(`${t}/config.md`),c=this.vault.getAbstractFileByPath(l);if(c instanceof C.TFile){let N=await this.vault.cachedRead(c);o=ae(N).frontmatter}let d={allow:[],deny:[]},h=(0,C.normalizePath)(`${t}/permissions.json`),u=this.vault.getAbstractFileByPath(h);if(u instanceof C.TFile)try{let N=await this.vault.cachedRead(u),I=JSON.parse(N);d={allow:ye(I.allow),deny:ye(I.deny)}}catch{}if(d.allow.length===0&&d.deny.length===0){let N=ye(o.allowed_tools),I=ye(o.blocked_tools);(N.length>0||I.length>0)&&(d={allow:N,deny:I},this.warnedLegacyPerms.has(i)||(this.warnedLegacyPerms.add(i),console.warn(`Agent Fleet: "${i}" still uses legacy allowed_tools/blocked_tools in config.md. Permission rules now live in permissions.json. Open this agent in Edit and Save to migrate.`)))}let p="",f=(0,C.normalizePath)(`${t}/SKILLS.md`),m=this.vault.getAbstractFileByPath(f);if(m instanceof C.TFile){let N=await this.vault.cachedRead(m);p=ae(N).body}let g="",k=(0,C.normalizePath)(`${t}/CONTEXT.md`),b=this.vault.getAbstractFileByPath(k);if(b instanceof C.TFile){let N=await this.vault.cachedRead(b);g=ae(N).body}let v=!1,y="",x="",T=!0,_="",R=(0,C.normalizePath)(`${t}/HEARTBEAT.md`),A=this.vault.getAbstractFileByPath(R);if(A instanceof C.TFile){let N=await this.vault.cachedRead(A),I=ae(N);v=Ke(I.frontmatter.enabled,!1),y=M(I.frontmatter.schedule)??"",T=Ke(I.frontmatter.notify,!0),_=M(I.frontmatter.channel)??"",x=I.body}let S=M(a.model),L=M(o.model);S&&L&&S!==L&&(this.warnedFolderAgentModelConflict.has(i)||(this.warnedFolderAgentModelConflict.add(i),console.warn(`Agent Fleet: "${i}" has conflicting model fields \u2014 agent.md says "${S}", config.md says "${L}". config.md wins. Remove agent.md's model field or sync the values to silence this warning.`)));let E=L??S??this.settings.defaultModel;return{filePath:e.path,name:i,description:M(a.description),model:E,adapter:M(o.adapter)??"claude-code",permissionMode:M(o.permission_mode)??"bypassPermissions",effort:M(o.effort),maxRetries:$e(o.max_retries,1),skills:ye(a.skills),mcpServers:ye(a.mcp_servers),cwd:M(o.cwd)||M(a.cwd),enabled:Ke(a.enabled,!0),timeout:$e(o.timeout,$e(a.timeout,300)),approvalRequired:ye(o.approval_required),memory:Ke(o.memory,Ke(a.memory,!1)),memoryMaxEntries:$e(o.memory_max_entries,100),autoCompactThreshold:$e(o.auto_compact_threshold??a.auto_compact_threshold,85),tags:ye(a.tags),avatar:M(a.avatar)??"",body:n,contextBody:g,skillsBody:p,env:this.parseEnvMap(o.env),permissionRules:d,isFolder:!0,heartbeatEnabled:v,heartbeatSchedule:y,heartbeatBody:x,heartbeatNotify:T,heartbeatChannel:_,wikiKeeper:this.parseWikiKeeperConfig(o.wiki_keeper??a.wiki_keeper),wikiReferences:this.parseWikiReferences(o.wiki_references??a.wiki_references)}}parseWikiReferences(t){if(!Array.isArray(t))return;let e=[];for(let s of t)if(typeof s=="string"&&s.trim())e.push({agent:s.trim()});else if(s&&typeof s=="object"){let a=s.agent;typeof a=="string"&&a.trim()&&e.push({agent:a.trim()})}return e.length>0?e:void 0}parseWikiKeeperConfig(t){if(!t||typeof t!="object")return;let e=t;return{scopeRoot:M(e.scope_root)??"",inboxPath:M(e.inbox_path)??"_sources/inbox",archivePath:M(e.archive_path)??"_sources/archive",failedPath:M(e.failed_path)??"_sources/failed",topicsRoot:M(e.topics_root)??"_topics",indexPath:M(e.index_path)??"index.md",logPath:M(e.log_path)??"log.md",watchedFolders:ye(e.watched_folders),excludePatterns:ye(e.exclude_patterns),watchedSince:M(e.watched_since)??"",fileSubstantiveAnswers:Ke(e.file_substantive_answers,!0),obsidianUrlScheme:Ke(e.obsidian_url_scheme,!0),maxTokensPerIngest:$e(e.max_tokens_per_ingest,6e4),maxTokensPerRefresh:$e(e.max_tokens_per_refresh,3e4),indexSplitThreshold:$e(e.index_split_threshold,100),dedupSimilarityThreshold:$e(e.dedup_similarity_threshold,.82),summaryStaleDays:$e(e.summary_stale_days,30),stateFile:M(e.state_file)??".wiki-keeper-state.json"}}removeFile(t){this.clearStoredFile(t)}getSnapshot(){return{agents:Array.from(this.agents.values()).sort((t,e)=>t.name.localeCompare(e.name)),skills:Array.from(this.skills.values()).sort((t,e)=>t.name.localeCompare(e.name)),tasks:Array.from(this.tasks.values()).sort((t,e)=>t.taskId.localeCompare(e.taskId)),channels:Array.from(this.channels.values()).sort((t,e)=>t.name.localeCompare(e.name)),validationIssues:Array.from(this.validationIssues.values()).flat()}}getAgentByName(t){return Array.from(this.agents.values()).find(e=>e.name===t)}getSkillByName(t){return Array.from(this.skills.values()).find(e=>e.name===t)}getTaskById(t){return Array.from(this.tasks.values()).find(e=>e.taskId===t)}getTasksForAgent(t){return Array.from(this.tasks.values()).filter(e=>e.agent===t)}getChannelByName(t){return Array.from(this.channels.values()).find(e=>e.name===t)}getChannelsForAgent(t){return Array.from(this.channels.values()).filter(e=>e.defaultAgent===t)}getRunsRoot(){return this.getSubfolder("runs")}getMemoryPath(t){return(0,C.normalizePath)(`${this.getSubfolder("memory")}/${xe(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}=ae(a);return{filePath:e,agent:M(n.agent)??t,lastUpdated:M(n.last_updated),body:i}}async appendMemory(t,e){if(e.length===0)return;let s=this.getMemoryPath(t),a=this.vault.getAbstractFileByPath(s),n=new Date().toISOString(),i=e.map(o=>`- ${o.trim()}`).join(`
|
|
11668
|
-
`)
|
|
11669
|
-
|
|
11670
|
-
|
|
11671
|
-
|
|
11672
|
-
${
|
|
11673
|
-
|
|
11674
|
-
name:
|
|
11702
|
+
`}function se(i){return i.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/(^-|-$)/g,"")}function Vt(i,t){return i.length<=t?i:`${i.slice(0,t-1)}\u2026`}var zs=2,Ja=["Preferences","Procedures","Observations","Recent"],Xa="Recent",Vn=/\[REMEMBER(?::([a-zA-Z]+))?\]([\s\S]*?)\[\/REMEMBER\]/g;function Oo(i){switch((i??"").toLowerCase()){case"pin":case"preference":case"preferences":return{pinned:!0,section:"Preferences"};case"procedure":case"procedures":return{pinned:!1,section:"Procedures"};case"observation":case"observations":return{pinned:!1,section:"Observations"};default:return{pinned:!1}}}function Gs(i){let t=[];for(let e of i.matchAll(Vn)){let s=(e[2]??"").trim();if(!s)continue;let n=Oo(e[1]);t.push({text:s,pinned:n.pinned,section:n.section})}return t}function ds(i){return i.replace(Vn,"").replace(/[ \t]+\n/g,`
|
|
11703
|
+
`).replace(/\n{3,}/g,`
|
|
11704
|
+
|
|
11705
|
+
`).trim()}function Qa(i){let t=i.replace(Vn,""),e=t.match(/\[REMEMBER(?::[a-zA-Z]+)?\][\s\S]*$/i);if(e&&e.index!==void 0)return t.slice(0,e.index);let s=t.match(/\[(?:R(?:E(?:M(?:E(?:M(?:B(?:E(?:R(?::[a-zA-Z]*)?)?)?)?)?)?)?)?)?$/i);return s&&s.index!==void 0?t.slice(0,s.index):t}var No={Preferences:"Preferences",Procedures:"Procedures",Observations:"Observations",Recent:"Recent (uncurated)"};function Bo(i){let t=i.trim().toLowerCase();return t.startsWith("preference")?"Preferences":t.startsWith("procedure")?"Procedures":t.startsWith("observation")?"Observations":t.startsWith("recent")?"Recent":"Observations"}function hs(i){return Math.ceil(i.length/4)}var Ya=500;function Za(i){let t=i.replace(/\s+/g," ").trim();return t.length<=Ya?t:`${t.slice(0,Ya-1).trimEnd()}\u2026`}function Gn(i){return typeof i=="string"?i:void 0}function Uo(i,t){return typeof i=="number"&&Number.isFinite(i)?i:t}function ei(i,t){return{filePath:i,agent:t,schema:zs,tokenEstimate:0,sections:[]}}var $o=/\s*<!--\s*([\s\S]*?)\s*-->\s*$/,Ka=/^\[pin\]\s+/i;function jo(i){let t=i.match(/^[-*]\s+(.*)$/);if(!t)return null;let e=(t[1]??"").trim();if(!e)return null;let s,n,a=e.match($o);if(a){e=e.slice(0,a.index).trim();let o=(a[1]??"").trim(),c=o.match(/^src:(\S+)(?:\s+(.+))?$/);c?(s=c[1],n=c[2]?.trim()||void 0):o&&(n=o)}let r=!1;return Ka.test(e)&&(r=!0,e=e.replace(Ka,"").trim()),e?{text:e,source:s,date:n,pinned:r}:null}function ti(i){let t=i.pinned?"[pin] ":"",e="";if(i.source||i.date){let s=[];i.source&&s.push(`src:${i.source}`),i.date&&s.push(i.date),e=` <!-- ${s.join(" ")} -->`}return`- ${t}${i.text}${e}`}function Je(i){return Wo(i).map(e=>{let s=e.entries.map(ti).join(`
|
|
11706
|
+
`);return`## ${No[e.name]}
|
|
11707
|
+
${s}`}).join(`
|
|
11708
|
+
|
|
11709
|
+
`)}function Wo(i){let t=[];for(let e of Ja){let s=i.find(n=>n.name===e);s&&s.entries.length>0&&t.push(s)}return t}function si(i,t,e){let{frontmatter:s,body:n}=Q(i),a=Vs(n);return{filePath:t,agent:Gn(s.agent)??e,schema:Uo(s.schema,zs),lastUpdated:Gn(s.last_updated),lastReflection:Gn(s.last_reflection),tokenEstimate:hs(Je(a)),sections:a}}function Vs(i){let t=new Map,e=(a,r)=>{let o=t.get(a)??[];o.push(r),t.set(a,o)},s="Observations";for(let a of i.split(`
|
|
11710
|
+
`)){let r=a.match(/^#{1,6}\s+(.+?)\s*$/);if(r){s=Bo(r[1]??"");continue}let o=jo(a);o&&e(s,o)}let n=[];for(let a of Ja){let r=t.get(a);r&&r.length&&n.push({name:a,entries:r})}return n}function ni(i){let t=Je(i.sections),e={agent:i.agent,schema:i.schema||zs,last_updated:i.lastUpdated??"",token_estimate:hs(t)};return i.lastReflection&&(e.last_reflection=i.lastReflection),z(e,t||"## Observations")}function ai(i,t,e,s){if(t.length===0)return i;let n=i.sections.map(o=>({name:o.name,entries:[...o.entries]})),a=n.find(o=>o.name===e);return a||(a={name:e,entries:[]},n.push(a)),a.entries.push(...t),{...i,sections:n,lastUpdated:s??i.lastUpdated,tokenEstimate:hs(Je(n))}}var Ho=["Recent","Observations","Procedures"];function ii(i,t){if(i.tokenEstimate<=t)return{wm:i,spilled:[]};let e=i.sections.map(o=>({name:o.name,entries:[...o.entries]})),s=[],n=Je(e).length,a=()=>Math.ceil(n/4)>t;for(let o of Ho){if(!a())break;let c=e.find(l=>l.name===o);if(c)for(;c.entries.length>0&&a();){let l=c.entries.findIndex(h=>!h.pinned);if(l===-1)break;let d=c.entries.splice(l,1)[0];d&&(s.push(d),n-=ti(d).length+1)}}let r=e.filter(o=>o.entries.length>0);return{wm:{...i,sections:r,tokenEstimate:hs(Je(r))},spilled:s}}function Yn(i,t,e,s){let n=Vs(i);return{filePath:t,agent:e,schema:zs,lastUpdated:s,tokenEstimate:hs(Je(n)),sections:n}}function ri(i,t){let e=(i?.sections??[]).flatMap(o=>o.entries).filter(o=>o.pinned);if(e.length===0)return t;let s=new Set(t.flatMap(o=>o.entries).map(o=>o.text.trim().toLowerCase())),n=e.filter(o=>!s.has(o.text.trim().toLowerCase()));if(n.length===0)return t;let a=t.map(o=>({name:o.name,entries:[...o.entries]})),r=a.find(o=>o.name==="Preferences");return r||(r={name:"Preferences",entries:[]},a.unshift(r)),r.entries.unshift(...n),a}var qo="When you learn a durable fact about the user, their preferences, or how to do your work better, save it to memory. Prefer the `remember` tool \u2014 call remember(fact, pin?, section?); it is the reliable way to record a memory. If that tool is not available, fall back to writing [REMEMBER] <one concise fact> [/REMEMBER] in your reply (use [REMEMBER:pin] for standing preferences and hard constraints). Record only durable, reusable facts \u2014 not transient task details. These notes persist into your future runs.";function Ys(i,t){if(!i.memory)return"";let s=(t?Je(t.sections).trim():"")||"Nothing yet \u2014 this is a fresh agent.";return`## Memory
|
|
11711
|
+
${qo}
|
|
11712
|
+
|
|
11713
|
+
### What you've learned so far
|
|
11714
|
+
${s}`}var yt=require("child_process"),oi=require("fs"),Yt=require("os"),gt=require("path");function Kn(){return(0,Yt.homedir)()}function us(){return(0,gt.join)((0,Yt.homedir)(),".claude")}function ps(){return(0,gt.join)((0,Yt.homedir)(),".claude.json")}function li(){if(process.platform==="darwin")return"/bin/zsh";for(let i of["/bin/bash","/bin/zsh","/bin/sh"])if((0,oi.existsSync)(i))return i;return"/bin/sh"}function zo(i){return`'${i.replace(/'/g,"'\\''")}'`}function qe(i,t,e){let s={cwd:e?.cwd,env:e?.env};if(process.platform==="win32")return(0,yt.spawn)(i,t,s);let n=li(),a=[i,...t].map(zo).join(" ");return(0,yt.spawn)(n,["-l","-c",a],s)}function Jn(i,t){let e={cwd:t?.cwd,env:t?.env,stdio:["pipe","pipe","pipe"]};if(process.platform==="win32")return(0,yt.spawn)(i,[],{...e,shell:!0});let s=li();return(0,yt.spawn)(s,["-l","-c",i],e)}function ci(i){try{require("electron").shell.openExternal(i)}catch{switch(process.platform){case"darwin":(0,yt.spawn)("open",[i],{stdio:"ignore"});break;case"win32":(0,yt.spawn)("cmd.exe",["/c","start","",i.replace(/&/g,"^&")],{stdio:"ignore"});break;default:(0,yt.spawn)("xdg-open",[i],{stdio:"ignore"});break}}}function oe(i){return i.split(/\r?\n/)}function di(i){let t=(0,Yt.homedir)();if(process.platform==="win32")return[i,(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&&Ks(s));let e=[i,(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&&Ks(s))}function Xn(i){let t=(0,Yt.homedir)();if(process.platform==="win32")return[i,(0,gt.join)(t,".local","bin","codex.exe"),"codex.exe","codex"].filter(s=>!!s&&Ks(s));let e=[i,(0,gt.join)(t,".local","bin","codex")];return process.platform==="darwin"&&e.push("/opt/homebrew/bin/codex"),e.push("/usr/local/bin/codex","/usr/bin/codex","codex"),e.filter(s=>!!s&&Ks(s))}function Ks(i){return!i||/[\n\r\0]/.test(i)?!1:i.startsWith("/")?/^[\w/.@+-]+$/.test(i):i.startsWith("~")?/^~[\w/.@+-]*$/.test(i):/^[a-zA-Z]:[\\/]/.test(i)?/^[a-zA-Z]:[\\/][\w\\/. @+-]+$/.test(i):i.startsWith("\\\\")?/^\\\\[\w\\/. @+-]+$/.test(i):!i.includes("/")&&!i.includes("\\")?/^[\w.@+-]+$/.test(i):!1}function Qn(i){return!!(i.includes("/")||i.includes("\\"))}function Js(i){return typeof i=="object"&&i!==null}function I(i){return typeof i=="string"?i:void 0}function je(i,t){return typeof i=="boolean"?i:t}function Me(i,t){return typeof i=="number"&&Number.isFinite(i)?i:t}function ye(i){return Array.isArray(i)?i.filter(t=>typeof t=="string"):[]}var hi=!1;function ui(i,t={}){let e=i.memory_token_budget??t.memory_token_budget;return e!==void 0?Me(e,Hs):((i.memory_max_entries??t.memory_max_entries)!==void 0&&!hi&&(hi=!0,console.info(`Agent Fleet: \`memory_max_entries\` is deprecated and no longer enforced; memory is now bounded by \`memory_token_budget\` (default ${Hs}). Set that field to tune memory size.`)),Hs)}function pi(i,t={}){let e=s=>i[s]??t[s];return{enabled:je(e("reflection_enabled"),!1),schedule:I(e("reflection_schedule"))??Ga,recurrenceThreshold:Me(e("reflection_recurrence_threshold"),Va),proposeSkills:je(e("reflection_propose_skills"),!1),model:I(e("reflection_model"))}}function mi(i){return(0,x.normalizePath)(i.replace(/\.md$/,".permissions.json"))}function fi(i){let t=0;for(let e=0;e<i.length;e++){let s=i.charCodeAt(e);t=(t<<5)-t+s|0}return t.toString(36)}var ms=class{constructor(t,e){this.vault=t;this.settings=e}agents=new Map;skills=new Map;tasks=new Map;channels=new Map;validationIssues=new Map;channelCredentialGetter;warnedFolderAgentModelConflict=new Set;warnedLegacyPerms=new Set;migratingMemory=new Set;setChannelCredentialGetter(t){this.channelCredentialGetter=t}getVaultBasePath(){let t=this.vault.adapter;return t instanceof x.FileSystemAdapter?t.getBasePath():void 0}getFleetRoot(){return(0,x.normalizePath)(this.settings.fleetFolder)}getSubfolder(t){return(0,x.normalizePath)(`${this.getFleetRoot()}/${t}`)}async ensureFleetStructure(){let t=this.getFleetRoot(),e=!this.vault.getAbstractFileByPath(t);await this.ensureFolder(t);for(let s of za)await this.ensureFolder(this.getSubfolder(s));return e}async ensureSamples(){let t=this.getFleetRoot();for(let e of zn){let s=(0,x.normalizePath)(`${t}/${e.path}`),n=s.substring(0,s.lastIndexOf("/"));await this.ensureFolder(n),await this.createFileIfMissing(s,e.content)}}async updateDefaults(t){let e=this.getFleetRoot(),s={...t};for(let n of zn){let a=(0,x.normalizePath)(`${e}/${n.path}`),r=fi(n.content),o=t[n.path];if(o===r)continue;let c=this.vault.getAbstractFileByPath(a);if(!(c instanceof x.TFile)){let h=a.substring(0,a.lastIndexOf("/"));await this.ensureFolder(h),await this.createFileIfMissing(a,n.content),s[n.path]=r;continue}let l=await this.vault.cachedRead(c),d=fi(l);(!o||d===o)&&(await this.vault.modify(c,n.content),s[n.path]=r)}return s}async loadAll(){this.agents.clear(),this.skills.clear(),this.tasks.clear(),this.channels.clear(),this.validationIssues.clear(),await this.loadFolderAgents(),await this.loadFolderSkills();let t=this.vault.getMarkdownFiles().filter(e=>e.path.startsWith(`${this.getFleetRoot()}/`));for(let e of t)await this.loadFile(e);return this.validateReferences(),this.getSnapshot()}async loadFile(t){let e=typeof t=="string"?this.vault.getAbstractFileByPath(t):t;if(!(e instanceof x.TFile)||e.extension!=="md")return;if(this.isInsideAgentFolder(e.path)){await this.reloadFolderAgentContaining(e.path);return}if(this.isInsideSkillFolder(e.path)){await this.reloadFolderSkillContaining(e.path);return}this.clearStoredFile(e.path);let s=`${this.getSubfolder("channels")}/`;if(e.path.startsWith(s)){if(!e.path.slice(s.length).includes("/")){let o=await this.vault.cachedRead(e),c=this.parseChannelFile(e.path,o);c&&this.channels.set(e.path,c)}return}let n=await this.vault.cachedRead(e),a=this.parseFile(e.path,n);if(a)if("taskId"in a)this.tasks.set(e.path,a);else if("model"in a){if(!a.isFolder){let r=mi(e.path),o=this.vault.getAbstractFileByPath(r);if(o instanceof x.TFile)try{let c=await this.vault.cachedRead(o),l=JSON.parse(c);a.permissionRules={allow:ye(l.allow),deny:ye(l.deny)}}catch{}}this.agents.set(e.path,a)}else this.skills.set(e.path,a)}async reloadFolderAgentContaining(t){let e=`${this.getSubfolder("agents")}/`,n=t.slice(e.length).split("/")[0];if(!n)return;let a=(0,x.normalizePath)(`${e}${n}`),r=(0,x.normalizePath)(`${a}/agent.md`);if(this.agents.delete(r),!(this.vault.getAbstractFileByPath(a)instanceof x.TFolder))return;let c=this.vault.getAbstractFileByPath(r);if(!(c instanceof x.TFile))return;let l=await this.loadFolderAgent(a,c);l&&this.agents.set(r,l)}isInsideAgentFolder(t){let e=`${this.getSubfolder("agents")}/`;return t.startsWith(e)?t.slice(e.length).includes("/"):!1}isInsideSkillFolder(t){let e=`${this.getSubfolder("skills")}/`;return t.startsWith(e)?t.slice(e.length).includes("/"):!1}async reloadFolderSkillContaining(t){let e=`${this.getSubfolder("skills")}/`,n=t.slice(e.length).split("/")[0];if(!n)return;let a=(0,x.normalizePath)(`${e}${n}`),r=(0,x.normalizePath)(`${a}/skill.md`);if(this.skills.delete(r),!(this.vault.getAbstractFileByPath(a)instanceof x.TFolder))return;let c=this.vault.getAbstractFileByPath(r);if(!(c instanceof x.TFile))return;let l=await this.loadFolderSkill(a,c);l&&this.skills.set(r,l)}async loadFolderSkills(){let t=this.vault.getAbstractFileByPath(this.getSubfolder("skills"));if(t instanceof x.TFolder)for(let e of t.children){if(!(e instanceof x.TFolder))continue;let s=(0,x.normalizePath)(`${e.path}/skill.md`),n=this.vault.getAbstractFileByPath(s);if(!(n instanceof x.TFile))continue;let a=await this.loadFolderSkill(e.path,n);a&&this.skills.set(s,a)}}async loadFolderSkill(t,e){let s=await this.vault.cachedRead(e),{frontmatter:n,body:a}=Q(s),r=I(n.name);if(!r)return this.setIssue(e.path,"Folder skill skill.md requires string field `name`."),null;let o=async c=>{let l=(0,x.normalizePath)(`${t}/${c}`),d=this.vault.getAbstractFileByPath(l);if(!(d instanceof x.TFile))return"";let h=await this.vault.cachedRead(d);return Q(h).body};return{filePath:e.path,name:r,description:I(n.description),tags:ye(n.tags),body:a,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 x.TFolder)for(let e of t.children){if(!(e instanceof x.TFolder))continue;let s=(0,x.normalizePath)(`${e.path}/agent.md`),n=this.vault.getAbstractFileByPath(s);if(!(n instanceof x.TFile))continue;let a=await this.loadFolderAgent(e.path,n);a&&this.agents.set(s,a)}}async loadFolderAgent(t,e){let s=await this.vault.cachedRead(e),{frontmatter:n,body:a}=Q(s),r=I(n.name);if(!r)return this.setIssue(e.path,"Folder agent agent.md requires string field `name`."),null;let o={},c=(0,x.normalizePath)(`${t}/config.md`),l=this.vault.getAbstractFileByPath(c);if(l instanceof x.TFile){let B=await this.vault.cachedRead(l);o=Q(B).frontmatter}let d={allow:[],deny:[]},h=(0,x.normalizePath)(`${t}/permissions.json`),u=this.vault.getAbstractFileByPath(h);if(u instanceof x.TFile)try{let B=await this.vault.cachedRead(u),F=JSON.parse(B);d={allow:ye(F.allow),deny:ye(F.deny)}}catch{}if(d.allow.length===0&&d.deny.length===0){let B=ye(o.allowed_tools),F=ye(o.blocked_tools);(B.length>0||F.length>0)&&(d={allow:B,deny:F},this.warnedLegacyPerms.has(r)||(this.warnedLegacyPerms.add(r),console.warn(`Agent Fleet: "${r}" still uses legacy allowed_tools/blocked_tools in config.md. Permission rules now live in permissions.json. Open this agent in Edit and Save to migrate.`)))}let p="",m=(0,x.normalizePath)(`${t}/SKILLS.md`),f=this.vault.getAbstractFileByPath(m);if(f instanceof x.TFile){let B=await this.vault.cachedRead(f);p=Q(B).body}let y="",w=(0,x.normalizePath)(`${t}/CONTEXT.md`),k=this.vault.getAbstractFileByPath(w);if(k instanceof x.TFile){let B=await this.vault.cachedRead(k);y=Q(B).body}let g=!1,v="",S="",T=!0,_="",D=(0,x.normalizePath)(`${t}/HEARTBEAT.md`),E=this.vault.getAbstractFileByPath(D);if(E instanceof x.TFile){let B=await this.vault.cachedRead(E),F=Q(B);g=je(F.frontmatter.enabled,!1),v=I(F.frontmatter.schedule)??"",T=je(F.frontmatter.notify,!0),_=I(F.frontmatter.channel)??"",S=F.body}let C=I(n.model),L=I(o.model);C&&L&&C!==L&&(this.warnedFolderAgentModelConflict.has(r)||(this.warnedFolderAgentModelConflict.add(r),console.warn(`Agent Fleet: "${r}" has conflicting model fields \u2014 agent.md says "${C}", config.md says "${L}". config.md wins. Remove agent.md's model field or sync the values to silence this warning.`)));let R=L??C??this.settings.defaultModel;return{filePath:e.path,name:r,description:I(n.description),model:R,adapter:I(o.adapter)??"claude-code",permissionMode:I(o.permission_mode)??"bypassPermissions",effort:I(o.effort),maxRetries:Me(o.max_retries,1),skills:ye(n.skills),mcpServers:ye(n.mcp_servers),cwd:I(o.cwd)||I(n.cwd),enabled:je(n.enabled,!0),timeout:Me(o.timeout,Me(n.timeout,300)),approvalRequired:ye(o.approval_required),memory:je(o.memory,je(n.memory,!1)),memoryMaxEntries:Me(o.memory_max_entries,100),memoryTokenBudget:ui(o,n),reflection:pi(o,n),autoCompactThreshold:Me(o.auto_compact_threshold??n.auto_compact_threshold,85),tags:ye(n.tags),avatar:I(n.avatar)??"",body:a,contextBody:y,skillsBody:p,env:this.parseEnvMap(o.env),permissionRules:d,isFolder:!0,heartbeatEnabled:g,heartbeatSchedule:v,heartbeatBody:S,heartbeatNotify:T,heartbeatChannel:_,wikiKeeper:this.parseWikiKeeperConfig(o.wiki_keeper??n.wiki_keeper),wikiReferences:this.parseWikiReferences(o.wiki_references??n.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 n=s.agent;typeof n=="string"&&n.trim()&&e.push({agent:n.trim()})}return e.length>0?e:void 0}parseWikiKeeperConfig(t){if(!t||typeof t!="object")return;let e=t;return{scopeRoot:I(e.scope_root)??"",inboxPath:I(e.inbox_path)??"_sources/inbox",archivePath:I(e.archive_path)??"_sources/archive",failedPath:I(e.failed_path)??"_sources/failed",topicsRoot:I(e.topics_root)??"_topics",indexPath:I(e.index_path)??"index.md",logPath:I(e.log_path)??"log.md",watchedFolders:ye(e.watched_folders),excludePatterns:ye(e.exclude_patterns),watchedSince:I(e.watched_since)??"",fileSubstantiveAnswers:je(e.file_substantive_answers,!0),obsidianUrlScheme:je(e.obsidian_url_scheme,!0),maxTokensPerIngest:Me(e.max_tokens_per_ingest,6e4),maxTokensPerRefresh:Me(e.max_tokens_per_refresh,3e4),indexSplitThreshold:Me(e.index_split_threshold,100),dedupSimilarityThreshold:Me(e.dedup_similarity_threshold,.82),summaryStaleDays:Me(e.summary_stale_days,30),stateFile:I(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,x.normalizePath)(`${this.getSubfolder("memory")}/${se(t)}.md`)}getMemoryDir(t){return(0,x.normalizePath)(`${this.getSubfolder("memory")}/${se(t)}`)}getWorkingMemoryPath(t){return(0,x.normalizePath)(`${this.getMemoryDir(t)}/working.md`)}getRawMemoryPath(t,e){let s=e.slice(0,10);return(0,x.normalizePath)(`${this.getMemoryDir(t)}/raw/${s}.md`)}async readWorkingMemory(t){let e=this.getWorkingMemoryPath(t),s=this.vault.getAbstractFileByPath(e);if(s instanceof x.TFile){let r=await this.vault.cachedRead(s);return si(r,e,t)}let n=this.getMemoryPath(t),a=this.vault.getAbstractFileByPath(n);if(a instanceof x.TFile){let r=await this.vault.cachedRead(a),{body:o}=Q(r);return Yn(o,e,t)}return null}async writeWorkingMemory(t,e){let s=this.getWorkingMemoryPath(t);await this.ensureFolder(this.getMemoryDir(t));let n=ni(e),a=this.vault.getAbstractFileByPath(s);a instanceof x.TFile?await this.vault.modify(a,n):await this.createFileIfMissing(s,n);let r=this.getMemoryPath(t);r!==s&&this.vault.getAbstractFileByPath(r)&&await this.trashFile(r)}async migrateLegacyMemory(t){let e=this.getWorkingMemoryPath(t);if(this.vault.getAbstractFileByPath(e))return;let s=this.getMemoryPath(t),n=this.vault.getAbstractFileByPath(s);if(n instanceof x.TFile&&!this.migratingMemory.has(t)){this.migratingMemory.add(t);try{let a=await this.vault.cachedRead(n),{body:r}=Q(a),o=new Date().toISOString(),c=r.split(`
|
|
11715
|
+
`).map(h=>h.replace(/^\s*[-*]\s+/,"").trim()).filter(h=>h.length>0&&!/^#{1,6}\s/.test(h)),l=[`- ${o} [migrated] Imported ${c.length} legacy memory entr${c.length===1?"y":"ies"} (pre-v2).`,...c.map(h=>`- ${o} [migrated] ${h}`)];await this.appendRawMemory(t,l,o);let d=Yn(r,e,t,o);await this.writeWorkingMemory(t,d)}finally{this.migratingMemory.delete(t)}}}async migrateAllLegacyMemory(){for(let t of this.getSnapshot().agents)if(t.memory)try{await this.migrateLegacyMemory(t.name)}catch(e){console.warn(`Agent Fleet: legacy memory migration failed for "${t.name}"`,e)}}getPendingDir(t){return(0,x.normalizePath)(`${this.getMemoryDir(t)}/pending`)}getPendingDirAbsolutePath(t){let e=this.vault.adapter;return e instanceof x.FileSystemAdapter?(0,gi.join)(e.getBasePath(),this.getPendingDir(t)):null}async ensureMemoryDir(t){await this.ensureFolder(this.getMemoryDir(t))}async readAndClearPending(t){let e=this.vault.adapter,s=this.getPendingDir(t),n;try{if(!await e.exists(s))return[];n=(await e.list(s)).files}catch{return[]}let a=[];for(let r of n)if(r.endsWith(".json"))try{let o=await e.read(r);for(let c of o.split(`
|
|
11716
|
+
`))c.trim().length>0&&a.push(c);await e.remove(r)}catch{}return a}async readRecentRaw(t,e=2){let s=(0,x.normalizePath)(`${this.getMemoryDir(t)}/raw`),n=this.vault.getAbstractFileByPath(s);if(!(n instanceof x.TFolder))return"";let a=n.children.filter(o=>o instanceof x.TFile&&o.extension==="md").sort((o,c)=>c.name.localeCompare(o.name)).slice(0,e),r=[];for(let o of a.reverse())r.push(`### ${o.basename}
|
|
11717
|
+
${await this.vault.cachedRead(o)}`);return r.join(`
|
|
11718
|
+
|
|
11719
|
+
`)}getCandidatesPath(t){return(0,x.normalizePath)(`${this.getMemoryDir(t)}/candidates.json`)}async readCandidates(t){let e=this.getCandidatesPath(t),s=this.vault.getAbstractFileByPath(e);if(!(s instanceof x.TFile))return[];try{let n=JSON.parse(await this.vault.cachedRead(s));return Array.isArray(n)?n:[]}catch{return[]}}async writeCandidates(t,e){let s=this.getCandidatesPath(t);await this.ensureFolder(this.getMemoryDir(t));let n=JSON.stringify(e,null,2),a=this.vault.getAbstractFileByPath(s);a instanceof x.TFile?await this.vault.modify(a,n):await this.createFileIfMissing(s,n)}getProposalsDir(){return(0,x.normalizePath)(`${this.getFleetRoot()}/proposals`)}async listProposals(){let t=this.vault.getAbstractFileByPath(this.getProposalsDir());if(!(t instanceof x.TFolder))return[];let e=[];for(let s of t.children){if(!(s instanceof x.TFile)||s.extension!=="md")continue;let n=await this.readProposal(s.basename);n&&e.push(n)}return e.sort((s,n)=>n.created.localeCompare(s.created)),e}async readProposal(t){let e=(0,x.normalizePath)(`${this.getProposalsDir()}/${t}.md`),s=this.vault.getAbstractFileByPath(e);if(!(s instanceof x.TFile))return null;let{frontmatter:n,body:a}=Q(await this.vault.cachedRead(s)),r=n.type==="skill_modify"?"skill_modify":"skill_create",o=n.status==="accepted"||n.status==="rejected"?n.status:"pending";return{id:t,type:r,agent:I(n.agent)??"",status:o,created:I(n.created)??"",targetSkill:I(n.target_skill),candidate:I(n.candidate),evidence:ye(n.evidence),rationale:I(n.rationale)??"",body:a}}async writeProposal(t){await this.ensureFolder(this.getProposalsDir());let e=(0,x.normalizePath)(`${this.getProposalsDir()}/${t.id}.md`),s={id:t.id,type:t.type,agent:t.agent,status:t.status,created:t.created,target_skill:t.targetSkill||void 0,candidate:t.candidate||void 0,evidence:t.evidence.length?t.evidence:void 0,rationale:t.rationale||void 0},n=z(s,t.body||""),a=this.vault.getAbstractFileByPath(e);a instanceof x.TFile?await this.vault.modify(a,n):await this.createFileIfMissing(e,n)}async setProposalStatus(t,e){let s=await this.readProposal(t);s&&(s.status=e,await this.writeProposal(s))}async applyProposal(t){if(t.type==="skill_create"){let e=t.targetSkill||`learned-${se(t.rationale).slice(0,24)||"skill"}`,s=await this.getAvailablePath(this.getSubfolder("skills"),se(e)),a={name:s.split("/").pop()?.replace(/\.md$/,"")||se(e),description:t.rationale||`Auto-proposed from recurring pattern for ${t.agent}.`,tags:["proposed"]};return await this.vault.create(s,z(a,t.body||"Skill instructions go here.")),s}if(t.targetSkill){let e=this.getSkillByName(t.targetSkill);if(e){let s=this.vault.getAbstractFileByPath(e.filePath);if(s instanceof x.TFile){let n=await this.vault.cachedRead(s),a=`
|
|
11720
|
+
|
|
11721
|
+
## Proposed update (${new Date().toISOString().slice(0,10)})
|
|
11722
|
+
${t.body}`;return await this.vault.modify(s,`${n.trimEnd()}${a}
|
|
11723
|
+
`),e.filePath}}}return null}async deleteProposal(t){await this.trashFile((0,x.normalizePath)(`${this.getProposalsDir()}/${t}.md`))}async appendRawMemory(t,e,s){if(e.length===0)return;let n=this.getRawMemoryPath(t,s);await this.ensureFolder(n.replace(/\/[^/]+$/,""));let a=e.map(o=>o.trimEnd()).join(`
|
|
11724
|
+
`),r=this.vault.getAbstractFileByPath(n);if(r instanceof x.TFile){let o=await this.vault.cachedRead(r);await this.vault.modify(r,`${o.trimEnd()}
|
|
11725
|
+
${a}
|
|
11726
|
+
`)}else await this.createFileIfMissing(n,`${a}
|
|
11727
|
+
`)}getConversationsDir(t){if(t.isFolder){let s=t.filePath.replace(/\/agent\.md$/,"");return(0,x.normalizePath)(`${s}/conversations`)}let e=this.getMemoryPath(t.name).replace(/\/[^/]+$/,"");return(0,x.normalizePath)(`${e}/${t.name}-conversations`)}getConversationPath(t,e){let s=this.getConversationsDir(t),n=se(e)||"conversation";return(0,x.normalizePath)(`${s}/${n}.json`)}async listConversations(t){let e=[],s=this.getConversationsDir(t),n=this.vault.getAbstractFileByPath(s);if(n instanceof x.TFolder)for(let a of n.children){if(!(a instanceof x.TFile)||a.extension!=="json")continue;let r=a.basename,o=await this.readConversationMeta(a,r);o&&e.push(o)}return e.sort((a,r)=>r.lastActive.localeCompare(a.lastActive)),e}async readConversationMeta(t,e){try{let s=await this.vault.cachedRead(t),n=JSON.parse(s);return{id:e,name:n.name?.trim()||"Untitled",lastActive:n.lastActive??new Date(t.stat.mtime).toISOString(),messageCount:Array.isArray(n.messages)?n.messages.length:0}}catch{return null}}async createConversation(t,e,s){let n=this.getConversationPath(t,e);if(this.vault.getAbstractFileByPath(n)instanceof x.TFile)return;let r=n.replace(/\/[^/]+$/,"");await this.ensureFolder(r);let o=new Date().toISOString(),c={sessionId:null,messages:[],lastActive:o,createdAt:o,name:s.trim()};await this.vault.create(n,JSON.stringify(c,null,2))}async renameConversation(t,e,s){let n=this.getConversationPath(t,e),a=this.vault.getAbstractFileByPath(n);if(!(a instanceof x.TFile))return;let r=await this.vault.cachedRead(a),o;try{o=JSON.parse(r)}catch{return}o.name=s.trim(),await this.vault.modify(a,JSON.stringify(o,null,2))}async deleteConversation(t,e){let s=this.getConversationPath(t,e),n=this.vault.getAbstractFileByPath(s);n instanceof x.TFile&&await this.vault.delete(n);let a=s.replace(/\.json$/,".threads"),r=this.vault.getAbstractFileByPath(a);r instanceof x.TFolder&&await this.vault.delete(r,!0);let o=this.getConversationsDir(t),c=this.vault.getAbstractFileByPath(o);c instanceof x.TFolder&&c.children.length===0&&await this.vault.delete(c,!0)}async getMemory(t){let e=await this.readWorkingMemory(t);return e?{filePath:e.filePath,agent:e.agent,lastUpdated:e.lastUpdated,body:Je(e.sections)}:null}async appendMemory(t,e){if(e.length===0)return;let s=this.getMemoryPath(t),n=this.vault.getAbstractFileByPath(s),a=new Date().toISOString(),r=e.map(o=>`- ${o.trim()}`).join(`
|
|
11728
|
+
`);if(n instanceof x.TFile){let c=`${(await this.getMemory(t))?.body.trim()||"## Learned Context"}
|
|
11729
|
+
|
|
11730
|
+
${r}`.trim();await this.vault.modify(n,z({agent:t,last_updated:a},c));return}await this.createFileIfMissing(s,z({agent:t,last_updated:a},`## Learned Context
|
|
11731
|
+
|
|
11732
|
+
${r}`))}async listRecentRuns(t=50){let e=this.vault.getAbstractFileByPath(this.getRunsRoot());if(!(e instanceof x.TFolder))return[];let s=[];this.collectMarkdownChildren(e,s),s.sort((r,o)=>o.path.localeCompare(r.path));let n=s.slice(0,t),a=[];for(let r of n){let o=await this.readRunLog(r);o&&a.push(o)}return a.sort((r,o)=>o.started.localeCompare(r.started))}async listRunsSince(t){let e=this.vault.getAbstractFileByPath(this.getRunsRoot());if(!(e instanceof x.TFolder))return[];let s=`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}`,n=[];for(let r of e.children)r instanceof x.TFolder&&(r.name<s||this.collectMarkdownChildren(r,n));let a=[];for(let r of n){let o=await this.readRunLog(r);o&&a.push(o)}return a.sort((r,o)=>o.started.localeCompare(r.started))}async readRunLog(t){let e=await this.vault.cachedRead(t),{frontmatter:s,body:n}=Q(e),a=n.match(/## Prompt\n([\s\S]*?)(?:\n## Result\n|\n## Output\n|$)/),r=n.match(/## Result\n([\s\S]*?)(?:\n## Output\n|$)/),o=n.match(/## Output\n([\s\S]*?)(?:\n## Tools Used\n|$)/),c=n.match(/## Tools Used\n([\s\S]*?)(?:\n## STDERR\n|$)/);return{filePath:t.path,runId:I(s.run_id)??t.basename,agent:I(s.agent)??"unknown",task:I(s.task)??"unknown",status:I(s.status)??"failure",started:I(s.started)??new Date(t.stat.ctime).toISOString(),completed:I(s.completed),durationSeconds:Me(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:I(s.model)??dt.defaultModel,modelSource:(()=>{let l=I(s.model_source);if(l==="task"||l==="agent"||l==="settings"||l==="cli-default")return l})(),concreteModel:I(s.resolved_concrete_model),exitCode:typeof s.exit_code=="number"?s.exit_code:null,tags:ye(s.tags),prompt:a?.[1]?.trim()??"",output:o?.[1]?.trim()??"",finalResult:r?.[1]?.trim()||void 0,toolsUsed:c?.[1]?oe(c[1]).map(l=>l.replace(/^- /,"").trim()).filter(Boolean):[],approvals:this.parseApprovals(s.approvals)}}async writeRunLog(t){let e=new Date(t.started),s=(0,x.normalizePath)(`${this.getRunsRoot()}/${e.toISOString().slice(0,10)}`);await this.ensureFolder(s);let n=`${e.toISOString().slice(11,19).replace(/:/g,"")}-${se(t.agent)}-${se(t.task)}.md`,a=(0,x.normalizePath)(`${s}/${n}`),r=z({run_id:t.runId,agent:t.agent,task:t.task,status:t.status,started:t.started,completed:t.completed,duration_seconds:t.durationSeconds,tokens_used:t.tokensUsed,cost_usd:t.costUsd,model:t.model,model_source:t.modelSource,resolved_concrete_model:t.concreteModel,exit_code:t.exitCode,tags:t.tags,approvals:t.approvals},["## Prompt","",t.prompt.trim(),"",...t.finalResult&&t.finalResult.trim()?["## Result","",t.finalResult.trim(),""]:[],"## Output","",t.output.trim()||"(no output)","","## Tools Used","",...t.toolsUsed.length>0?t.toolsUsed.map(c=>`- ${c}`):["- none"],...t.stderr?["","## STDERR","",t.stderr.trim()]:[]].join(`
|
|
11733
|
+
`)),o=this.vault.getAbstractFileByPath(a);return o instanceof x.TFile?await this.vault.modify(o,r):await this.vault.create(a,r),a}async updateTaskRunMetadata(t,e){let s=this.vault.getAbstractFileByPath(t.filePath);if(!(s instanceof x.TFile))return;let n=await this.vault.cachedRead(s),{frontmatter:a,body:r}=Q(n),o={...a,last_run:e.lastRun??t.lastRun,next_run:e.nextRun??t.nextRun,run_count:e.runCount??t.runCount};await this.vault.modify(s,z(o,r)),await this.loadFile(s)}async setApprovalDecision(t,e,s){let n=this.vault.getAbstractFileByPath(t);if(!(n instanceof x.TFile))return;let a=await this.vault.cachedRead(n),{frontmatter:r,body:o}=Q(a),c=(this.parseApprovals(r.approvals)??[]).map(l=>l.tool===e?{...l,status:s,resolvedAt:new Date().toISOString()}:l);await this.vault.modify(n,z({...r,approvals:c},o))}async createAgentTemplate(t){let e=await this.getAvailablePath(this.getSubfolder("agents"),se(t)),s=`---
|
|
11734
|
+
name: ${se(t)}
|
|
11675
11735
|
description:
|
|
11676
11736
|
enabled: true
|
|
11677
11737
|
skills: []
|
|
@@ -11679,35 +11739,35 @@ tags: []
|
|
|
11679
11739
|
---
|
|
11680
11740
|
|
|
11681
11741
|
Agent instructions go here.
|
|
11682
|
-
`;return await this.vault.create(e,s)}async createAgentFolder(t){let e=
|
|
11683
|
-
`)}return
|
|
11684
|
-
name: ${
|
|
11742
|
+
`;return await this.vault.create(e,s)}async createAgentFolder(t){let e=se(t.name),s=(0,x.normalizePath)(`${this.getSubfolder("agents")}/${e}`);await this.ensureFolder(s);let n={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"&&(n.model=t.model);let a=(0,x.normalizePath)(`${s}/agent.md`);await this.vault.create(a,z(n,t.systemPrompt||""));let r={model:t.model||"default",adapter:t.adapter||"claude-code",timeout:t.timeout,max_retries:1,cwd:t.cwd||"",permission_mode:t.permissionMode||"bypassPermissions",effort:t.effort||void 0,approval_required:t.approvalRequired,memory:t.memory,memory_max_entries:t.memoryMaxEntries};typeof t.autoCompactThreshold=="number"&&(r.auto_compact_threshold=t.autoCompactThreshold),t.wikiReferences&&t.wikiReferences.length>0&&(r.wiki_references=t.wikiReferences.map(h=>({agent:h})));let o=(0,x.normalizePath)(`${s}/config.md`);await this.vault.create(o,z(r,""));let c=(0,x.normalizePath)(`${s}/SKILLS.md`);await this.vault.create(c,z({},t.skillsBody||""));let l=(0,x.normalizePath)(`${s}/CONTEXT.md`);await this.vault.create(l,z({},t.contextBody||""));let d=t.permissionRules;if(d&&(d.allow.length>0||d.deny.length>0)){let h=(0,x.normalizePath)(`${s}/permissions.json`);await this.vault.create(h,JSON.stringify(d,null,2)+`
|
|
11743
|
+
`)}return a}async createSkillTemplate(t){let e=await this.getAvailablePath(this.getSubfolder("skills"),se(t)),s=`---
|
|
11744
|
+
name: ${se(t)}
|
|
11685
11745
|
description:
|
|
11686
11746
|
tags: []
|
|
11687
11747
|
---
|
|
11688
11748
|
|
|
11689
11749
|
Skill instructions go here.
|
|
11690
|
-
`;return await this.vault.create(e,s)}async createSkillFolder(t){let e=(0,
|
|
11750
|
+
`;return await this.vault.create(e,s)}async createSkillFolder(t){let e=(0,x.normalizePath)(`${this.getSubfolder("skills")}/${se(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},n=(0,x.normalizePath)(`${e}/skill.md`);if(await this.createFileIfMissing(n,z(s,t.body||"Skill instructions go here.")),t.toolsBody){let a=(0,x.normalizePath)(`${e}/tools.md`);await this.createFileIfMissing(a,`# Tools
|
|
11691
11751
|
|
|
11692
|
-
${t.toolsBody}`)}if(t.referencesBody){let
|
|
11752
|
+
${t.toolsBody}`)}if(t.referencesBody){let a=(0,x.normalizePath)(`${e}/references.md`);await this.createFileIfMissing(a,`# References
|
|
11693
11753
|
|
|
11694
|
-
${t.referencesBody}`)}if(t.examplesBody){let
|
|
11754
|
+
${t.referencesBody}`)}if(t.examplesBody){let a=(0,x.normalizePath)(`${e}/examples.md`);await this.createFileIfMissing(a,`# Examples
|
|
11695
11755
|
|
|
11696
|
-
${t.examplesBody}`)}}async updateAgent(t,e){let s=this.getAgentByName(t);if(s)if(s.isFolder){let
|
|
11697
|
-
`;
|
|
11698
|
-
`;d instanceof
|
|
11756
|
+
${t.examplesBody}`)}}async updateAgent(t,e){let s=this.getAgentByName(t);if(s)if(s.isFolder){let n=(0,x.normalizePath)(s.filePath.replace(/\/agent\.md$/,"")),a=this.vault.getAbstractFileByPath(s.filePath);if(a instanceof x.TFile){let c=await this.vault.cachedRead(a),{frontmatter:l,body:d}=Q(c);e.description!==void 0&&(l.description=e.description||void 0),e.avatar!==void 0&&(l.avatar=e.avatar||void 0),e.tags!==void 0&&(l.tags=e.tags),e.skills!==void 0&&(l.skills=e.skills),e.mcpServers!==void 0&&(l.mcp_servers=e.mcpServers.length>0?e.mcpServers:void 0),e.enabled!==void 0&&(l.enabled=e.enabled),e.model!==void 0&&e.model!=="default"&&(l.model=e.model);let h=e.systemPrompt!==void 0?e.systemPrompt:d;await this.vault.modify(a,z(l,h))}let r=(0,x.normalizePath)(`${n}/config.md`),o=this.vault.getAbstractFileByPath(r);if(o instanceof x.TFile){let c=await this.vault.cachedRead(o),{frontmatter:l,body:d}=Q(c);e.model!==void 0&&(l.model=e.model),e.adapter!==void 0&&(l.adapter=e.adapter),e.timeout!==void 0&&(l.timeout=e.timeout),e.cwd!==void 0&&(l.cwd=e.cwd),e.permissionMode!==void 0&&(l.permission_mode=e.permissionMode),e.effort!==void 0&&(l.effort=e.effort||void 0),e.approvalRequired!==void 0&&(l.approval_required=e.approvalRequired),e.memory!==void 0&&(l.memory=e.memory),e.memoryTokenBudget!==void 0&&(l.memory_token_budget=e.memoryTokenBudget),e.reflectionEnabled!==void 0&&(l.reflection_enabled=e.reflectionEnabled),e.reflectionSchedule!==void 0&&(l.reflection_schedule=e.reflectionSchedule),e.reflectionProposeSkills!==void 0&&(l.reflection_propose_skills=e.reflectionProposeSkills),e.autoCompactThreshold!==void 0&&(l.auto_compact_threshold=e.autoCompactThreshold),e.wikiReferences!==void 0&&(l.wiki_references=e.wikiReferences.length>0?e.wikiReferences.map(h=>({agent:h})):void 0),delete l.allowed_tools,delete l.blocked_tools,await this.vault.modify(o,z(l,d))}if(e.skillsBody!==void 0){let c=(0,x.normalizePath)(`${n}/SKILLS.md`),l=this.vault.getAbstractFileByPath(c);l instanceof x.TFile?await this.vault.modify(l,z({},e.skillsBody)):await this.vault.create(c,z({},e.skillsBody))}if(e.contextBody!==void 0){let c=(0,x.normalizePath)(`${n}/CONTEXT.md`),l=this.vault.getAbstractFileByPath(c);l instanceof x.TFile?await this.vault.modify(l,z({},e.contextBody)):await this.vault.create(c,z({},e.contextBody))}if(e.permissionRules!==void 0){let c=(0,x.normalizePath)(`${n}/permissions.json`),l=this.vault.getAbstractFileByPath(c),d=e.permissionRules;if(d.allow.length>0||d.deny.length>0){let h=JSON.stringify(d,null,2)+`
|
|
11757
|
+
`;l instanceof x.TFile?await this.vault.modify(l,h):await this.vault.create(c,h)}else l instanceof x.TFile&&await this.vault.trash(l,!0)}}else{let n=this.vault.getAbstractFileByPath(s.filePath);if(!(n instanceof x.TFile))return;let a=await this.vault.cachedRead(n),{frontmatter:r,body:o}=Q(a);e.description!==void 0&&(r.description=e.description||void 0),e.avatar!==void 0&&(r.avatar=e.avatar||void 0),e.tags!==void 0&&(r.tags=e.tags),e.skills!==void 0&&(r.skills=e.skills),e.mcpServers!==void 0&&(r.mcp_servers=e.mcpServers.length>0?e.mcpServers:void 0),e.enabled!==void 0&&(r.enabled=e.enabled),e.model!==void 0&&(r.model=e.model),e.adapter!==void 0&&(r.adapter=e.adapter),e.timeout!==void 0&&(r.timeout=e.timeout),e.cwd!==void 0&&(r.cwd=e.cwd),e.permissionMode!==void 0&&(r.permission_mode=e.permissionMode),e.effort!==void 0&&(r.effort=e.effort||void 0),e.approvalRequired!==void 0&&(r.approval_required=e.approvalRequired),e.memory!==void 0&&(r.memory=e.memory),e.autoCompactThreshold!==void 0&&(r.auto_compact_threshold=e.autoCompactThreshold),e.wikiReferences!==void 0&&(r.wiki_references=e.wikiReferences.length>0?e.wikiReferences.map(l=>({agent:l})):void 0),delete r.allowed_tools,delete r.blocked_tools;let c=e.systemPrompt!==void 0?e.systemPrompt:o;if(await this.vault.modify(n,z(r,c)),e.permissionRules!==void 0){let l=mi(s.filePath),d=this.vault.getAbstractFileByPath(l),h=e.permissionRules;if(h.allow.length>0||h.deny.length>0){let u=JSON.stringify(h,null,2)+`
|
|
11758
|
+
`;d instanceof x.TFile?await this.vault.modify(d,u):await this.vault.create(l,u)}else d instanceof x.TFile&&await this.vault.trash(d,!0)}}}async updateTask(t,e){let s=this.getTaskById(t);if(!s)return;let n=this.vault.getAbstractFileByPath(s.filePath);if(!(n instanceof x.TFile))return;let a=await this.vault.cachedRead(n),{frontmatter:r,body:o}=Q(a);e.agent!==void 0&&(r.agent=e.agent),e.type!==void 0&&(r.type=e.type),e.schedule!==void 0&&(r.schedule=e.schedule||void 0),e.runAt!==void 0&&(r.run_at=e.runAt||void 0),e.enabled!==void 0&&(r.enabled=e.enabled),e.priority!==void 0&&(r.priority=e.priority),e.catch_up!==void 0&&(r.catch_up=e.catch_up),e.effort!==void 0&&(r.effort=e.effort||void 0),e.model!==void 0&&(r.model=e.model||void 0),e.tags!==void 0&&(r.tags=e.tags);let c=e.body!==void 0?e.body:o;await this.vault.modify(n,z(r,c))}async updateSkill(t,e){let s=this.getSkillByName(t);if(s)if(s.isFolder){let n=(0,x.normalizePath)(s.filePath.replace(/\/skill\.md$/,"")),a=this.vault.getAbstractFileByPath(s.filePath);if(a instanceof x.TFile){let r=await this.vault.cachedRead(a),{frontmatter:o,body:c}=Q(r);e.description!==void 0&&(o.description=e.description||void 0),e.tags!==void 0&&(o.tags=e.tags.length>0?e.tags:void 0);let l=e.body!==void 0?e.body:c;await this.vault.modify(a,z(o,l))}if(e.toolsBody!==void 0){let r=(0,x.normalizePath)(`${n}/tools.md`),o=this.vault.getAbstractFileByPath(r);e.toolsBody&&(o instanceof x.TFile?await this.vault.modify(o,`# Tools
|
|
11699
11759
|
|
|
11700
|
-
${e.toolsBody}`):await this.vault.create(
|
|
11760
|
+
${e.toolsBody}`):await this.vault.create(r,`# Tools
|
|
11701
11761
|
|
|
11702
|
-
${e.toolsBody}`))}if(e.referencesBody!==void 0){let
|
|
11762
|
+
${e.toolsBody}`))}if(e.referencesBody!==void 0){let r=(0,x.normalizePath)(`${n}/references.md`),o=this.vault.getAbstractFileByPath(r);e.referencesBody&&(o instanceof x.TFile?await this.vault.modify(o,`# References
|
|
11703
11763
|
|
|
11704
|
-
${e.referencesBody}`):await this.vault.create(
|
|
11764
|
+
${e.referencesBody}`):await this.vault.create(r,`# References
|
|
11705
11765
|
|
|
11706
|
-
${e.referencesBody}`))}if(e.examplesBody!==void 0){let
|
|
11766
|
+
${e.referencesBody}`))}if(e.examplesBody!==void 0){let r=(0,x.normalizePath)(`${n}/examples.md`),o=this.vault.getAbstractFileByPath(r);e.examplesBody&&(o instanceof x.TFile?await this.vault.modify(o,`# Examples
|
|
11707
11767
|
|
|
11708
|
-
${e.examplesBody}`):await this.vault.create(
|
|
11768
|
+
${e.examplesBody}`):await this.vault.create(r,`# Examples
|
|
11709
11769
|
|
|
11710
|
-
${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}=ae(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,K(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}=ae(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,K(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")}/${xe(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}=ae(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,K(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,K(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}=ae(e);if(!ks(s))return this.setIssue(t,"Invalid frontmatter."),null;let n=M(s.name),i=M(s.model)??this.settings.defaultModel;if(!n||!i)return this.setIssue(t,"Agent requires string field `name` and a valid model or default model setting."),null;let o=ye(s.allowed_tools),l=ye(s.blocked_tools);return(o.length>0||l.length>0)&&!this.warnedLegacyPerms.has(n)&&(this.warnedLegacyPerms.add(n),console.warn(`Agent Fleet: "${n}" still uses legacy allowed_tools/blocked_tools in its frontmatter. Permission rules now live in <name>.permissions.json beside the .md. Open Edit and Save to migrate.`)),{filePath:t,name:n,description:M(s.description),model:i,adapter:M(s.adapter)??"claude-code",permissionMode:M(s.permission_mode)??"bypassPermissions",effort:M(s.effort),maxRetries:$e(s.max_retries,1),skills:ye(s.skills),mcpServers:ye(s.mcp_servers),cwd:M(s.cwd),enabled:Ke(s.enabled,!0),timeout:$e(s.timeout,300),approvalRequired:ye(s.approval_required),memory:Ke(s.memory,!1),memoryMaxEntries:$e(s.memory_max_entries,100),autoCompactThreshold:$e(s.auto_compact_threshold,85),tags:ye(s.tags),avatar:M(s.avatar)??"",body:a,contextBody:"",skillsBody:"",env:this.parseEnvMap(s.env),permissionRules:{allow:o,deny:l},isFolder:!1,heartbeatEnabled:!1,heartbeatSchedule:"",heartbeatBody:"",heartbeatNotify:!0,heartbeatChannel:""}}parseSkill(t,e){let{frontmatter:s,body:a}=ae(e),n=M(s.name);return n?{filePath:t,name:n,description:M(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}=ae(e),n=M(s.task_id),i=M(s.agent),o=M(s.type);if(!n||!i||!o)return this.setIssue(t,"Task requires `task_id`, `agent`, and `type`."),null;if(o==="recurring"&&!M(s.schedule))return this.setIssue(t,"Recurring task requires `schedule`."),null;if(o==="once"&&!M(s.run_at))return this.setIssue(t,"One-time task requires `run_at`."),null;let l=M(s.priority),d=l&&["low","medium","high","critical"].includes(l)?l:"medium";return{filePath:t,taskId:n,agent:i,schedule:M(s.schedule),runAt:M(s.run_at),type:o,priority:d,enabled:Ke(s.enabled,!0),created:M(s.created)??new Date().toISOString(),lastRun:M(s.last_run),nextRun:M(s.next_run),runCount:$e(s.run_count,0),catchUp:Ke(s.catch_up,this.settings.catchUpMissedTasks),effort:M(s.effort),model:M(s.model),tags:ye(s.tags),body:a}}parseChannelFile(t,e){let{frontmatter:s,body:a}=ae(e),n=M(s.name);if(!n)return this.setIssue(t,"Channel requires string field `name`."),null;let i=M(s.type),o=["slack","telegram"];if(!i||!o.includes(i))return this.setIssue(t,`Channel \`${n}\` requires \`type\` to be one of: ${o.join(", ")}.`),null;let l=i,c=M(s.default_agent)??M(s.agent);if(!c)return this.setIssue(t,`Channel \`${n}\` requires \`default_agent\` (or \`agent\`).`),null;let d=ye(s.allowed_agents),h=M(s.credential_ref);if(!h)return this.setIssue(t,`Channel \`${n}\` requires \`credential_ref\` pointing at a configured credential.`),null;let u=ks(s.transport)?s.transport:{};return{filePath:t,name:n,type:l,defaultAgent:c,allowedAgents:d,enabled:Ke(s.enabled,!0),credentialRef:h,allowedUsers:ye(s.allowed_users),perUserSessions:Ke(s.per_user_sessions,!0),channelContext:M(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(!ks(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(!ks(e)||!M(e.tool))return[];let s=M(e.tool);return s?[{tool:s,command:M(e.command),reason:M(e.reason),status:M(e.status)??"pending",resolvedAt:M(e.resolvedAt),note:M(e.note)}]:[]})}};var Ct=require("obsidian"),xs=class extends Ct.Modal{constructor(e,s,a){super(e);this.info=s;this.onConfirm=a}deleteTasks=!0;onOpen(){let{contentEl:e}=this;e.empty(),e.addClass("af-confirm-delete-modal");let s=e.createDiv({cls:"af-delete-header"}),a=s.createSpan({cls:"af-delete-header-icon"});(0,Ct.setIcon)(a,"alert-triangle"),s.createEl("h3",{text:`Delete agent "${this.info.agentName}"?`});let n=e.createDiv({cls:"af-delete-summary"});n.createDiv({text:"This action will:"});let i=n.createEl("ul",{cls:"af-delete-impact-list"});i.createEl("li",{text:"Move the agent definition to trash"}),this.info.hasMemory&&i.createEl("li",{text:"Move the agent's memory file to trash"}),this.info.taskCount>0&&i.createEl("li").setText(`${this.info.taskCount} task${this.info.taskCount!==1?"s":""} reference this agent`),this.info.runCount>0&&i.createEl("li",{cls:"af-delete-preserved"}).setText(`${this.info.runCount} run log${this.info.runCount!==1?"s":""} will be preserved`),e.createDiv({cls:"af-delete-note",text:"Files are moved to your system trash and can be recovered."}),this.info.taskCount>0&&new Ct.Setting(e).setName("Also delete associated tasks").setDesc(`Delete ${this.info.taskCount} task${this.info.taskCount!==1?"s":""} that reference this agent`).addToggle(h=>{h.setValue(this.deleteTasks),h.onChange(u=>{this.deleteTasks=u})});let o=e.createDiv({cls:"af-delete-actions"}),l=o.createEl("button",{text:"Cancel"});l.onclick=()=>this.close();let c=o.createEl("button",{cls:"af-delete-confirm-btn",text:"Delete Agent"}),d=c.createSpan({cls:"af-delete-btn-icon"});(0,Ct.setIcon)(d,"trash-2"),c.onclick=()=>{this.onConfirm(this.deleteTasks),this.close()}}};var B=require("obsidian");var on=[{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"}],Ss="__custom__";function ir(r){let t=r.trim();return!t||t==="default"||t==="subscription"?"inherit":on.some(e=>e.value===t)?"alias":"custom"}function _t(r,t){r.empty(),r.addClass("af-model-picker");let e=ir(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 on)n.createEl("option",{text:o.label,attr:{value:o.value}});s.createEl("option",{text:"Custom\u2026",attr:{value:Ss}});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=Ss,i.value=t.value.trim(),i.style.display=""),s.addEventListener("change",()=>{s.value===Ss?(i.style.display="",i.focus(),t.onChange(i.value.trim())):(i.style.display="none",t.onChange(s.value))}),i.addEventListener("input",()=>{s.value===Ss&&t.onChange(i.value.trim())})}var oe=require("obsidian");function rr(){let r=new Date,t=r.getFullYear(),e=String(r.getMonth()+1).padStart(2,"0"),s=String(r.getDate()).padStart(2,"0");return`${t}-${e}-${s}`}var or="0 3 * * *",lr="0 9 * * 0";function pa(){return{scopeSlug:"",scopeRoot:"",inboxPath:"_sources/inbox",archivePath:"_sources/archive",topicsRoot:"_topics",indexPath:"index.md",logPath:"log.md",watchedFolders:[],excludePatterns:[],watchedSince:rr(),heartbeatChannel:"",fileSubstantiveAnswers:!0,obsidianUrlScheme:!0,maxTokensPerIngest:6e4,maxTokensPerRefresh:3e4,indexSplitThreshold:100,dedupSimilarityThreshold:.82,summaryStaleDays:30,failedPath:"_sources/failed",stateFile:".wiki-keeper-state.json"}}var dn=pa();function ma(r){let t=xe(r).trim();return t?`wiki-keeper-${t}`:"wiki-keeper"}function cr(r,t){let e=t||"the whole vault",s={name:r,description:`Cultivates ${e} as a self-maintaining wiki. Ingests new sources, extracts durable claims from watched folders, answers questions with citations, and periodically lints.`,avatar:"library",enabled:!0,skills:["wiki-ingest","wiki-query","wiki-lint","wiki-refresh"],tags:t?["wiki-keeper",`scope:${xe(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).
|
|
11770
|
+
${e.examplesBody}`))}}else{let n=this.vault.getAbstractFileByPath(s.filePath);if(!(n instanceof x.TFile))return;let a=await this.vault.cachedRead(n),{frontmatter:r,body:o}=Q(a);e.description!==void 0&&(r.description=e.description||void 0),e.tags!==void 0&&(r.tags=e.tags.length>0?e.tags:void 0);let c=e.body!==void 0?e.body:o;await this.vault.modify(n,z(r,c))}}async deleteSkill(t){let e=this.getSkillByName(t);if(e)if(e.isFolder){let s=(0,x.normalizePath)(e.filePath.replace(/\/skill\.md$/,"")),n=this.vault.getAbstractFileByPath(s);n instanceof x.TFolder&&await this.vault.trash(n,!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 n=this.vault.getAbstractFileByPath(s.filePath);if(!(n instanceof x.TFile))return;let a=await this.vault.cachedRead(n),{frontmatter:r,body:o}=Q(a);e.default_agent!==void 0&&(r.default_agent=e.default_agent,delete r.agent),e.allowed_agents!==void 0&&(r.allowed_agents=e.allowed_agents),e.enabled!==void 0&&(r.enabled=e.enabled),e.credential_ref!==void 0&&(r.credential_ref=e.credential_ref),e.allowed_users!==void 0&&(r.allowed_users=e.allowed_users),e.per_user_sessions!==void 0&&(r.per_user_sessions=e.per_user_sessions),e.channel_context!==void 0&&(r.channel_context=e.channel_context||void 0),e.tags!==void 0&&(r.tags=e.tags),e.type!==void 0&&(r.type=e.type),e.transport!==void 0&&(r.transport=e.transport);let c=e.body!==void 0?e.body:o;await this.vault.modify(n,z(r,c))}async deleteChannel(t){let e=this.getChannelByName(t);if(!e)return;await this.trashFile(e.filePath);let s=(0,x.normalizePath)(`${this.getSubfolder("channels")}/${se(t)}/sessions`),n=this.vault.getAbstractFileByPath(s);n instanceof x.TFolder&&await this.vault.trash(n,!0)}async updateHeartbeat(t,e){let s=this.getAgentByName(t);if(!s||!s.isFolder)return;let n=(0,x.normalizePath)(s.filePath.replace(/\/agent\.md$/,"")),a=(0,x.normalizePath)(`${n}/HEARTBEAT.md`),r=this.vault.getAbstractFileByPath(a);if(r instanceof x.TFile){let o=await this.vault.cachedRead(r),{frontmatter:c,body:l}=Q(o);e.enabled!==void 0&&(c.enabled=e.enabled),e.schedule!==void 0&&(c.schedule=e.schedule||void 0),e.notify!==void 0&&(c.notify=e.notify),e.channel!==void 0&&(c.channel=e.channel||void 0);let d=e.body!==void 0?e.body:l;await this.vault.modify(r,z(c,d))}else{let o={enabled:e.enabled??!1};e.schedule&&(o.schedule=e.schedule),e.notify!==void 0&&(o.notify=e.notify),e.channel&&(o.channel=e.channel);let c=e.body??"";await this.vault.create(a,z(o,c))}}async deleteAgent(t,e){let s=[],n=this.getAgentByName(t);if(!n)return{trashedFiles:s};if(n.isFolder){let c=(0,x.normalizePath)(n.filePath.replace(/\/agent\.md$/,"")),l=this.vault.getAbstractFileByPath(c);if(l instanceof x.TFolder){let d=[];this.collectMarkdownChildren(l,d);for(let h of d)s.push(h.path);await this.vault.trash(l,!0)}}else await this.trashFile(n.filePath),s.push(n.filePath);let a=this.getMemoryPath(t);this.vault.getAbstractFileByPath(a)&&(await this.trashFile(a),s.push(a));let r=this.getMemoryDir(t),o=this.vault.getAbstractFileByPath(r);if(o instanceof x.TFolder&&(await this.vault.trash(o,!0),s.push(r)),e){let c=this.getTasksForAgent(t);for(let l of c)await this.trashFile(l.filePath),s.push(l.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 n=s===0?"":`-${s+1}`,a=(0,x.normalizePath)(`${t}/${e}${n}.md`);if(!this.vault.getAbstractFileByPath(a))return a;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 x.TFile&&s.extension==="md"&&e.push(s),s instanceof x.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:n}=Q(e);if(!Js(s))return this.setIssue(t,"Invalid frontmatter."),null;let a=I(s.name),r=I(s.model)??this.settings.defaultModel;if(!a||!r)return this.setIssue(t,"Agent requires string field `name` and a valid model or default model setting."),null;let o=ye(s.allowed_tools),c=ye(s.blocked_tools);return(o.length>0||c.length>0)&&!this.warnedLegacyPerms.has(a)&&(this.warnedLegacyPerms.add(a),console.warn(`Agent Fleet: "${a}" still uses legacy allowed_tools/blocked_tools in its frontmatter. Permission rules now live in <name>.permissions.json beside the .md. Open Edit and Save to migrate.`)),{filePath:t,name:a,description:I(s.description),model:r,adapter:I(s.adapter)??"claude-code",permissionMode:I(s.permission_mode)??"bypassPermissions",effort:I(s.effort),maxRetries:Me(s.max_retries,1),skills:ye(s.skills),mcpServers:ye(s.mcp_servers),cwd:I(s.cwd),enabled:je(s.enabled,!0),timeout:Me(s.timeout,300),approvalRequired:ye(s.approval_required),memory:je(s.memory,!1),memoryMaxEntries:Me(s.memory_max_entries,100),memoryTokenBudget:ui(s),reflection:pi(s),autoCompactThreshold:Me(s.auto_compact_threshold,85),tags:ye(s.tags),avatar:I(s.avatar)??"",body:n,contextBody:"",skillsBody:"",env:this.parseEnvMap(s.env),permissionRules:{allow:o,deny:c},isFolder:!1,heartbeatEnabled:!1,heartbeatSchedule:"",heartbeatBody:"",heartbeatNotify:!0,heartbeatChannel:""}}parseSkill(t,e){let{frontmatter:s,body:n}=Q(e),a=I(s.name);return a?{filePath:t,name:a,description:I(s.description),tags:ye(s.tags),body:n,toolsBody:"",referencesBody:"",examplesBody:"",isFolder:!1}:(this.setIssue(t,"Skill requires string field `name`."),null)}parseTask(t,e){let{frontmatter:s,body:n}=Q(e),a=I(s.task_id),r=I(s.agent),o=I(s.type);if(!a||!r||!o)return this.setIssue(t,"Task requires `task_id`, `agent`, and `type`."),null;if(o==="recurring"&&!I(s.schedule))return this.setIssue(t,"Recurring task requires `schedule`."),null;if(o==="once"&&!I(s.run_at))return this.setIssue(t,"One-time task requires `run_at`."),null;let c=I(s.priority),d=c&&["low","medium","high","critical"].includes(c)?c:"medium";return{filePath:t,taskId:a,agent:r,schedule:I(s.schedule),runAt:I(s.run_at),type:o,priority:d,enabled:je(s.enabled,!0),created:I(s.created)??new Date().toISOString(),lastRun:I(s.last_run),nextRun:I(s.next_run),runCount:Me(s.run_count,0),catchUp:je(s.catch_up,this.settings.catchUpMissedTasks),effort:I(s.effort),model:I(s.model),tags:ye(s.tags),body:n}}parseChannelFile(t,e){let{frontmatter:s,body:n}=Q(e),a=I(s.name);if(!a)return this.setIssue(t,"Channel requires string field `name`."),null;let r=I(s.type),o=["slack","telegram"];if(!r||!o.includes(r))return this.setIssue(t,`Channel \`${a}\` requires \`type\` to be one of: ${o.join(", ")}.`),null;let c=r,l=I(s.default_agent)??I(s.agent);if(!l)return this.setIssue(t,`Channel \`${a}\` requires \`default_agent\` (or \`agent\`).`),null;let d=ye(s.allowed_agents),h=I(s.credential_ref);if(!h)return this.setIssue(t,`Channel \`${a}\` requires \`credential_ref\` pointing at a configured credential.`),null;let u=Js(s.transport)?s.transport:{};return{filePath:t,name:a,type:c,defaultAgent:l,allowedAgents:d,enabled:je(s.enabled,!0),credentialRef:h,allowedUsers:ye(s.allowed_users),perUserSessions:je(s.per_user_sessions,!0),channelContext:I(s.channel_context)??"",transport:u,tags:ye(s.tags),body:n}}validateReferences(){let t=new Set;for(let r of this.skills.values())t.has(r.name)&&this.setIssue(r.filePath,`Duplicate skill name \`${r.name}\`.`),t.add(r.name);let e=new Set;for(let r of this.agents.values()){e.has(r.name)&&this.setIssue(r.filePath,`Duplicate agent name \`${r.name}\`.`),e.add(r.name);for(let o of r.skills)t.has(o)||this.setIssue(r.filePath,`Agent references missing skill \`${o}\`.`)}for(let r of this.tasks.values())e.has(r.agent)||this.setIssue(r.filePath,`Task references missing agent \`${r.agent}\`.`);let s=new Set,n=new Map;for(let r of this.agents.values())n.set(r.name,r);let a=this.channelCredentialGetter?.()??this.settings.channelCredentials??{};for(let r of this.channels.values()){s.has(r.name)&&this.setIssue(r.filePath,`Duplicate channel name \`${r.name}\`.`),s.add(r.name);let o=n.get(r.defaultAgent);o?o.approvalRequired.length>0&&this.setIssue(r.filePath,`Channel \`${r.name}\` cannot bind to agent \`${o.name}\` because the agent has \`approval_required\` set (${o.approvalRequired.join(", ")}). Clear the approval list on the agent or pick a different agent.`):this.setIssue(r.filePath,`Channel \`${r.name}\` references missing agent \`${r.defaultAgent}\`.`);let c=a[r.credentialRef];c?c.type!==r.type&&this.setIssue(r.filePath,`Channel \`${r.name}\` is type \`${r.type}\` but credential \`${r.credentialRef}\` is type \`${c.type}\`.`):this.setIssue(r.filePath,`Channel \`${r.name}\` references missing credential \`${r.credentialRef}\`. Add it under Settings \u2192 Channel Credentials.`)}}parseEnvMap(t){if(!Js(t))return{};let e={};for(let[s,n]of Object.entries(t))typeof n=="string"?e[s]=n:(typeof n=="number"||typeof n=="boolean")&&(e[s]=String(n));return e}parseApprovals(t){if(Array.isArray(t))return t.flatMap(e=>{if(!Js(e)||!I(e.tool))return[];let s=I(e.tool);return s?[{tool:s,command:I(e.command),reason:I(e.reason),status:I(e.status)??"pending",resolvedAt:I(e.resolvedAt),note:I(e.note)}]:[]})}};var Ft=require("obsidian"),Xs=class extends Ft.Modal{constructor(e,s,n){super(e);this.info=s;this.onConfirm=n}deleteTasks=!0;onOpen(){let{contentEl:e}=this;e.empty(),e.addClass("af-confirm-delete-modal");let s=e.createDiv({cls:"af-delete-header"}),n=s.createSpan({cls:"af-delete-header-icon"});(0,Ft.setIcon)(n,"alert-triangle"),s.createEl("h3",{text:`Delete agent "${this.info.agentName}"?`});let a=e.createDiv({cls:"af-delete-summary"});a.createDiv({text:"This action will:"});let r=a.createEl("ul",{cls:"af-delete-impact-list"});r.createEl("li",{text:"Move the agent definition to trash"}),this.info.hasMemory&&r.createEl("li",{text:"Move the agent's memory file to trash"}),this.info.taskCount>0&&r.createEl("li").setText(`${this.info.taskCount} task${this.info.taskCount!==1?"s":""} reference this agent`),this.info.runCount>0&&r.createEl("li",{cls:"af-delete-preserved"}).setText(`${this.info.runCount} run log${this.info.runCount!==1?"s":""} will be preserved`),e.createDiv({cls:"af-delete-note",text:"Files are moved to your system trash and can be recovered."}),this.info.taskCount>0&&new Ft.Setting(e).setName("Also delete associated tasks").setDesc(`Delete ${this.info.taskCount} task${this.info.taskCount!==1?"s":""} that reference this agent`).addToggle(h=>{h.setValue(this.deleteTasks),h.onChange(u=>{this.deleteTasks=u})});let o=e.createDiv({cls:"af-delete-actions"}),c=o.createEl("button",{text:"Cancel"});c.onclick=()=>this.close();let l=o.createEl("button",{cls:"af-delete-confirm-btn",text:"Delete Agent"}),d=l.createSpan({cls:"af-delete-btn-icon"});(0,Ft.setIcon)(d,"trash-2"),l.onclick=()=>{this.onConfirm(this.deleteTasks),this.close()}}};var O=require("obsidian");var Zs=[{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"}],en=[{value:"gpt-5.5",label:"gpt-5.5 \u2014 frontier"},{value:"gpt-5.4",label:"gpt-5.4"},{value:"gpt-5.4-mini",label:"gpt-5.4-mini \u2014 fast"},{value:"gpt-5.3-codex",label:"gpt-5.3-codex"}],Qs="__custom__";function yi(i){let t=(i??"").trim().toLowerCase();return t==="codex"||t==="openai-codex"}function vi(i){return yi(i)?en:Zs}function Go(i,t){let e=i.trim();return!e||e==="default"||e==="subscription"?"inherit":vi(t).some(s=>s.value===e)?"alias":"custom"}function Ot(i,t){i.empty(),i.addClass("af-model-picker");let e=yi(t.adapter),s=vi(t.adapter),n=Go(t.value,t.adapter),a=i.createEl("select",{cls:"af-form-select af-mp-select"}),r=t.allowInherit?t.inheritPlaceholder??"Inherit from agent":e?"Default (let Codex pick)":"Default (let Claude Code pick)";a.createEl("option",{text:r,attr:{value:""}});let o=a.createEl("optgroup",{attr:{label:e?"Codex models":"Aliases (any backend)"}});for(let l of s)o.createEl("option",{text:l.label,attr:{value:l.value}});a.createEl("option",{text:"Custom\u2026",attr:{value:Qs}});let c=i.createEl("input",{cls:"af-form-input af-mp-custom-input",attr:{type:"text",placeholder:e?"e.g. gpt-5.5 \xB7 gpt-5.4-mini":"e.g. claude-opus-4-7 \xB7 us.anthropic.claude-opus-4-7 \xB7 claude-opus-4-7@20251101",spellcheck:"false"}});n==="inherit"?(a.value="",c.value="",c.style.display="none"):n==="alias"?(a.value=t.value.trim(),c.value="",c.style.display="none"):(a.value=Qs,c.value=t.value.trim(),c.style.display=""),a.addEventListener("change",()=>{a.value===Qs?(c.style.display="",c.focus(),t.onChange(c.value.trim())):(c.style.display="none",t.onChange(a.value))}),c.addEventListener("input",()=>{a.value===Qs&&t.onChange(c.value.trim())})}var he=require("obsidian");function Vo(){let i=new Date,t=i.getFullYear(),e=String(i.getMonth()+1).padStart(2,"0"),s=String(i.getDate()).padStart(2,"0");return`${t}-${e}-${s}`}var Yo="0 3 * * *",Ko="0 9 * * 0";function Zn(){return{scopeSlug:"",scopeRoot:"",inboxPath:"_sources/inbox",archivePath:"_sources/archive",topicsRoot:"_topics",indexPath:"index.md",logPath:"log.md",watchedFolders:[],excludePatterns:[],watchedSince:Vo(),heartbeatChannel:"",fileSubstantiveAnswers:!0,obsidianUrlScheme:!0,maxTokensPerIngest:6e4,maxTokensPerRefresh:3e4,indexSplitThreshold:100,dedupSimilarityThreshold:.82,summaryStaleDays:30,failedPath:"_sources/failed",stateFile:".wiki-keeper-state.json"}}var ki=Zn();function ea(i){let t=se(i).trim();return t?`wiki-keeper-${t}`:"wiki-keeper"}function Jo(i,t){let e=t||"the whole vault",s={name:i,description:`Cultivates ${e} as a self-maintaining wiki. Ingests new sources, extracts durable claims from watched folders, answers questions with citations, and periodically lints.`,avatar:"library",enabled:!0,skills:["wiki-ingest","wiki-query","wiki-lint","wiki-refresh"],tags:t?["wiki-keeper",`scope:${se(t)}`]:["wiki-keeper"]},n=`You are the Wiki Keeper for the \`${e}\` scope. Maintain it as a persistent, interlinked knowledge base (Karpathy's "LLM wiki" pattern).
|
|
11711
11771
|
|
|
11712
11772
|
## Scope isolation
|
|
11713
11773
|
|
|
@@ -11749,7 +11809,7 @@ Use \`memory\` for procedural learning ("user prefers concept pages under sub-fo
|
|
|
11749
11809
|
- Never write outside the scope root.
|
|
11750
11810
|
- Never edit \`## Claims\` history (append only).
|
|
11751
11811
|
- Never edit \`## Summary\` blocks by hand \u2014 \`wiki-refresh\` owns them.
|
|
11752
|
-
`;return
|
|
11812
|
+
`;return z(s,n)}function Xo(i){let t={model:"opus",adapter:"claude-code",timeout:900,max_retries:1,cwd:"",permission_mode:"acceptEdits",approval_required:["Write"],memory:!0,memory_max_entries:200,wiki_keeper:{scope_root:i.scopeRoot,inbox_path:i.inboxPath,archive_path:i.archivePath,failed_path:i.failedPath,topics_root:i.topicsRoot,index_path:i.indexPath,log_path:i.logPath,watched_folders:i.watchedFolders,exclude_patterns:i.excludePatterns,watched_since:i.watchedSince,file_substantive_answers:i.fileSubstantiveAnswers,obsidian_url_scheme:i.obsidianUrlScheme,max_tokens_per_ingest:i.maxTokensPerIngest,max_tokens_per_refresh:i.maxTokensPerRefresh,index_split_threshold:i.indexSplitThreshold,dedup_similarity_threshold:i.dedupSimilarityThreshold,summary_stale_days:i.summaryStaleDays,state_file:i.stateFile}};return z(t,"")}function Qo(i){let t={enabled:!0,schedule:Yo,notify:!0};return i.heartbeatChannel&&(t.channel=i.heartbeatChannel),z(t,`Run wiki-ingest in both modes:
|
|
11753
11813
|
|
|
11754
11814
|
1. Drain every unprocessed file in the configured inbox (inbox mode).
|
|
11755
11815
|
2. Diff watched folders against the state file; process changed or new files (watched mode).
|
|
@@ -11757,96 +11817,205 @@ Use \`memory\` for procedural learning ("user prefers concept pages under sub-fo
|
|
|
11757
11817
|
Lint runs on its own schedule via the sibling \`*-lint\` task.
|
|
11758
11818
|
|
|
11759
11819
|
Change the schedule by editing this file's \`schedule:\` frontmatter directly, or via the agent editor in the dashboard.
|
|
11760
|
-
`)}function
|
|
11761
|
-
`;return s instanceof
|
|
11820
|
+
`)}function xi(i){let e={task_id:`${i}-lint`,agent:i,type:"recurring",schedule:Ko,priority:"low",enabled:!0,created:new Date().toISOString(),run_count:0,catch_up:!1,tags:["wiki-keeper","lint"]};return z(e,"Run the `wiki-lint` skill on the current scope.\n\nWrite the report as a new `## Lint YYYY-MM-DD` section inside the fenced block in log.md.\n")}function ta(i,t){return(0,he.normalizePath)(`${i}/tasks/${t}-lint.md`)}var Zo="# Wiki Schema\n\n## Page types\n\n- **Entity pages** \u2014 people, orgs, products. Frontmatter: `type: entity`.\n- **Concept pages** \u2014 ideas, techniques, patterns. `type: concept`.\n- **Event pages** \u2014 meetings, releases, decisions. `type: event, date: YYYY-MM-DD`.\n- **Summary pages** (inbox-mode only) \u2014 one per ingested inbox source. `type: summary, source: <filename>`.\n- **Synthesis pages** \u2014 filed answers from `wiki-query` substantive responses. `type: synthesis, question: <q>, refreshed: <ISO>`. Live under `_topics/syntheses/`.\n\nTopic pages of type entity/concept/event SHOULD also carry:\n- `summary_refreshed: <ISO>` \u2014 last time `wiki-refresh` regenerated this page's summary block. Empty string on creation.\n- `claims_at_refresh: <int>` \u2014 how many claims existed at last refresh. Drives the staleness threshold.\n\n## Page anatomy\n\nEvery entity/concept/event page has this structure:\n\n```markdown\n---\ntype: entity\nname: Vendor X\nsummary_refreshed: 2026-04-26\nclaims_at_refresh: 14\n---\n\n<!-- wiki-keeper:summary:begin -->\n## Summary\n\n- 3-7 bullet synthesis maintained by wiki-refresh.\n- Forward-links to other topics with [[wikilinks]].\n<!-- wiki-keeper:summary:end -->\n\n## Claims\n\n- 2026-04-18 [meeting]: from [[meetings/2026-04-18-vendor-sync|vendor-sync]]: Vendor X raised prices 15%\n- 2026-04-12 [doc]: from [[summaries/2026-04-12-renewal-brief|renewal-brief]]: contract auto-renews 2026-12-01\n\n## Contradictions\n\n(only present when contradictions exist)\n```\n\nThe fenced summary block is auto-managed by `wiki-refresh`. The `## Claims` section is append-only history written by `wiki-ingest` and `wiki-query` compounding. `## Contradictions` is created on demand. **Anything outside these structures is user-authored** and must be preserved.\n\n## Source-type tags\n\nEvery dated bullet in `## Claims` carries a tag indicating the source type:\n\n- `[doc]` \u2014 PDF, DOCX, XLSX, or other document\n- `[meeting]` \u2014 meeting note or transcript\n- `[email]` \u2014 email forward\n- `[note]` \u2014 markdown daily note or in-vault note\n- `[web]` \u2014 web clipping or URL drop\n- `[synthesis]` \u2014 bullet emitted by `wiki-query` compounding (links to a synthesis page)\n- `[other]` \u2014 fallback\n\nTags help `wiki-query` weight evidence quality and help `wiki-lint` identify drift.\n\n## Naming\n\n- Slug-case filenames: `vendor-x.md`, not `Vendor X.md`.\n- Group by type under `_topics/<type>/` when there are >5 pages of a type.\n\n## Links\n\n- Every entity/concept page MUST have \u22651 inbound link from `index.md` (or a sub-MOC) or a sibling.\n- Summary pages MUST forward-link to every entity/concept they mention.\n- Watched-mode extractions append dated entries to topic pages; forward-link to the source file path so readers can find the raw note.\n- Synthesis pages forward-link to every cited topic and earn their place that way (lint exempts them from orphan flagging if they have any outbound links to topics).\n\n## Index split\n\nWhen the topic count exceeds the `index_split_threshold` config value (default 100) or any single type exceeds 30 pages, `index.md` becomes a hub of hubs and per-type sub-MOCs live at `<scope>/index/<type>.md`. After split, new topic-page entries go into the matching sub-MOC, not the root index.\n\n## Conflict resolution\n\n- New claim contradicts existing? Add a `## Contradictions` section with a dated entry. Do NOT overwrite.\n- Flag in `log.md` for user review.\n- `wiki-refresh` reflects unresolved contradictions in the summary so query-time readers see them.\n\n## Failed sources\n\nFiles that fail ingest 3 times in a row are quarantined to `<failed_path>/` (default `_sources/failed/`) with a sidecar `.error.md`. Lint surfaces the quarantine count weekly.\n",el=["Read","Write","Edit","Glob","Grep","Bash(mv *)","Bash(mkdir *)"],tl=["Bash(rm -rf *)","Bash(git push *)","Bash(rm -rf /*)","Bash(mv * /*)","Bash(cp -r * /*)"];async function Si(i,t){let e=(0,he.normalizePath)(`${t}/permissions.json`),s=i.getAbstractFileByPath(e),n={allow:[],deny:[]};if(s instanceof he.TFile)try{let o=await i.cachedRead(s),c=JSON.parse(o),l=Array.isArray(c.allow)?c.allow.filter(h=>typeof h=="string"):[],d=Array.isArray(c.deny)?c.deny.filter(h=>typeof h=="string"):[];n={allow:l,deny:d}}catch{}let a={allow:bi(n.allow,el),deny:bi(n.deny,tl)},r=JSON.stringify(a,null,2)+`
|
|
11821
|
+
`;return s instanceof he.TFile?await i.modify(s,r):await i.create(e,r),a}function bi(i,t){let e=new Set,s=[];for(let n of i)e.has(n)||(e.add(n),s.push(n));for(let n of t)e.has(n)||(e.add(n),s.push(n));return s}async function Ci(i,t,e){let s=ea(e.scopeSlug||e.scopeRoot),n=(0,he.normalizePath)(`${t}/agents/${s}`);if(await Kt(i,n),i.getAbstractFileByPath((0,he.normalizePath)(`${n}/agent.md`)))throw new Error(`Wiki Keeper agent already exists at ${n}. Delete it first or choose a different scope slug.`);await i.create((0,he.normalizePath)(`${n}/agent.md`),Jo(s,e.scopeRoot)),await i.create((0,he.normalizePath)(`${n}/config.md`),Xo(e)),await i.create((0,he.normalizePath)(`${n}/HEARTBEAT.md`),Qo(e)),await i.create((0,he.normalizePath)(`${n}/CONTEXT.md`),Zo),await Si(i,n);let r=ta(t,s);i.getAbstractFileByPath(r)||(await Kt(i,(0,he.normalizePath)(`${t}/tasks`)),await i.create(r,xi(s)));let o=e.scopeRoot.trim(),c=o?`${o}/`:"";return await Kt(i,(0,he.normalizePath)(`${c}${e.inboxPath}`)),await Kt(i,(0,he.normalizePath)(`${c}${e.topicsRoot}`)),await wi(i,(0,he.normalizePath)(`${c}${e.indexPath}`),`# Index
|
|
11762
11822
|
|
|
11763
11823
|
<!-- wiki-keeper:begin -->
|
|
11764
11824
|
<!-- wiki-keeper:end -->
|
|
11765
|
-
`),await
|
|
11825
|
+
`),await wi(i,(0,he.normalizePath)(`${c}${e.logPath}`),`# Log
|
|
11766
11826
|
|
|
11767
11827
|
<!-- wiki-keeper:begin -->
|
|
11768
11828
|
<!-- wiki-keeper:end -->
|
|
11769
|
-
`),{path:a,name:s}}async function Ot(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 cn(r,t,e){if(r.getAbstractFileByPath(t))return;let s=t.replace(/\/[^/]+$/,"");s&&s!==t&&await Ot(r,s),await r.create(t,e)}async function mn(r,t,e,s){let a=(0,oe.normalizePath)(`${t}/agents/${e}`),n=(0,oe.normalizePath)(`${a}/config.md`),i=r.getAbstractFileByPath(n);if(!(i instanceof oe.TFile))throw new Error(`Config file not found for ${e} at ${n}`);let o=await r.cachedRead(i),{frontmatter:l,body:c}=ae(o),d=l.wiki_keeper??{};d.watched_folders=s.watchedFolders,d.exclude_patterns=s.excludePatterns,s.watchedSince?d.watched_since=s.watchedSince:delete d.watched_since,delete d.ingest_schedule,delete d.lint_schedule,delete d.lint_day,d.file_substantive_answers=s.fileSubstantiveAnswers,d.obsidian_url_scheme=s.obsidianUrlScheme,d.max_tokens_per_ingest=s.maxTokensPerIngest,d.max_tokens_per_refresh=s.maxTokensPerRefresh,d.index_split_threshold=s.indexSplitThreshold,d.dedup_similarity_threshold=s.dedupSimilarityThreshold,d.summary_stale_days=s.summaryStaleDays,(typeof d.failed_path!="string"||!d.failed_path)&&(d.failed_path="_sources/failed"),l.wiki_keeper=d,delete l.allowed_tools,delete l.blocked_tools,await r.modify(i,K(l,c)),await un(r,a);let h=(0,oe.normalizePath)(`${a}/HEARTBEAT.md`),u=r.getAbstractFileByPath(h);if(u instanceof oe.TFile){let m=await r.cachedRead(u),{frontmatter:g,body:k}=ae(m);s.heartbeatChannel?g.channel=s.heartbeatChannel:delete g.channel,await r.modify(u,K(g,k))}let p=fa(t,e);r.getAbstractFileByPath(p)instanceof oe.TFile||(await Ot(r,(0,oe.normalizePath)(`${t}/tasks`)),await r.create(p,hn(e)))}async function fn(r,t,e){let s=(0,oe.normalizePath)(`${t}/agents/${e}`),a=r.getAbstractFileByPath(s);if(!a)return;if(!(a instanceof oe.TFolder))throw new Error(`Expected folder at ${s}`);await r.adapter.rmdir(s,!0);let n=fa(t,e),i=r.getAbstractFileByPath(n);i instanceof oe.TFile&&await r.delete(i)}var Ts=class extends B.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 B.Setting(e).setName("Fleet folder").addText(n=>n.setValue(this.plugin.settings.fleetFolder).onChange(async i=>{this.plugin.settings.fleetFolder=i.trim()||lt.fleetFolder,await this.plugin.saveSettings()})),new B.Setting(e).setName("Claude CLI path").addText(n=>n.setValue(this.plugin.settings.claudeCliPath).onChange(async i=>{this.plugin.settings.claudeCliPath=i.trim()||lt.claudeCliPath,await this.plugin.saveSettings()}));let a=new B.Setting(e).setName("Default model").setDesc("Fallback for agents that don\u2019t set their own. Aliases (opus/sonnet/haiku) work on any backend; use Custom for pinned IDs or Bedrock/Vertex/Foundry.").controlEl.createDiv();_t(a,{value:this.plugin.settings.defaultModel,onChange:async n=>{this.plugin.settings.defaultModel=n||lt.defaultModel,await this.plugin.saveSettings()}}),new B.Setting(e).setName("AWS region").addText(n=>n.setValue(this.plugin.settings.awsRegion).onChange(async i=>{this.plugin.settings.awsRegion=i.trim()||lt.awsRegion,await this.plugin.saveSettings()})),new B.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 B.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 B.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 B.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 B.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 B.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 B.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 B.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 B.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 B.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 B.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 B.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 B.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 B.Setting(o).setName("Bot token (xoxb-...)").addText(c=>{c.inputEl.type="password",c.setPlaceholder("xoxb-...").onChange(d=>{i.botToken=d.trim()})}),new B.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 B.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 B.Setting(n).addButton(c=>c.setButtonText("Add credential").setCta().onClick(async()=>{if(!i.ref||!i.botToken){new B.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 B.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 B.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 ${gr(fr(n))}`,cls:"af-muted"}).style.color="var(--text-muted)";let l=i.createEl("button",{text:"Remove"});l.onclick=()=>{this.plugin.channelCredentials.delete(a),new B.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 ya(this.plugin,o,()=>{this.scheduleRerender()}).open()};let p=h.createEl("button",{text:"Delete",cls:"af-wk-row-btn af-wk-row-btn-danger"});p.onclick=async()=>{if(confirm(`Delete Wiki Keeper "${o.name}"?
|
|
11829
|
+
`),{path:n,name:s}}async function Kt(i,t){if(i.getAbstractFileByPath(t))return;let e=t.split("/"),s="";for(let n of e)if(s=s?`${s}/${n}`:n,!i.getAbstractFileByPath(s))try{await i.createFolder(s)}catch(a){if(!(a instanceof Error?a.message:String(a)).includes("already exists"))throw a}}async function wi(i,t,e){if(i.getAbstractFileByPath(t))return;let s=t.replace(/\/[^/]+$/,"");s&&s!==t&&await Kt(i,s),await i.create(t,e)}async function Ti(i,t,e,s){let n=(0,he.normalizePath)(`${t}/agents/${e}`),a=(0,he.normalizePath)(`${n}/config.md`),r=i.getAbstractFileByPath(a);if(!(r instanceof he.TFile))throw new Error(`Config file not found for ${e} at ${a}`);let o=await i.cachedRead(r),{frontmatter:c,body:l}=Q(o),d=c.wiki_keeper??{};d.watched_folders=s.watchedFolders,d.exclude_patterns=s.excludePatterns,s.watchedSince?d.watched_since=s.watchedSince:delete d.watched_since,delete d.ingest_schedule,delete d.lint_schedule,delete d.lint_day,d.file_substantive_answers=s.fileSubstantiveAnswers,d.obsidian_url_scheme=s.obsidianUrlScheme,d.max_tokens_per_ingest=s.maxTokensPerIngest,d.max_tokens_per_refresh=s.maxTokensPerRefresh,d.index_split_threshold=s.indexSplitThreshold,d.dedup_similarity_threshold=s.dedupSimilarityThreshold,d.summary_stale_days=s.summaryStaleDays,(typeof d.failed_path!="string"||!d.failed_path)&&(d.failed_path="_sources/failed"),c.wiki_keeper=d,delete c.allowed_tools,delete c.blocked_tools,await i.modify(r,z(c,l)),await Si(i,n);let h=(0,he.normalizePath)(`${n}/HEARTBEAT.md`),u=i.getAbstractFileByPath(h);if(u instanceof he.TFile){let f=await i.cachedRead(u),{frontmatter:y,body:w}=Q(f);s.heartbeatChannel?y.channel=s.heartbeatChannel:delete y.channel,await i.modify(u,z(y,w))}let p=ta(t,e);i.getAbstractFileByPath(p)instanceof he.TFile||(await Kt(i,(0,he.normalizePath)(`${t}/tasks`)),await i.create(p,xi(e)))}async function _i(i,t,e){let s=(0,he.normalizePath)(`${t}/agents/${e}`),n=i.getAbstractFileByPath(s);if(!n)return;if(!(n instanceof he.TFolder))throw new Error(`Expected folder at ${s}`);await i.adapter.rmdir(s,!0);let a=ta(t,e),r=i.getAbstractFileByPath(a);r instanceof he.TFile&&await i.delete(r)}var tn=class extends O.PluginSettingTab{constructor(e){super(e.app,e);this.plugin=e}display(){let{containerEl:e}=this;e.empty(),e.createEl("h2",{text:"Agent Fleet Settings"}),new O.Setting(e).setName("Fleet folder").addText(a=>a.setValue(this.plugin.settings.fleetFolder).onChange(async r=>{this.plugin.settings.fleetFolder=r.trim()||dt.fleetFolder,await this.plugin.saveSettings()})),new O.Setting(e).setName("Claude CLI path").addText(a=>a.setValue(this.plugin.settings.claudeCliPath).onChange(async r=>{this.plugin.settings.claudeCliPath=r.trim()||dt.claudeCliPath,await this.plugin.saveSettings()})),new O.Setting(e).setName("Codex CLI path").setDesc("Used by agents with adapter \u201Ccodex\u201D. Install via `npm i -g @openai/codex` and authenticate with `codex login` in a terminal first.").addText(a=>a.setValue(this.plugin.settings.codexCliPath).onChange(async r=>{this.plugin.settings.codexCliPath=r.trim()||dt.codexCliPath,await this.plugin.saveSettings()}));let n=new O.Setting(e).setName("Default model").setDesc("Fallback for agents that don\u2019t set their own. Aliases (opus/sonnet/haiku) work on any backend; use Custom for pinned IDs or Bedrock/Vertex/Foundry.").controlEl.createDiv();Ot(n,{value:this.plugin.settings.defaultModel,onChange:async a=>{this.plugin.settings.defaultModel=a||dt.defaultModel,await this.plugin.saveSettings()}}),new O.Setting(e).setName("AWS region").addText(a=>a.setValue(this.plugin.settings.awsRegion).onChange(async r=>{this.plugin.settings.awsRegion=r.trim()||dt.awsRegion,await this.plugin.saveSettings()})),new O.Setting(e).setName("Max concurrent runs").addSlider(a=>a.setLimits(1,10,1).setValue(this.plugin.settings.maxConcurrentRuns).setDynamicTooltip().onChange(async r=>{this.plugin.settings.maxConcurrentRuns=r,await this.plugin.saveSettings()})),new O.Setting(e).setName("Run log retention").setDesc("Days to keep run logs before auto-prune.").addSlider(a=>a.setLimits(1,365,1).setValue(this.plugin.settings.runLogRetentionDays).setDynamicTooltip().onChange(async r=>{this.plugin.settings.runLogRetentionDays=r,await this.plugin.saveSettings()})),new O.Setting(e).setName("Catch up missed tasks").addToggle(a=>a.setValue(this.plugin.settings.catchUpMissedTasks).onChange(async r=>{this.plugin.settings.catchUpMissedTasks=r,await this.plugin.saveSettings()})),new O.Setting(e).setName("Notification level").addDropdown(a=>a.addOption("all","All").addOption("failures-only","Failures only").addOption("none","None").setValue(this.plugin.settings.notificationLevel).onChange(async r=>{this.plugin.settings.notificationLevel=r,await this.plugin.saveSettings()})),new O.Setting(e).setName("Status bar").addToggle(a=>a.setValue(this.plugin.settings.showStatusBar).onChange(async r=>{this.plugin.settings.showStatusBar=r,await this.plugin.saveSettings(),this.plugin.refreshStatusBar()})),new O.Setting(e).setName("Chat watchdog timeout (minutes)").setDesc("How long a chat session waits for any output from the Claude CLI before killing the subprocess and surfacing a timeout error. Raise this if your agents run long tools (e.g. large web fetches, builds) and you're seeing spurious 'no response' errors.").addSlider(a=>a.setLimits(1,60,1).setValue(this.plugin.settings.chatWatchdogMinutes).setDynamicTooltip().onChange(async r=>{this.plugin.settings.chatWatchdogMinutes=r,await this.plugin.saveSettings()})),new O.Setting(e).setName("Verify Claude CLI").setDesc("Checks that the configured binary is reachable.").addButton(a=>a.setButtonText("Verify").onClick(async()=>{let r=await this.plugin.verifyClaudeCli();new O.Notice(r?"Claude CLI detected.":"Claude CLI check failed. See console for details.")})),new O.Setting(e).setName("Verify Codex CLI").setDesc("Checks that the configured Codex binary is reachable.").addButton(a=>a.setButtonText("Verify").onClick(async()=>{let r=await this.plugin.verifyCodexCli();new O.Notice(r?"Codex CLI detected.":"Codex CLI check failed. See console for details.")})),this.renderWikiKeepersSection(e),this.renderChannelsSection(e)}renderChannelsSection(e){e.createEl("h3",{text:"Channels"});let s=e.createDiv({cls:"af-settings-warning"});s.style.padding="12px",s.style.margin="8px 0 16px 0",s.style.border="1px solid var(--background-modifier-border)",s.style.borderRadius="6px",s.style.background="var(--background-secondary)",s.createEl("strong",{text:"Credential storage: "}),s.createSpan({text:"Channel credentials are stored in this plugin's data.json inside your vault's .obsidian folder. If you sync your .obsidian folder across devices, credentials will sync with it. Do not commit this file to a public git repository."}),new O.Setting(e).setName("Max concurrent channel sessions").setDesc("Hard cap on live claude subprocesses across all channels. Oldest idle session is hibernated when exceeded.").addSlider(l=>l.setLimits(1,20,1).setValue(this.plugin.settings.maxConcurrentChannelSessions).setDynamicTooltip().onChange(async d=>{this.plugin.settings.maxConcurrentChannelSessions=d,await this.plugin.saveSettings()})),new O.Setting(e).setName("Idle timeout (minutes)").setDesc("Channel sessions with no activity for this long get their subprocess hibernated. State is preserved and the next message resumes transparently.").addSlider(l=>l.setLimits(1,120,1).setValue(this.plugin.settings.channelIdleTimeoutMinutes).setDynamicTooltip().onChange(async d=>{this.plugin.settings.channelIdleTimeoutMinutes=d,await this.plugin.saveSettings()})),new O.Setting(e).setName("Rate limit per conversation").setDesc("Maximum messages allowed per external conversation within the rolling window.").addSlider(l=>l.setLimits(1,100,1).setValue(this.plugin.settings.channelRateLimitPerConversation).setDynamicTooltip().onChange(async d=>{this.plugin.settings.channelRateLimitPerConversation=d,await this.plugin.saveSettings()})),new O.Setting(e).setName("Rate limit window (minutes)").addSlider(l=>l.setLimits(1,60,1).setValue(this.plugin.settings.channelRateLimitWindowMinutes).setDynamicTooltip().onChange(async d=>{this.plugin.settings.channelRateLimitWindowMinutes=d,await this.plugin.saveSettings()})),e.createEl("h4",{text:"Channel credentials"});let n=e.createDiv({cls:"af-channel-credentials"});this.renderCredentialList(n);let a=e.createDiv({cls:"af-channel-credential-add"});a.style.marginTop="12px",a.style.padding="12px",a.style.border="1px dashed var(--background-modifier-border)",a.style.borderRadius="6px",a.createEl("strong",{text:"Add a channel credential"});let r={ref:"",type:"slack",botToken:"",appToken:""};new O.Setting(a).setName("Reference name").setDesc("Used by `credential_ref` in _fleet/channels/*.md files.").addText(l=>l.setPlaceholder("my-creds").onChange(d=>{r.ref=d.trim()})),new O.Setting(a).setName("Type").addDropdown(l=>l.addOption("slack","Slack").addOption("telegram","Telegram").setValue("slack").onChange(d=>{r.type=d,o.style.display=d==="slack"?"":"none",c.style.display=d==="telegram"?"":"none"}));let o=a.createDiv();new O.Setting(o).setName("Bot token (xoxb-...)").addText(l=>{l.inputEl.type="password",l.setPlaceholder("xoxb-...").onChange(d=>{r.botToken=d.trim()})}),new O.Setting(o).setName("App-level token (xapp-...)").setDesc("Generated in your Slack app's Basic Information \u2192 App-Level Tokens.").addText(l=>{l.inputEl.type="password",l.setPlaceholder("xapp-...").onChange(d=>{r.appToken=d.trim()})});let c=a.createDiv();c.style.display="none",new O.Setting(c).setName("Bot token").setDesc("From @BotFather on Telegram.").addText(l=>{l.inputEl.type="password",l.setPlaceholder("123456:ABC-DEF1234...").onChange(d=>{r.botToken=d.trim()})}),new O.Setting(a).addButton(l=>l.setButtonText("Add credential").setCta().onClick(async()=>{if(!r.ref||!r.botToken){new O.Notice("Fill in the reference name and bot token.");return}let d;if(r.type==="telegram")d={type:"telegram",botToken:r.botToken};else{if(!r.appToken){new O.Notice("Slack requires both bot token and app-level token.");return}d={type:"slack",botToken:r.botToken,appToken:r.appToken}}this.plugin.channelCredentials.set(r.ref,d),new O.Notice(`Added credential \`${r.ref}\`.`),this.display()}))}renderCredentialList(e){let s=this.plugin.channelCredentials.list();if(s.length===0){e.createDiv({text:"No channel credentials configured yet.",cls:"af-muted"}).style.color="var(--text-muted)";return}for(let{ref:n,entry:a}of s){let r=e.createDiv({cls:"af-channel-credential-row"});r.style.display="flex",r.style.justifyContent="space-between",r.style.alignItems="center",r.style.padding="8px 12px",r.style.border="1px solid var(--background-modifier-border)",r.style.borderRadius="6px",r.style.marginBottom="6px";let o=r.createDiv();o.createEl("strong",{text:n}),o.createEl("span",{text:` \xB7 ${a.type} \xB7 ${nl(sl(a))}`,cls:"af-muted"}).style.color="var(--text-muted)";let c=r.createEl("button",{text:"Remove"});c.onclick=()=>{this.plugin.channelCredentials.delete(n),new O.Notice(`Removed credential \`${n}\`.`),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 a=this.plugin.runtime.getSnapshot().agents.filter(o=>o.wikiKeeper!==void 0),r=e.createDiv({cls:"af-wk-list"});if(a.length===0)r.createDiv({cls:"af-wk-empty",text:"No Wiki Keepers yet. Click Add to create one."});else for(let o of a){let c=r.createDiv({cls:"af-wk-row"}),l=c.createDiv({cls:"af-wk-row-left"});l.createSpan({cls:"af-wk-name",text:o.name});let d=o.wikiKeeper.scopeRoot||"(whole vault)";l.createSpan({cls:"af-wk-scope",text:`scope: ${d}`});let h=c.createDiv({cls:"af-wk-row-actions"}),u=h.createEl("button",{text:"Edit",cls:"af-wk-row-btn"});u.onclick=()=>{new na(this.plugin,o,()=>{this.scheduleRerender()}).open()};let p=h.createEl("button",{text:"Delete",cls:"af-wk-row-btn af-wk-row-btn-danger"});p.onclick=async()=>{if(confirm(`Delete Wiki Keeper "${o.name}"?
|
|
11770
11830
|
|
|
11771
11831
|
This removes the agent folder at _fleet/agents/${o.name}/.
|
|
11772
|
-
Your scope's inbox, topics, index, and log are NOT deleted.`))try{await fn(this.plugin.app.vault,this.plugin.settings.fleetFolder,o.name),await this.plugin.refreshFromVault(),new B.Notice(`Wiki Keeper "${o.name}" deleted.`),this.scheduleRerender()}catch(m){let g=m instanceof Error?m.message:String(m);new B.Notice(`Failed to delete: ${g}`)}}}new B.Setting(e).setName("Add Wiki Keeper").setDesc("Create a new scoped wiki agent. All fields optional.").addButton(o=>o.setButtonText("+ Add").onClick(()=>{new ga(this.plugin,()=>{this.scheduleRerender()}).open()}))}scheduleRerender(){window.setTimeout(async()=>{try{await this.plugin.refreshFromVault()}catch{}this.display()},600)}},ga=class extends B.Modal{constructor(e,s){super(e.app);this.plugin=e;this.onCreated=s;this.input=pa()}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 B.Setting(e).setName("Scope folder").setDesc("Optional. Vault-relative path. Empty = whole vault.").addText(d=>d.setPlaceholder("(empty = whole vault)").setValue(this.input.scopeRoot).onChange(h=>{this.input.scopeRoot=h.trim(),this.renderPreview()})),new B.Setting(e).setName("Slug").setDesc("Optional. Agent name suffix. Default: derived from scope folder name, or 'wiki-keeper' for whole-vault.").addText(d=>d.setPlaceholder("(auto)").setValue(this.input.scopeSlug).onChange(h=>{this.input.scopeSlug=h.trim(),this.renderPreview()})),this.renderPreview(),e.createEl("h3",{text:"Sources",cls:"af-wk-section-h"}),new B.Setting(e).setName("Watched folders").setDesc("Optional. Comma- or newline-separated vault-relative paths. Read-only \u2014 claims are extracted but files are never moved. Leave empty for inbox-only mode (you drop files into _sources/inbox/ and they get archived after processing).").addTextArea(d=>d.setPlaceholder("(empty = inbox-only)").setValue(this.input.watchedFolders.join(", ")).onChange(h=>{this.input.watchedFolders=h.split(/[,\n]/).map(u=>u.trim()).filter(Boolean)})),new B.Setting(e).setName("Exclude patterns").setDesc("Optional. Glob patterns to skip. Leave empty to process everything under watched + inbox.").addTextArea(d=>d.setPlaceholder("(empty = no excludes)").setValue(this.input.excludePatterns.join(", ")).onChange(h=>{this.input.excludePatterns=h.split(/[,\n]/).map(u=>u.trim()).filter(Boolean)})),new B.Setting(e).setName("Watch files modified since").setDesc("Watched mode only \u2014 files whose last modification date is before this are skipped. Defaults to today so an established vault doesn't flood the keeper on first run. Clear the field to process everything.").addText(d=>{d.inputEl.type="date",d.setValue(this.input.watchedSince).onChange(h=>{this.input.watchedSince=h.trim()})});let a=this.plugin.runtime.getSnapshot().channels.map(d=>d.name);new B.Setting(e).setName("Heartbeat channel").setDesc("Optional. Slack/Telegram channel for ingest + lint summaries. Leave as (none) to disable.").addDropdown(d=>{d.addOption("","(none)");for(let h of a)d.addOption(h,h);d.setValue(this.input.heartbeatChannel).onChange(h=>{this.input.heartbeatChannel=h})}),e.createEl("h3",{text:"Advanced",cls:"af-wk-section-h"}),new B.Setting(e).setName("Max tokens per ingest").setDesc("Hard cap on token spend per run. Unprocessed files resume next cycle.").addText(d=>d.setValue(String(this.input.maxTokensPerIngest)).onChange(h=>{let u=parseInt(h,10);!isNaN(u)&&u>0&&(this.input.maxTokensPerIngest=u)})),new B.Setting(e).setName("File substantive answers").setDesc("If on, wiki-query files long answers under _topics/syntheses/. Off = answers live only in chat.").addToggle(d=>d.setValue(this.input.fileSubstantiveAnswers).onChange(h=>{this.input.fileSubstantiveAnswers=h})),new B.Setting(e).setName("Obsidian URL scheme for citations").setDesc("If on, external-channel (Slack/Telegram) replies rewrite [[wikilinks]] as obsidian:// URLs.").addToggle(d=>d.setValue(this.input.obsidianUrlScheme).onChange(h=>{this.input.obsidianUrlScheme=h}));let n=e.createEl("h3",{text:"Paths (expert)",cls:"af-wk-section-h"});n.title="Paths are relative to scope_root. Defaults work for almost everyone. Override only if your vault layout demands it \u2014 these cannot be changed after creation.";let i=(d,h,u)=>{new B.Setting(e).setName(d).setDesc(h).addText(p=>p.setPlaceholder(String(this.input[u]??"")).setValue(String(this.input[u]??"")).onChange(f=>{this.input[u]=f.trim()||dn[u]}))};i("Inbox path","Where one-off source drops land.","inboxPath"),i("Archive path","Where processed inbox files are moved after summarization.","archivePath"),i("Topics root","Folder under scope_root that holds the generated topic pages.","topicsRoot"),i("Index path","Relative file path for the curated index.","indexPath"),i("Log path","Relative file path for the keeper's activity log.","logPath"),i("State file","Hidden JSON file that tracks watched-source mtimes.","stateFile");let o=e.createDiv({cls:"af-wk-modal-footer"}),l=o.createEl("button",{text:"Cancel"});l.onclick=()=>this.close();let c=o.createEl("button",{text:"Create",cls:"mod-cta"});c.onclick=async()=>{c.setText("Creating\u2026"),c.disabled=!0;try{let{name:d}=await pn(this.app.vault,this.plugin.settings.fleetFolder,this.input);await this.plugin.refreshFromVault(),this.close(),new B.Notice(`Wiki Keeper "${d}" created.`),this.onCreated()}catch(d){let h=d instanceof Error?d.message:String(d);new B.Notice(`Failed to create Wiki Keeper: ${h}`),c.setText("Create"),c.disabled=!1}}}renderPreview(){let e=this.contentEl.querySelector(".af-wk-name-preview");e||(e=this.contentEl.createDiv({cls:"af-wk-name-preview"}));let s=this.input.scopeSlug||this.input.scopeRoot;e.setText(`\u2192 Will create agent: ${ma(s)}`)}onClose(){this.contentEl.empty()}},ya=class extends B.Modal{constructor(e,s,a){super(e.app);this.plugin=e;this.agent=s;this.onSaved=a;let n=s.wikiKeeper;this.edit={watchedFolders:[...n.watchedFolders],excludePatterns:[...n.excludePatterns],watchedSince:n.watchedSince,heartbeatChannel:s.heartbeatChannel??"",fileSubstantiveAnswers:n.fileSubstantiveAnswers,obsidianUrlScheme:n.obsidianUrlScheme,maxTokensPerIngest:n.maxTokensPerIngest,maxTokensPerRefresh:n.maxTokensPerRefresh,indexSplitThreshold:n.indexSplitThreshold,dedupSimilarityThreshold:n.dedupSimilarityThreshold,summaryStaleDays:n.summaryStaleDays}}edit;onOpen(){let{contentEl:e}=this;e.empty(),e.addClass("af-wk-modal"),e.createEl("h2",{text:`Edit ${this.agent.name}`});let s=e.createDiv({cls:"af-wk-banner"}),a=this.agent.wikiKeeper.scopeRoot||"(whole vault)";s.createEl("strong",{text:"Scope: "}),s.createSpan({text:a}),s.createEl("br"),s.createSpan({text:"Scope and paths are fixed after creation \u2014 changing them would orphan existing topic pages. To move a Wiki Keeper, delete this one and create a new one at the new scope."}),e.createEl("h3",{text:"Sources",cls:"af-wk-section-h"}),new B.Setting(e).setName("Watched folders").setDesc("Comma- or newline-separated vault-relative paths.").addTextArea(c=>c.setValue(this.edit.watchedFolders.join(", ")).onChange(d=>{this.edit.watchedFolders=d.split(/[,\n]/).map(h=>h.trim()).filter(Boolean)})),new B.Setting(e).setName("Exclude patterns").setDesc("Glob patterns to skip.").addTextArea(c=>c.setValue(this.edit.excludePatterns.join(", ")).onChange(d=>{this.edit.excludePatterns=d.split(/[,\n]/).map(h=>h.trim()).filter(Boolean)})),new B.Setting(e).setName("Watch files modified since").setDesc("Watched mode skips files older than this date. Clear to process everything.").addText(c=>{c.inputEl.type="date",c.setValue(this.edit.watchedSince).onChange(d=>{this.edit.watchedSince=d.trim()})});let n=this.plugin.runtime.getSnapshot().channels.map(c=>c.name);new B.Setting(e).setName("Heartbeat channel").addDropdown(c=>{c.addOption("","(none)");for(let d of n)c.addOption(d,d);c.setValue(this.edit.heartbeatChannel).onChange(d=>{this.edit.heartbeatChannel=d})}),e.createEl("h3",{text:"Advanced",cls:"af-wk-section-h"}),new B.Setting(e).setName("Max tokens per ingest").addText(c=>c.setValue(String(this.edit.maxTokensPerIngest)).onChange(d=>{let h=parseInt(d,10);!isNaN(h)&&h>0&&(this.edit.maxTokensPerIngest=h)})),new B.Setting(e).setName("Max tokens per refresh").setDesc("Separate budget for the synthesis-refresh phase that regenerates topic-page summaries.").addText(c=>c.setValue(String(this.edit.maxTokensPerRefresh)).onChange(d=>{let h=parseInt(d,10);!isNaN(h)&&h>0&&(this.edit.maxTokensPerRefresh=h)})),new B.Setting(e).setName("Index split threshold").setDesc("Topic-page count above which index.md splits into per-type sub-MOCs.").addText(c=>c.setValue(String(this.edit.indexSplitThreshold)).onChange(d=>{let h=parseInt(d,10);!isNaN(h)&&h>0&&(this.edit.indexSplitThreshold=h)})),new B.Setting(e).setName("Summary stale (days)").setDesc("Lint flags topic-page summaries older than this and chains wiki-refresh.").addText(c=>c.setValue(String(this.edit.summaryStaleDays)).onChange(d=>{let h=parseInt(d,10);!isNaN(h)&&h>0&&(this.edit.summaryStaleDays=h)})),new B.Setting(e).setName("Dedup similarity threshold").setDesc("Levenshtein-ratio above which lint proposes merging two same-type pages (0.0\u20131.0).").addText(c=>c.setValue(String(this.edit.dedupSimilarityThreshold)).onChange(d=>{let h=parseFloat(d);!isNaN(h)&&h>0&&h<=1&&(this.edit.dedupSimilarityThreshold=h)})),new B.Setting(e).setName("File substantive answers").setDesc("Compounding: wiki-query files long answers under _topics/syntheses/ and bullets each cited topic page. Default ON.").addToggle(c=>c.setValue(this.edit.fileSubstantiveAnswers).onChange(d=>{this.edit.fileSubstantiveAnswers=d})),new B.Setting(e).setName("Obsidian URL scheme for citations").setDesc("Rewrite [[wikilinks]] as obsidian:// URLs when replying via Slack/Telegram.").addToggle(c=>c.setValue(this.edit.obsidianUrlScheme).onChange(d=>{this.edit.obsidianUrlScheme=d}));let i=e.createDiv({cls:"af-wk-modal-footer"}),o=i.createEl("button",{text:"Cancel"});o.onclick=()=>this.close();let l=i.createEl("button",{text:"Save",cls:"mod-cta"});l.onclick=async()=>{l.setText("Saving\u2026"),l.disabled=!0;try{await mn(this.app.vault,this.plugin.settings.fleetFolder,this.agent.name,this.edit),await this.plugin.refreshFromVault(),this.close(),new B.Notice("Saved."),this.onSaved()}catch(c){let d=c instanceof Error?c.message:String(c);new B.Notice(`Failed to save: ${d}`),l.setText("Save"),l.disabled=!1}}}onClose(){this.contentEl.empty()}};function fr(r){return r.type==="slack",r.botToken}function gr(r){return r.length<=10?"***":`${r.slice(0,6)}\u2026${r.slice(-4)}`}var An=require("crypto");function qe(r,t,e,s,a,n,i,o){return qe.fromTZ(qe.tp(r,t,e,s,a,n,i),o)}qe.fromTZISO=(r,t,e)=>qe.fromTZ(yr(r,t),e);qe.fromTZ=function(r,t){let e=new Date(Date.UTC(r.y,r.m-1,r.d,r.h,r.i,r.s)),s=va(r.tz,e),a=new Date(e.getTime()-s),n=va(r.tz,a);if(n-s===0)return a;{let i=new Date(e.getTime()-n),o=va(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}};qe.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}};qe.tp=(r,t,e,s,a,n,i)=>({y:r,m:t,d:e,h:s,i:a,s:n,tz:i});function va(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 yr(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("+")?qe.tp(e.getUTCFullYear(),e.getUTCMonth()+1,e.getUTCDate(),e.getUTCHours(),e.getUTCMinutes(),e.getUTCSeconds(),"Etc/UTC"):qe.tp(e.getFullYear(),e.getMonth()+1,e.getDate(),e.getHours(),e.getMinutes(),e.getSeconds(),t)}qe.minitz=qe;function vr(r){if(r===void 0&&(r={}),delete r.name,r.legacyMode=r.legacyMode===void 0?!0:r.legacyMode,r.paused=r.paused===void 0?!1:r.paused,r.maxRuns=r.maxRuns===void 0?1/0:r.maxRuns,r.catch=r.catch===void 0?!1:r.catch,r.interval=r.interval===void 0?0:parseInt(r.interval,10),r.utcOffset=r.utcOffset===void 0?void 0:parseInt(r.utcOffset,10),r.unref=r.unref===void 0?!1:r.unref,r.startAt&&(r.startAt=new Ce(r.startAt,r.timezone)),r.stopAt&&(r.stopAt=new Ce(r.stopAt,r.timezone)),r.interval!==null){if(isNaN(r.interval))throw new Error("CronOptions: Supplied value for interval is not a number");if(r.interval<0)throw new Error("CronOptions: Supplied value for interval can not be negative")}if(r.utcOffset!==void 0){if(isNaN(r.utcOffset))throw new Error("CronOptions: Invalid value passed for utcOffset, should be number representing minutes offset from UTC.");if(r.utcOffset<-870||r.utcOffset>870)throw new Error("CronOptions: utcOffset out of bounds.");if(r.utcOffset!==void 0&&r.timezone)throw new Error("CronOptions: Combining 'utcOffset' with 'timezone' is not allowed.")}if(r.unref!==!0&&r.unref!==!1)throw new Error("CronOptions: Unref should be either true, false or undefined(false).");return r}var ba=32,ts=31|ba,yn=[1,2,4,8,16];function ze(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()}ze.prototype.parse=function(){if(!(typeof this.pattern=="string"||this.pattern.constructor===String))throw new TypeError("CronPattern: Pattern has to be of type string.");this.pattern.indexOf("@")>=0&&(this.pattern=this.handleNicknames(this.pattern).trim());let r=this.pattern.replace(/\s+/g," ").split(" ");if(r.length<5||r.length>6)throw new TypeError("CronPattern: invalid configuration format ('"+this.pattern+"'), exactly five or six space separated parts are required.");if(r.length===5&&r.unshift("0"),r[3].indexOf("L")>=0&&(r[3]=r[3].replace("L",""),this.lastDayOfMonth=!0),r[3]=="*"&&(this.starDOM=!0),r[4].length>=3&&(r[4]=this.replaceAlphaMonths(r[4])),r[5].length>=3&&(r[5]=this.replaceAlphaDays(r[5])),r[5]=="*"&&(this.starDOW=!0),this.pattern.indexOf("?")>=0){let t=new Ce(new Date,this.timezone).getDate(!0);r[0]=r[0].replace("?",t.getSeconds()),r[1]=r[1].replace("?",t.getMinutes()),r[2]=r[2].replace("?",t.getHours()),this.starDOM||(r[3]=r[3].replace("?",t.getDate())),r[4]=r[4].replace("?",t.getMonth()+1),this.starDOW||(r[5]=r[5].replace("?",t.getDay()))}this.throwAtIllegalCharacters(r),this.partToArray("second",r[0],0,1),this.partToArray("minute",r[1],0,1),this.partToArray("hour",r[2],0,1),this.partToArray("day",r[3],-1,1),this.partToArray("month",r[4],-1,1),this.partToArray("dayOfWeek",r[5],0,ts),this.dayOfWeek[7]&&(this.dayOfWeek[0]=this.dayOfWeek[7])};ze.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)};ze.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.")};ze.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)};ze.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};ze.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)};ze.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]};ze.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)};ze.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)};ze.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")};ze.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")};ze.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};ze.prototype.setNthWeekdayOfMonth=function(r,t){if(t==="L")this.dayOfWeek[r]=this.dayOfWeek[r]|ba;else if(t<6&&t>0)this.dayOfWeek[r]=this.dayOfWeek[r]|yn[t-1];else if(t===ts)this.dayOfWeek[r]=ts;else throw new TypeError(`CronPattern: nth weekday of of range, should be 1-5 or L. Value: ${t}`)};var vn=[31,28,31,30,31,30,31,31,30,31,30,31],pt=[["month","year",0],["day","month",-1],["hour","day",0],["minute","hour",0],["second","minute",0]];function Ce(r,t){if(this.tz=t,r&&r instanceof Date)if(!isNaN(r))this.fromDate(r);else throw new TypeError("CronDate: Invalid date passed to CronDate constructor");else if(r===void 0)this.fromDate(new Date);else if(r&&typeof r=="string")this.fromString(r);else if(r instanceof Ce)this.fromCronDate(r);else throw new TypeError("CronDate: Invalid type ("+typeof r+") passed to CronDate constructor")}Ce.prototype.isNthWeekdayOfMonth=function(r,t,e,s){let n=new Date(Date.UTC(r,t,e)).getUTCDay(),i=0;for(let o=1;o<=e;o++)new Date(Date.UTC(r,t,o)).getUTCDay()===n&&i++;if(s&ts&&yn[i-1]&s)return!0;if(s&ba){let o=new Date(Date.UTC(r,t+1,0)).getUTCDate();for(let l=e+1;l<=o;l++)if(new Date(Date.UTC(r,t,l)).getUTCDay()===n)return!1;return!0}return!1};Ce.prototype.fromDate=function(r){if(this.tz!==void 0)if(typeof this.tz=="number")this.ms=r.getUTCMilliseconds(),this.second=r.getUTCSeconds(),this.minute=r.getUTCMinutes()+this.tz,this.hour=r.getUTCHours(),this.day=r.getUTCDate(),this.month=r.getUTCMonth(),this.year=r.getUTCFullYear(),this.apply();else{let t=qe.toTZ(r,this.tz);this.ms=r.getMilliseconds(),this.second=t.s,this.minute=t.i,this.hour=t.h,this.day=t.d,this.month=t.m-1,this.year=t.y}else this.ms=r.getMilliseconds(),this.second=r.getSeconds(),this.minute=r.getMinutes(),this.hour=r.getHours(),this.day=r.getDate(),this.month=r.getMonth(),this.year=r.getFullYear()};Ce.prototype.fromCronDate=function(r){this.tz=r.tz,this.year=r.year,this.month=r.month,this.day=r.day,this.hour=r.hour,this.minute=r.minute,this.second=r.second,this.ms=r.ms};Ce.prototype.apply=function(){if(this.month>11||this.day>vn[this.month]||this.hour>59||this.minute>59||this.second>59||this.hour<0||this.minute<0||this.second<0){let r=new Date(Date.UTC(this.year,this.month,this.day,this.hour,this.minute,this.second,this.ms));return this.ms=r.getUTCMilliseconds(),this.second=r.getUTCSeconds(),this.minute=r.getUTCMinutes(),this.hour=r.getUTCHours(),this.day=r.getUTCDate(),this.month=r.getUTCMonth(),this.year=r.getUTCFullYear(),!0}else return!1};Ce.prototype.fromString=function(r){if(typeof this.tz=="number"){let t=qe.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(qe.fromTZISO(r,this.tz))};Ce.prototype.findNext=function(r,t,e,s){let a=this[t],n;e.lastDayOfMonth&&(this.month!==1?n=vn[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&ts)c=this.isNthWeekdayOfMonth(this.year,this.month,o-s,c);else if(c)throw new Error(`CronDate: Invalid value for dayOfWeek encountered. ${c}`);r.legacyMode&&!e.starDOM?l=l||c:l=l&&c}if(l)return this[t]=o-s,a!==this[t]?2:1}return 3};Ce.prototype.recurse=function(r,t,e){let s=this.findNext(t,pt[e][0],r,pt[e][2]);if(s>1){let a=e+1;for(;a<pt.length;)this[pt[a][0]]=-pt[a][2],a++;if(s===3)return this[pt[e][1]]++,this[pt[e][0]]=-pt[e][2],this.apply(),this.recurse(r,t,0);if(this.apply())return this.recurse(r,t,e-1)}return e+=1,e>=pt.length?this:this.year>=3e3?null:this.recurse(r,t,e)};Ce.prototype.increment=function(r,t,e){return this.second+=t.interval>1&&e?t.interval:1,this.ms=0,this.apply(),this.recurse(r,t,0)};Ce.prototype.getDate=function(r){return r||this.tz===void 0?new Date(this.year,this.month,this.day,this.hour,this.minute,this.second,this.ms):typeof this.tz=="number"?new Date(Date.UTC(this.year,this.month,this.day,this.hour,this.minute-this.tz,this.second,this.ms)):qe(this.year,this.month+1,this.day,this.hour,this.minute,this.second,this.tz)};Ce.prototype.getTime=function(){return this.getDate().getTime()};function Cs(r){return Object.prototype.toString.call(r)==="[object Function]"||typeof r=="function"||r instanceof Function}function br(r){typeof Deno<"u"&&typeof Deno.unrefTimer<"u"?Deno.unrefTimer(r):r&&typeof r.unref<"u"&&r.unref()}var gn=30*1e3,ss=[];function le(r,t,e){if(!(this instanceof le))return new le(r,t,e);let s,a;if(Cs(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(Cs(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=vr(s),this._states={kill:!1,blocking:!1,previousRun:void 0,currentRun:void 0,once:void 0,currentTimeout:void 0,maxRuns:s?s.maxRuns:void 0,paused:s?s.paused:!1,pattern:void 0},r&&(r instanceof Date||typeof r=="string"&&r.indexOf(":")>0)?this._states.once=new Ce(r,this.options.timezone||this.options.utcOffset):this._states.pattern=new ze(r,this.options.timezone),this.name){if(ss.find(i=>i.name===this.name))throw new Error("Cron: Tried to initialize new named job '"+this.name+"', but name already taken.");ss.push(this)}return a!==void 0&&(this.fn=a,this.schedule()),this}le.prototype.nextRun=function(r){let t=this._next(r);return t?t.getDate():null};le.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};le.prototype.getPattern=function(){return this._states.pattern?this._states.pattern.pattern:void 0};le.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};le.prototype.isStopped=function(){return this._states.kill};le.prototype.isBusy=function(){return this._states.blocking};le.prototype.currentRun=function(){return this._states.currentRun?this._states.currentRun.getDate():null};le.prototype.previousRun=function(){return this._states.previousRun?this._states.previousRun.getDate():null};le.prototype.msToNext=function(r){r=r||new Date;let t=this._next(r);return t?t.getTime()-r.getTime():null};le.prototype.stop=function(){this._states.kill=!0,this._states.currentTimeout&&clearTimeout(this._states.currentTimeout);let r=ss.indexOf(this);r>=0&&ss.splice(r,1)};le.prototype.pause=function(){return this._states.paused=!0,!this._states.kill};le.prototype.resume=function(){return this._states.paused=!1,!this._states.kill};le.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>gn&&(t=gn),this._states.currentTimeout=setTimeout(()=>this._checkTrigger(e),t),this._states.currentTimeout&&this.options.unref&&br(this._states.currentTimeout),this)};le.prototype._trigger=async function(r){if(this._states.blocking=!0,this._states.currentRun=new Ce(void 0,this.options.timezone||this.options.utcOffset),this.options.catch)try{await this.fn(this,this.options.context)}catch(t){Cs(this.options.catch)&&this.options.catch(t,this)}else await this.fn(this,this.options.context);this._states.previousRun=new Ce(r,this.options.timezone||this.options.utcOffset),this._states.blocking=!1};le.prototype.trigger=async function(){await this._trigger()};le.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&&Cs(this.options.protect)&&setTimeout(()=>this.options.protect(this),0),this.schedule()};le.prototype._next=function(r){let t=!!(r||this._states.currentRun),e=!1;!r&&this.options.startAt&&this.options.interval&&([r,t]=this._calculatePreviousRun(r,t),e=!r),r=new Ce(r,this.options.timezone||this.options.utcOffset),this.options.startAt&&r&&r.getTime()<this.options.startAt.getTime()&&(r=this.options.startAt);let s=this._states.once||new Ce(r,this.options.timezone||this.options.utcOffset);return!e&&s!==this._states.once&&(s=s.increment(this._states.pattern,this.options,t)),this._states.once&&this._states.once.getTime()<=r.getTime()||s===null||this._states.maxRuns<=0||this._states.kill||this.options.stopAt&&s.getTime()>=this.options.stopAt.getTime()?null:s};le.prototype._calculatePreviousRun=function(r,t){let e=new Ce(void 0,this.options.timezone||this.options.utcOffset);if(this.options.startAt.getTime()<=e.getTime()){r=this.options.startAt;let s=r.getTime()+this.options.interval*1e3;for(;s<=e.getTime();)r=new Ce(r,this.options.timezone||this.options.utcOffset).increment(this._states.pattern,this.options,!0),s=r.getTime()+this.options.interval*1e3;t=!0}return[r,t]};le.Cron=le;le.scheduledJobs=ss;var En=require("obsidian");var kn=require("crypto");var bn=new Set(["","default","subscription"]);function Nt(r,t,e){let s=wa(r?.model);if(s)return{value:ka(s),source:"task"};let a=wa(t.model);if(a)return{value:ka(a),source:"agent"};let n=wa(e.defaultModel);return n?{value:ka(n),source:"settings"}:{value:"",source:"cli-default"}}function _s(r){return!bn.has(r.trim())}function wa(r){if(!r)return"";let t=r.trim();return t||""}function ka(r){return bn.has(r)?"":r}function As(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(`
|
|
11773
|
-
|
|
11774
|
-
|
|
11775
|
-
`)
|
|
11776
|
-
\u25B8 ${
|
|
11777
|
-
`)}if(a.length>0)return a.join("")}}if(
|
|
11778
|
-
${
|
|
11779
|
-
|
|
11780
|
-
${
|
|
11781
|
-
|
|
11832
|
+
Your scope's inbox, topics, index, and log are NOT deleted.`))try{await _i(this.plugin.app.vault,this.plugin.settings.fleetFolder,o.name),await this.plugin.refreshFromVault(),new O.Notice(`Wiki Keeper "${o.name}" deleted.`),this.scheduleRerender()}catch(f){let y=f instanceof Error?f.message:String(f);new O.Notice(`Failed to delete: ${y}`)}}}new O.Setting(e).setName("Add Wiki Keeper").setDesc("Create a new scoped wiki agent. All fields optional.").addButton(o=>o.setButtonText("+ Add").onClick(()=>{new sa(this.plugin,()=>{this.scheduleRerender()}).open()}))}scheduleRerender(){window.setTimeout(async()=>{try{await this.plugin.refreshFromVault()}catch{}this.display()},600)}},sa=class extends O.Modal{constructor(e,s){super(e.app);this.plugin=e;this.onCreated=s;this.input=Zn()}input;onOpen(){let{contentEl:e}=this;e.empty(),e.addClass("af-wk-modal"),e.createEl("h2",{text:"Add Wiki Keeper"});let s=e.createDiv({cls:"af-wk-banner"});s.createEl("strong",{text:"All fields are optional. "}),s.createSpan({text:"Click Create with everything blank and you'll get a whole-vault keeper with sensible defaults (inbox at _sources/inbox, topics at topics/, nightly ingest at 3am, Sunday lint)."}),e.createEl("h3",{text:"Scope",cls:"af-wk-section-h"}),new O.Setting(e).setName("Scope folder").setDesc("Optional. Vault-relative path. Empty = whole vault.").addText(d=>d.setPlaceholder("(empty = whole vault)").setValue(this.input.scopeRoot).onChange(h=>{this.input.scopeRoot=h.trim(),this.renderPreview()})),new O.Setting(e).setName("Slug").setDesc("Optional. Agent name suffix. Default: derived from scope folder name, or 'wiki-keeper' for whole-vault.").addText(d=>d.setPlaceholder("(auto)").setValue(this.input.scopeSlug).onChange(h=>{this.input.scopeSlug=h.trim(),this.renderPreview()})),this.renderPreview(),e.createEl("h3",{text:"Sources",cls:"af-wk-section-h"}),new O.Setting(e).setName("Watched folders").setDesc("Optional. Comma- or newline-separated vault-relative paths. Read-only \u2014 claims are extracted but files are never moved. Leave empty for inbox-only mode (you drop files into _sources/inbox/ and they get archived after processing).").addTextArea(d=>d.setPlaceholder("(empty = inbox-only)").setValue(this.input.watchedFolders.join(", ")).onChange(h=>{this.input.watchedFolders=h.split(/[,\n]/).map(u=>u.trim()).filter(Boolean)})),new O.Setting(e).setName("Exclude patterns").setDesc("Optional. Glob patterns to skip. Leave empty to process everything under watched + inbox.").addTextArea(d=>d.setPlaceholder("(empty = no excludes)").setValue(this.input.excludePatterns.join(", ")).onChange(h=>{this.input.excludePatterns=h.split(/[,\n]/).map(u=>u.trim()).filter(Boolean)})),new O.Setting(e).setName("Watch files modified since").setDesc("Watched mode only \u2014 files whose last modification date is before this are skipped. Defaults to today so an established vault doesn't flood the keeper on first run. Clear the field to process everything.").addText(d=>{d.inputEl.type="date",d.setValue(this.input.watchedSince).onChange(h=>{this.input.watchedSince=h.trim()})});let n=this.plugin.runtime.getSnapshot().channels.map(d=>d.name);new O.Setting(e).setName("Heartbeat channel").setDesc("Optional. Slack/Telegram channel for ingest + lint summaries. Leave as (none) to disable.").addDropdown(d=>{d.addOption("","(none)");for(let h of n)d.addOption(h,h);d.setValue(this.input.heartbeatChannel).onChange(h=>{this.input.heartbeatChannel=h})}),e.createEl("h3",{text:"Advanced",cls:"af-wk-section-h"}),new O.Setting(e).setName("Max tokens per ingest").setDesc("Hard cap on token spend per run. Unprocessed files resume next cycle.").addText(d=>d.setValue(String(this.input.maxTokensPerIngest)).onChange(h=>{let u=parseInt(h,10);!isNaN(u)&&u>0&&(this.input.maxTokensPerIngest=u)})),new O.Setting(e).setName("File substantive answers").setDesc("If on, wiki-query files long answers under _topics/syntheses/. Off = answers live only in chat.").addToggle(d=>d.setValue(this.input.fileSubstantiveAnswers).onChange(h=>{this.input.fileSubstantiveAnswers=h})),new O.Setting(e).setName("Obsidian URL scheme for citations").setDesc("If on, external-channel (Slack/Telegram) replies rewrite [[wikilinks]] as obsidian:// URLs.").addToggle(d=>d.setValue(this.input.obsidianUrlScheme).onChange(h=>{this.input.obsidianUrlScheme=h}));let a=e.createEl("h3",{text:"Paths (expert)",cls:"af-wk-section-h"});a.title="Paths are relative to scope_root. Defaults work for almost everyone. Override only if your vault layout demands it \u2014 these cannot be changed after creation.";let r=(d,h,u)=>{new O.Setting(e).setName(d).setDesc(h).addText(p=>p.setPlaceholder(String(this.input[u]??"")).setValue(String(this.input[u]??"")).onChange(m=>{this.input[u]=m.trim()||ki[u]}))};r("Inbox path","Where one-off source drops land.","inboxPath"),r("Archive path","Where processed inbox files are moved after summarization.","archivePath"),r("Topics root","Folder under scope_root that holds the generated topic pages.","topicsRoot"),r("Index path","Relative file path for the curated index.","indexPath"),r("Log path","Relative file path for the keeper's activity log.","logPath"),r("State file","Hidden JSON file that tracks watched-source mtimes.","stateFile");let o=e.createDiv({cls:"af-wk-modal-footer"}),c=o.createEl("button",{text:"Cancel"});c.onclick=()=>this.close();let l=o.createEl("button",{text:"Create",cls:"mod-cta"});l.onclick=async()=>{l.setText("Creating\u2026"),l.disabled=!0;try{let{name:d}=await Ci(this.app.vault,this.plugin.settings.fleetFolder,this.input);await this.plugin.refreshFromVault(),this.close(),new O.Notice(`Wiki Keeper "${d}" created.`),this.onCreated()}catch(d){let h=d instanceof Error?d.message:String(d);new O.Notice(`Failed to create Wiki Keeper: ${h}`),l.setText("Create"),l.disabled=!1}}}renderPreview(){let e=this.contentEl.querySelector(".af-wk-name-preview");e||(e=this.contentEl.createDiv({cls:"af-wk-name-preview"}));let s=this.input.scopeSlug||this.input.scopeRoot;e.setText(`\u2192 Will create agent: ${ea(s)}`)}onClose(){this.contentEl.empty()}},na=class extends O.Modal{constructor(e,s,n){super(e.app);this.plugin=e;this.agent=s;this.onSaved=n;let a=s.wikiKeeper;this.edit={watchedFolders:[...a.watchedFolders],excludePatterns:[...a.excludePatterns],watchedSince:a.watchedSince,heartbeatChannel:s.heartbeatChannel??"",fileSubstantiveAnswers:a.fileSubstantiveAnswers,obsidianUrlScheme:a.obsidianUrlScheme,maxTokensPerIngest:a.maxTokensPerIngest,maxTokensPerRefresh:a.maxTokensPerRefresh,indexSplitThreshold:a.indexSplitThreshold,dedupSimilarityThreshold:a.dedupSimilarityThreshold,summaryStaleDays:a.summaryStaleDays}}edit;onOpen(){let{contentEl:e}=this;e.empty(),e.addClass("af-wk-modal"),e.createEl("h2",{text:`Edit ${this.agent.name}`});let s=e.createDiv({cls:"af-wk-banner"}),n=this.agent.wikiKeeper.scopeRoot||"(whole vault)";s.createEl("strong",{text:"Scope: "}),s.createSpan({text:n}),s.createEl("br"),s.createSpan({text:"Scope and paths are fixed after creation \u2014 changing them would orphan existing topic pages. To move a Wiki Keeper, delete this one and create a new one at the new scope."}),e.createEl("h3",{text:"Sources",cls:"af-wk-section-h"}),new O.Setting(e).setName("Watched folders").setDesc("Comma- or newline-separated vault-relative paths.").addTextArea(l=>l.setValue(this.edit.watchedFolders.join(", ")).onChange(d=>{this.edit.watchedFolders=d.split(/[,\n]/).map(h=>h.trim()).filter(Boolean)})),new O.Setting(e).setName("Exclude patterns").setDesc("Glob patterns to skip.").addTextArea(l=>l.setValue(this.edit.excludePatterns.join(", ")).onChange(d=>{this.edit.excludePatterns=d.split(/[,\n]/).map(h=>h.trim()).filter(Boolean)})),new O.Setting(e).setName("Watch files modified since").setDesc("Watched mode skips files older than this date. Clear to process everything.").addText(l=>{l.inputEl.type="date",l.setValue(this.edit.watchedSince).onChange(d=>{this.edit.watchedSince=d.trim()})});let a=this.plugin.runtime.getSnapshot().channels.map(l=>l.name);new O.Setting(e).setName("Heartbeat channel").addDropdown(l=>{l.addOption("","(none)");for(let d of a)l.addOption(d,d);l.setValue(this.edit.heartbeatChannel).onChange(d=>{this.edit.heartbeatChannel=d})}),e.createEl("h3",{text:"Advanced",cls:"af-wk-section-h"}),new O.Setting(e).setName("Max tokens per ingest").addText(l=>l.setValue(String(this.edit.maxTokensPerIngest)).onChange(d=>{let h=parseInt(d,10);!isNaN(h)&&h>0&&(this.edit.maxTokensPerIngest=h)})),new O.Setting(e).setName("Max tokens per refresh").setDesc("Separate budget for the synthesis-refresh phase that regenerates topic-page summaries.").addText(l=>l.setValue(String(this.edit.maxTokensPerRefresh)).onChange(d=>{let h=parseInt(d,10);!isNaN(h)&&h>0&&(this.edit.maxTokensPerRefresh=h)})),new O.Setting(e).setName("Index split threshold").setDesc("Topic-page count above which index.md splits into per-type sub-MOCs.").addText(l=>l.setValue(String(this.edit.indexSplitThreshold)).onChange(d=>{let h=parseInt(d,10);!isNaN(h)&&h>0&&(this.edit.indexSplitThreshold=h)})),new O.Setting(e).setName("Summary stale (days)").setDesc("Lint flags topic-page summaries older than this and chains wiki-refresh.").addText(l=>l.setValue(String(this.edit.summaryStaleDays)).onChange(d=>{let h=parseInt(d,10);!isNaN(h)&&h>0&&(this.edit.summaryStaleDays=h)})),new O.Setting(e).setName("Dedup similarity threshold").setDesc("Levenshtein-ratio above which lint proposes merging two same-type pages (0.0\u20131.0).").addText(l=>l.setValue(String(this.edit.dedupSimilarityThreshold)).onChange(d=>{let h=parseFloat(d);!isNaN(h)&&h>0&&h<=1&&(this.edit.dedupSimilarityThreshold=h)})),new O.Setting(e).setName("File substantive answers").setDesc("Compounding: wiki-query files long answers under _topics/syntheses/ and bullets each cited topic page. Default ON.").addToggle(l=>l.setValue(this.edit.fileSubstantiveAnswers).onChange(d=>{this.edit.fileSubstantiveAnswers=d})),new O.Setting(e).setName("Obsidian URL scheme for citations").setDesc("Rewrite [[wikilinks]] as obsidian:// URLs when replying via Slack/Telegram.").addToggle(l=>l.setValue(this.edit.obsidianUrlScheme).onChange(d=>{this.edit.obsidianUrlScheme=d}));let r=e.createDiv({cls:"af-wk-modal-footer"}),o=r.createEl("button",{text:"Cancel"});o.onclick=()=>this.close();let c=r.createEl("button",{text:"Save",cls:"mod-cta"});c.onclick=async()=>{c.setText("Saving\u2026"),c.disabled=!0;try{await Ti(this.app.vault,this.plugin.settings.fleetFolder,this.agent.name,this.edit),await this.plugin.refreshFromVault(),this.close(),new O.Notice("Saved."),this.onSaved()}catch(l){let d=l instanceof Error?l.message:String(l);new O.Notice(`Failed to save: ${d}`),c.setText("Save"),c.disabled=!1}}}onClose(){this.contentEl.empty()}};function sl(i){return i.type==="slack",i.botToken}function nl(i){return i.length<=10?"***":`${i.slice(0,6)}\u2026${i.slice(-4)}`}var Zi=require("crypto");function We(i,t,e,s,n,a,r,o){return We.fromTZ(We.tp(i,t,e,s,n,a,r),o)}We.fromTZISO=(i,t,e)=>We.fromTZ(al(i,t),e);We.fromTZ=function(i,t){let e=new Date(Date.UTC(i.y,i.m-1,i.d,i.h,i.i,i.s)),s=aa(i.tz,e),n=new Date(e.getTime()-s),a=aa(i.tz,n);if(a-s===0)return n;{let r=new Date(e.getTime()-a),o=aa(i.tz,r);if(o-a===0)return r;if(!t&&o-a>0)return r;if(t)throw new Error("Invalid date passed to fromTZ()");return n}};We.toTZ=function(i,t){let e=i.toLocaleString("en-US",{timeZone:t}).replace(/[\u202f]/," "),s=new Date(e);return{y:s.getFullYear(),m:s.getMonth()+1,d:s.getDate(),h:s.getHours(),i:s.getMinutes(),s:s.getSeconds(),tz:t}};We.tp=(i,t,e,s,n,a,r)=>({y:i,m:t,d:e,h:s,i:n,s:a,tz:r});function aa(i,t=new Date){let e=t.toLocaleString("en-US",{timeZone:i,timeZoneName:"shortOffset"}).split(" ").slice(-1)[0],s=t.toLocaleString("en-US").replace(/[\u202f]/," ");return Date.parse(`${s} GMT`)-Date.parse(`${s} ${e}`)}function al(i,t){let e=new Date(Date.parse(i));if(isNaN(e))throw new Error("minitz: Invalid ISO8601 passed to parser.");let s=i.substring(9);return i.includes("Z")||s.includes("-")||s.includes("+")?We.tp(e.getUTCFullYear(),e.getUTCMonth()+1,e.getUTCDate(),e.getUTCHours(),e.getUTCMinutes(),e.getUTCSeconds(),"Etc/UTC"):We.tp(e.getFullYear(),e.getMonth()+1,e.getDate(),e.getHours(),e.getMinutes(),e.getSeconds(),t)}We.minitz=We;function il(i){if(i===void 0&&(i={}),delete i.name,i.legacyMode=i.legacyMode===void 0?!0:i.legacyMode,i.paused=i.paused===void 0?!1:i.paused,i.maxRuns=i.maxRuns===void 0?1/0:i.maxRuns,i.catch=i.catch===void 0?!1:i.catch,i.interval=i.interval===void 0?0:parseInt(i.interval,10),i.utcOffset=i.utcOffset===void 0?void 0:parseInt(i.utcOffset,10),i.unref=i.unref===void 0?!1:i.unref,i.startAt&&(i.startAt=new _e(i.startAt,i.timezone)),i.stopAt&&(i.stopAt=new _e(i.stopAt,i.timezone)),i.interval!==null){if(isNaN(i.interval))throw new Error("CronOptions: Supplied value for interval is not a number");if(i.interval<0)throw new Error("CronOptions: Supplied value for interval can not be negative")}if(i.utcOffset!==void 0){if(isNaN(i.utcOffset))throw new Error("CronOptions: Invalid value passed for utcOffset, should be number representing minutes offset from UTC.");if(i.utcOffset<-870||i.utcOffset>870)throw new Error("CronOptions: utcOffset out of bounds.");if(i.utcOffset!==void 0&&i.timezone)throw new Error("CronOptions: Combining 'utcOffset' with 'timezone' is not allowed.")}if(i.unref!==!0&&i.unref!==!1)throw new Error("CronOptions: Unref should be either true, false or undefined(false).");return i}var ia=32,fs=31|ia,Ai=[1,2,4,8,16];function ze(i,t){this.pattern=i,this.timezone=t,this.second=Array(60).fill(0),this.minute=Array(60).fill(0),this.hour=Array(24).fill(0),this.day=Array(31).fill(0),this.month=Array(12).fill(0),this.dayOfWeek=Array(7).fill(0),this.lastDayOfMonth=!1,this.starDOM=!1,this.starDOW=!1,this.parse()}ze.prototype.parse=function(){if(!(typeof this.pattern=="string"||this.pattern.constructor===String))throw new TypeError("CronPattern: Pattern has to be of type string.");this.pattern.indexOf("@")>=0&&(this.pattern=this.handleNicknames(this.pattern).trim());let i=this.pattern.replace(/\s+/g," ").split(" ");if(i.length<5||i.length>6)throw new TypeError("CronPattern: invalid configuration format ('"+this.pattern+"'), exactly five or six space separated parts are required.");if(i.length===5&&i.unshift("0"),i[3].indexOf("L")>=0&&(i[3]=i[3].replace("L",""),this.lastDayOfMonth=!0),i[3]=="*"&&(this.starDOM=!0),i[4].length>=3&&(i[4]=this.replaceAlphaMonths(i[4])),i[5].length>=3&&(i[5]=this.replaceAlphaDays(i[5])),i[5]=="*"&&(this.starDOW=!0),this.pattern.indexOf("?")>=0){let t=new _e(new Date,this.timezone).getDate(!0);i[0]=i[0].replace("?",t.getSeconds()),i[1]=i[1].replace("?",t.getMinutes()),i[2]=i[2].replace("?",t.getHours()),this.starDOM||(i[3]=i[3].replace("?",t.getDate())),i[4]=i[4].replace("?",t.getMonth()+1),this.starDOW||(i[5]=i[5].replace("?",t.getDay()))}this.throwAtIllegalCharacters(i),this.partToArray("second",i[0],0,1),this.partToArray("minute",i[1],0,1),this.partToArray("hour",i[2],0,1),this.partToArray("day",i[3],-1,1),this.partToArray("month",i[4],-1,1),this.partToArray("dayOfWeek",i[5],0,fs),this.dayOfWeek[7]&&(this.dayOfWeek[0]=this.dayOfWeek[7])};ze.prototype.partToArray=function(i,t,e,s){let n=this[i],a=i==="day"&&this.lastDayOfMonth;if(t===""&&!a)throw new TypeError("CronPattern: configuration entry "+i+" ("+t+") is empty, check for trailing spaces.");if(t==="*")return n.fill(s);let r=t.split(",");if(r.length>1)for(let o=0;o<r.length;o++)this.partToArray(i,r[o],e,s);else t.indexOf("-")!==-1&&t.indexOf("/")!==-1?this.handleRangeWithStepping(t,i,e,s):t.indexOf("-")!==-1?this.handleRange(t,i,e,s):t.indexOf("/")!==-1?this.handleStepping(t,i,e,s):t!==""&&this.handleNumber(t,i,e,s)};ze.prototype.throwAtIllegalCharacters=function(i){for(let t=0;t<i.length;t++)if((t===5?/[^/*0-9,\-#L]+/:/[^/*0-9,-]+/).test(i[t]))throw new TypeError("CronPattern: configuration entry "+t+" ("+i[t]+") contains illegal characters.")};ze.prototype.handleNumber=function(i,t,e,s){let n=this.extractNth(i,t),a=parseInt(n[0],10)+e;if(isNaN(a))throw new TypeError("CronPattern: "+t+" is not a number: '"+i+"'");this.setPart(t,a,n[1]||s)};ze.prototype.setPart=function(i,t,e){if(!Object.prototype.hasOwnProperty.call(this,i))throw new TypeError("CronPattern: Invalid part specified: "+i);if(i==="dayOfWeek"){if(t===7&&(t=0),(t<0||t>6)&&t!=="L")throw new RangeError("CronPattern: Invalid value for dayOfWeek: "+t);this.setNthWeekdayOfMonth(t,e);return}if(i==="second"||i==="minute"){if(t<0||t>=60)throw new RangeError("CronPattern: Invalid value for "+i+": "+t)}else if(i==="hour"){if(t<0||t>=24)throw new RangeError("CronPattern: Invalid value for "+i+": "+t)}else if(i==="day"){if(t<0||t>=31)throw new RangeError("CronPattern: Invalid value for "+i+": "+t)}else if(i==="month"&&(t<0||t>=12))throw new RangeError("CronPattern: Invalid value for "+i+": "+t);this[i][t]=e};ze.prototype.handleRangeWithStepping=function(i,t,e,s){let n=this.extractNth(i,t),a=n[0].match(/^(\d+)-(\d+)\/(\d+)$/);if(a===null)throw new TypeError("CronPattern: Syntax error, illegal range with stepping: '"+i+"'");let[,r,o,c]=a;if(r=parseInt(r,10)+e,o=parseInt(o,10)+e,c=parseInt(c,10),isNaN(r))throw new TypeError("CronPattern: Syntax error, illegal lower range (NaN)");if(isNaN(o))throw new TypeError("CronPattern: Syntax error, illegal upper range (NaN)");if(isNaN(c))throw new TypeError("CronPattern: Syntax error, illegal stepping: (NaN)");if(c===0)throw new TypeError("CronPattern: Syntax error, illegal stepping: 0");if(c>this[t].length)throw new TypeError("CronPattern: Syntax error, steps cannot be greater than maximum value of part ("+this[t].length+")");if(r>o)throw new TypeError("CronPattern: From value is larger than to value: '"+i+"'");for(let l=r;l<=o;l+=c)this.setPart(t,l,n[1]||s)};ze.prototype.extractNth=function(i,t){let e=i,s;if(e.includes("#")){if(t!=="dayOfWeek")throw new Error("CronPattern: nth (#) only allowed in day-of-week field");s=e.split("#")[1],e=e.split("#")[0]}return[e,s]};ze.prototype.handleRange=function(i,t,e,s){let n=this.extractNth(i,t),a=n[0].split("-");if(a.length!==2)throw new TypeError("CronPattern: Syntax error, illegal range: '"+i+"'");let r=parseInt(a[0],10)+e,o=parseInt(a[1],10)+e;if(isNaN(r))throw new TypeError("CronPattern: Syntax error, illegal lower range (NaN)");if(isNaN(o))throw new TypeError("CronPattern: Syntax error, illegal upper range (NaN)");if(r>o)throw new TypeError("CronPattern: From value is larger than to value: '"+i+"'");for(let c=r;c<=o;c++)this.setPart(t,c,n[1]||s)};ze.prototype.handleStepping=function(i,t,e,s){let n=this.extractNth(i,t),a=n[0].split("/");if(a.length!==2)throw new TypeError("CronPattern: Syntax error, illegal stepping: '"+i+"'");let r=0;a[0]!=="*"&&(r=parseInt(a[0],10)+e);let o=parseInt(a[1],10);if(isNaN(o))throw new TypeError("CronPattern: Syntax error, illegal stepping: (NaN)");if(o===0)throw new TypeError("CronPattern: Syntax error, illegal stepping: 0");if(o>this[t].length)throw new TypeError("CronPattern: Syntax error, max steps for part is ("+this[t].length+")");for(let c=r;c<this[t].length;c+=o)this.setPart(t,c,n[1]||s)};ze.prototype.replaceAlphaDays=function(i){return i.replace(/-sun/gi,"-7").replace(/sun/gi,"0").replace(/mon/gi,"1").replace(/tue/gi,"2").replace(/wed/gi,"3").replace(/thu/gi,"4").replace(/fri/gi,"5").replace(/sat/gi,"6")};ze.prototype.replaceAlphaMonths=function(i){return i.replace(/jan/gi,"1").replace(/feb/gi,"2").replace(/mar/gi,"3").replace(/apr/gi,"4").replace(/may/gi,"5").replace(/jun/gi,"6").replace(/jul/gi,"7").replace(/aug/gi,"8").replace(/sep/gi,"9").replace(/oct/gi,"10").replace(/nov/gi,"11").replace(/dec/gi,"12")};ze.prototype.handleNicknames=function(i){let t=i.trim().toLowerCase();return t==="@yearly"||t==="@annually"?"0 0 1 1 *":t==="@monthly"?"0 0 1 * *":t==="@weekly"?"0 0 * * 0":t==="@daily"?"0 0 * * *":t==="@hourly"?"0 * * * *":i};ze.prototype.setNthWeekdayOfMonth=function(i,t){if(t==="L")this.dayOfWeek[i]=this.dayOfWeek[i]|ia;else if(t<6&&t>0)this.dayOfWeek[i]=this.dayOfWeek[i]|Ai[t-1];else if(t===fs)this.dayOfWeek[i]=fs;else throw new TypeError(`CronPattern: nth weekday of of range, should be 1-5 or L. Value: ${t}`)};var Pi=[31,28,31,30,31,30,31,31,30,31,30,31],vt=[["month","year",0],["day","month",-1],["hour","day",0],["minute","hour",0],["second","minute",0]];function _e(i,t){if(this.tz=t,i&&i instanceof Date)if(!isNaN(i))this.fromDate(i);else throw new TypeError("CronDate: Invalid date passed to CronDate constructor");else if(i===void 0)this.fromDate(new Date);else if(i&&typeof i=="string")this.fromString(i);else if(i instanceof _e)this.fromCronDate(i);else throw new TypeError("CronDate: Invalid type ("+typeof i+") passed to CronDate constructor")}_e.prototype.isNthWeekdayOfMonth=function(i,t,e,s){let a=new Date(Date.UTC(i,t,e)).getUTCDay(),r=0;for(let o=1;o<=e;o++)new Date(Date.UTC(i,t,o)).getUTCDay()===a&&r++;if(s&fs&&Ai[r-1]&s)return!0;if(s&ia){let o=new Date(Date.UTC(i,t+1,0)).getUTCDate();for(let c=e+1;c<=o;c++)if(new Date(Date.UTC(i,t,c)).getUTCDay()===a)return!1;return!0}return!1};_e.prototype.fromDate=function(i){if(this.tz!==void 0)if(typeof this.tz=="number")this.ms=i.getUTCMilliseconds(),this.second=i.getUTCSeconds(),this.minute=i.getUTCMinutes()+this.tz,this.hour=i.getUTCHours(),this.day=i.getUTCDate(),this.month=i.getUTCMonth(),this.year=i.getUTCFullYear(),this.apply();else{let t=We.toTZ(i,this.tz);this.ms=i.getMilliseconds(),this.second=t.s,this.minute=t.i,this.hour=t.h,this.day=t.d,this.month=t.m-1,this.year=t.y}else this.ms=i.getMilliseconds(),this.second=i.getSeconds(),this.minute=i.getMinutes(),this.hour=i.getHours(),this.day=i.getDate(),this.month=i.getMonth(),this.year=i.getFullYear()};_e.prototype.fromCronDate=function(i){this.tz=i.tz,this.year=i.year,this.month=i.month,this.day=i.day,this.hour=i.hour,this.minute=i.minute,this.second=i.second,this.ms=i.ms};_e.prototype.apply=function(){if(this.month>11||this.day>Pi[this.month]||this.hour>59||this.minute>59||this.second>59||this.hour<0||this.minute<0||this.second<0){let i=new Date(Date.UTC(this.year,this.month,this.day,this.hour,this.minute,this.second,this.ms));return this.ms=i.getUTCMilliseconds(),this.second=i.getUTCSeconds(),this.minute=i.getUTCMinutes(),this.hour=i.getUTCHours(),this.day=i.getUTCDate(),this.month=i.getUTCMonth(),this.year=i.getUTCFullYear(),!0}else return!1};_e.prototype.fromString=function(i){if(typeof this.tz=="number"){let t=We.fromTZISO(i);this.ms=t.getUTCMilliseconds(),this.second=t.getUTCSeconds(),this.minute=t.getUTCMinutes(),this.hour=t.getUTCHours(),this.day=t.getUTCDate(),this.month=t.getUTCMonth(),this.year=t.getUTCFullYear(),this.apply()}else return this.fromDate(We.fromTZISO(i,this.tz))};_e.prototype.findNext=function(i,t,e,s){let n=this[t],a;e.lastDayOfMonth&&(this.month!==1?a=Pi[this.month]:a=new Date(Date.UTC(this.year,this.month+1,0,0,0,0,0)).getUTCDate());let r=!e.starDOW&&t=="day"?new Date(Date.UTC(this.year,this.month,1,0,0,0,0)).getUTCDay():void 0;for(let o=this[t]+s;o<e[t].length;o++){let c=e[t][o];if(t==="day"&&e.lastDayOfMonth&&o-s==a&&(c=!0),t==="day"&&!e.starDOW){let l=e.dayOfWeek[(r+(o-s-1))%7];if(l&&l&fs)l=this.isNthWeekdayOfMonth(this.year,this.month,o-s,l);else if(l)throw new Error(`CronDate: Invalid value for dayOfWeek encountered. ${l}`);i.legacyMode&&!e.starDOM?c=c||l:c=c&&l}if(c)return this[t]=o-s,n!==this[t]?2:1}return 3};_e.prototype.recurse=function(i,t,e){let s=this.findNext(t,vt[e][0],i,vt[e][2]);if(s>1){let n=e+1;for(;n<vt.length;)this[vt[n][0]]=-vt[n][2],n++;if(s===3)return this[vt[e][1]]++,this[vt[e][0]]=-vt[e][2],this.apply(),this.recurse(i,t,0);if(this.apply())return this.recurse(i,t,e-1)}return e+=1,e>=vt.length?this:this.year>=3e3?null:this.recurse(i,t,e)};_e.prototype.increment=function(i,t,e){return this.second+=t.interval>1&&e?t.interval:1,this.ms=0,this.apply(),this.recurse(i,t,0)};_e.prototype.getDate=function(i){return i||this.tz===void 0?new Date(this.year,this.month,this.day,this.hour,this.minute,this.second,this.ms):typeof this.tz=="number"?new Date(Date.UTC(this.year,this.month,this.day,this.hour,this.minute-this.tz,this.second,this.ms)):We(this.year,this.month+1,this.day,this.hour,this.minute,this.second,this.tz)};_e.prototype.getTime=function(){return this.getDate().getTime()};function sn(i){return Object.prototype.toString.call(i)==="[object Function]"||typeof i=="function"||i instanceof Function}function rl(i){typeof Deno<"u"&&typeof Deno.unrefTimer<"u"?Deno.unrefTimer(i):i&&typeof i.unref<"u"&&i.unref()}var Ei=30*1e3,gs=[];function le(i,t,e){if(!(this instanceof le))return new le(i,t,e);let s,n;if(sn(t))n=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(sn(e))n=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=il(s),this._states={kill:!1,blocking:!1,previousRun:void 0,currentRun:void 0,once:void 0,currentTimeout:void 0,maxRuns:s?s.maxRuns:void 0,paused:s?s.paused:!1,pattern:void 0},i&&(i instanceof Date||typeof i=="string"&&i.indexOf(":")>0)?this._states.once=new _e(i,this.options.timezone||this.options.utcOffset):this._states.pattern=new ze(i,this.options.timezone),this.name){if(gs.find(r=>r.name===this.name))throw new Error("Cron: Tried to initialize new named job '"+this.name+"', but name already taken.");gs.push(this)}return n!==void 0&&(this.fn=n,this.schedule()),this}le.prototype.nextRun=function(i){let t=this._next(i);return t?t.getDate():null};le.prototype.nextRuns=function(i,t){i>this._states.maxRuns&&(i=this._states.maxRuns);let e=[],s=t||this._states.currentRun;for(;i--&&(s=this.nextRun(s));)e.push(s);return e};le.prototype.getPattern=function(){return this._states.pattern?this._states.pattern.pattern:void 0};le.prototype.isRunning=function(){let i=this.nextRun(this._states.currentRun),t=!this._states.paused,e=this.fn!==void 0,s=!this._states.kill;return t&&e&&s&&i!==null};le.prototype.isStopped=function(){return this._states.kill};le.prototype.isBusy=function(){return this._states.blocking};le.prototype.currentRun=function(){return this._states.currentRun?this._states.currentRun.getDate():null};le.prototype.previousRun=function(){return this._states.previousRun?this._states.previousRun.getDate():null};le.prototype.msToNext=function(i){i=i||new Date;let t=this._next(i);return t?t.getTime()-i.getTime():null};le.prototype.stop=function(){this._states.kill=!0,this._states.currentTimeout&&clearTimeout(this._states.currentTimeout);let i=gs.indexOf(this);i>=0&&gs.splice(i,1)};le.prototype.pause=function(){return this._states.paused=!0,!this._states.kill};le.prototype.resume=function(){return this._states.paused=!1,!this._states.kill};le.prototype.schedule=function(i){if(i&&this.fn)throw new Error("Cron: It is not allowed to schedule two functions using the same Croner instance.");i&&(this.fn=i);let t=this.msToNext(),e=this.nextRun(this._states.currentRun);return t==null||isNaN(t)||e===null?this:(t>Ei&&(t=Ei),this._states.currentTimeout=setTimeout(()=>this._checkTrigger(e),t),this._states.currentTimeout&&this.options.unref&&rl(this._states.currentTimeout),this)};le.prototype._trigger=async function(i){if(this._states.blocking=!0,this._states.currentRun=new _e(void 0,this.options.timezone||this.options.utcOffset),this.options.catch)try{await this.fn(this,this.options.context)}catch(t){sn(this.options.catch)&&this.options.catch(t,this)}else await this.fn(this,this.options.context);this._states.previousRun=new _e(i,this.options.timezone||this.options.utcOffset),this._states.blocking=!1};le.prototype.trigger=async function(){await this._trigger()};le.prototype._checkTrigger=function(i){let t=new Date,e=!this._states.paused&&t.getTime()>=i,s=this._states.blocking&&this.options.protect;e&&!s?(this._states.maxRuns--,this._trigger()):e&&s&&sn(this.options.protect)&&setTimeout(()=>this.options.protect(this),0),this.schedule()};le.prototype._next=function(i){let t=!!(i||this._states.currentRun),e=!1;!i&&this.options.startAt&&this.options.interval&&([i,t]=this._calculatePreviousRun(i,t),e=!i),i=new _e(i,this.options.timezone||this.options.utcOffset),this.options.startAt&&i&&i.getTime()<this.options.startAt.getTime()&&(i=this.options.startAt);let s=this._states.once||new _e(i,this.options.timezone||this.options.utcOffset);return!e&&s!==this._states.once&&(s=s.increment(this._states.pattern,this.options,t)),this._states.once&&this._states.once.getTime()<=i.getTime()||s===null||this._states.maxRuns<=0||this._states.kill||this.options.stopAt&&s.getTime()>=this.options.stopAt.getTime()?null:s};le.prototype._calculatePreviousRun=function(i,t){let e=new _e(void 0,this.options.timezone||this.options.utcOffset);if(this.options.startAt.getTime()<=e.getTime()){i=this.options.startAt;let s=i.getTime()+this.options.interval*1e3;for(;s<=e.getTime();)i=new _e(i,this.options.timezone||this.options.utcOffset).increment(this._states.pattern,this.options,!0),s=i.getTime()+this.options.interval*1e3;t=!0}return[i,t]};le.Cron=le;le.scheduledJobs=gs;var er=require("obsidian");var Gi=require("crypto");var Xe=require("fs"),ra=require("path"),ol="mcp__remember";function nn(i,t,e={}){let s=t.permissionRules.allow.length>0||t.permissionRules.deny.length>0,n=t.permissionMode?.trim(),a=!!n&&n!=="default",r=(t.mcpServers?.length??0)>0,o=e.allowRememberTool===!0;if(!(s||a||r||o))return null;let l=(0,ra.join)(i,".claude"),d=(0,ra.join)(l,"settings.local.json");(0,Xe.existsSync)(l)||(0,Xe.mkdirSync)(l,{recursive:!0});let h=null;if((0,Xe.existsSync)(d))try{h=(0,Xe.readFileSync)(d,"utf-8")}catch{h=null}let u={};if(a&&(u.defaultMode=n),s||r||o){let p=[...t.permissionRules.allow];if(r)for(let m of t.mcpServers??[]){let f=m.replace(/[\s.]+/g,"_");p.push(`mcp__${f}`)}o&&p.push(ol),u.permissions={allow:p,deny:t.permissionRules.deny}}return(0,Xe.writeFileSync)(d,JSON.stringify(u,null,2)+`
|
|
11833
|
+
`,"utf-8"),{path:d,backupContent:h}}function Nt(i){if(i)try{i.backupContent!==null?(0,Xe.writeFileSync)(i.path,i.backupContent,"utf-8"):(0,Xe.existsSync)(i.path)&&(0,Xe.unlinkSync)(i.path)}catch{}}function Ri(i){if(typeof i=="string")return i;if(Array.isArray(i))return i.map(e=>typeof e=="string"?e:e&&typeof e=="object"&&"text"in e&&typeof e.text=="string"?e.text:"").filter(Boolean).join(`
|
|
11834
|
+
`);if(i&&typeof i=="object"){for(let t of["output","result","text","message"])if(t in i)return Ri(i[t])}}function oa(i,t=[]){if(Array.isArray(i)){for(let r of i)oa(r,t);return t}if(!i||typeof i!="object")return t;let e=i,s=typeof e.tool_name=="string"&&e.tool_name||typeof e.tool=="string"&&e.tool||typeof e.name=="string"&&e.name,n=typeof e.command=="string"?e.command:typeof e.input=="string"?e.input:typeof e.cmd=="string"?e.cmd:void 0,a=typeof e.reason=="string"?e.reason:void 0;s&&["tool_use","tool","name","tool_name"].some(r=>r in e)&&t.push({tool:s,command:n,reason:a});for(let r of Object.values(e))oa(r,t);return t}function Di(i){if(!i||typeof i!="object")return;let t=i,e=t.usage;if(e&&typeof e=="object"){let n=typeof e.input_tokens=="number"?e.input_tokens:0,a=typeof e.output_tokens=="number"?e.output_tokens:0,r=typeof e.cache_creation_input_tokens=="number"?e.cache_creation_input_tokens:0,o=typeof e.cache_read_input_tokens=="number"?e.cache_read_input_tokens:0,c=n+a+r+o;if(c>0)return c}let s=t.modelUsage;if(s&&typeof s=="object"){let n=0;for(let a of Object.values(s)){if(!a||typeof a!="object")continue;let r=a;n+=typeof r.inputTokens=="number"?r.inputTokens:0,n+=typeof r.outputTokens=="number"?r.outputTokens:0,n+=typeof r.cacheReadInputTokens=="number"?r.cacheReadInputTokens:0,n+=typeof r.cacheCreationInputTokens=="number"?r.cacheCreationInputTokens:0}if(n>0)return n}for(let n of["tokens_used","total_tokens","totalTokens"])if(typeof t[n]=="number")return t[n];for(let n of Object.values(t)){let a=Di(n);if(typeof a=="number")return a}}function Ii(i){if(!i||typeof i!="object")return;let t=i;if(typeof t.total_cost_usd=="number")return t.total_cost_usd;for(let e of Object.values(t)){let s=Ii(e);if(typeof s=="number")return s}}function la(i){if(!i||typeof i!="object")return;let t=i;if(t.type==="result"&&typeof t.result=="string"&&t.result.trim())return t.result}function an(i){if(!i||typeof i!="object")return;let t=i;if(t.modelUsage&&typeof t.modelUsage=="object"){let s=Object.keys(t.modelUsage);if(s.length>0&&s[0])return s[0]}let e=t.message;if(e&&typeof e.model=="string"&&e.model)return e.model;if(typeof t.model=="string"&&t.model)return t.model;for(let s of Object.values(t)){let n=an(s);if(n)return n}}function ll(i){return/^gpt-|codex/i.test(i.trim())}var Mi={id:"claude-code",label:"Claude Code",cliPath(i){return i.claudeCliPath},buildExec(i){let t=["-p",i.prompt,"--output-format",i.streaming?"stream-json":"json"];i.streaming&&t.push("--verbose");let e=i.modelSource==="settings"&&ll(i.model);i.model&&!e&&t.push("--model",i.model),i.effort&&t.push("--effort",i.effort);let s=i.agent.permissionMode?.trim();return s&&s!=="default"?t.push("--permission-mode",s):t.push("--permission-mode","bypassPermissions"),Promise.resolve({cliPath:i.settings.claudeCliPath,args:t})},parseExecOutput(i,t,e){let s=i.trim(),n;if(e){let c=oe(s);for(let l=c.length-1;l>=0;l--){let d=c[l]?.trim();if(d)try{let h=JSON.parse(d);if(h&&typeof h=="object"){n=h;break}}catch{}}}else if(s.startsWith("{")||s.startsWith("["))try{n=JSON.parse(s)}catch{n=void 0}let a=Ri(n)??"";if(!a&&e){let c=[];for(let l of oe(s)){let d=l.trim();if(d)try{let h=JSON.parse(d);if(h.type==="assistant"&&h.message?.content)for(let u of h.message.content)u.type==="text"&&u.text&&c.push(u.text);else h.type==="result"&&typeof h.result=="string"&&c.push(h.result)}catch{}}a=c.join(`
|
|
11835
|
+
`).trim()}a||(a=t.trim()||"(no output)");let r=an(n),o=la(n);if((!r||!o)&&e)for(let c of oe(s)){let l=c.trim();if(l)try{let d=JSON.parse(l);if(!r){let h=an(d);h&&(r=h)}if(!o){let h=la(d);h&&(o=h)}if(r&&o)break}catch{}}return{outputText:a,finalResult:o,tokensUsed:Di(n),costUsd:Ii(n),toolsUsed:oa(n),concreteModel:r,rawJson:n}},extractStreamChunk(i){let t=i.trim();if(!t)return null;let e;try{e=JSON.parse(t)}catch{return null}let s=e.type;if(s==="assistant"){let n=e.message;if(n?.content&&Array.isArray(n.content)){let a=[];for(let r of n.content)if(r.type==="text"&&typeof r.text=="string")a.push(r.text);else if(r.type==="tool_use"){let o=String(r.name??"tool"),c=r.input,l=c?.command??c?.content??"";a.push(`
|
|
11836
|
+
\u25B8 ${o}${l?`: ${String(l).slice(0,200)}`:""}
|
|
11837
|
+
`)}if(a.length>0)return a.join("")}}if(s==="result"){let n=typeof e.result=="string"?e.result:null;if(n)return`
|
|
11838
|
+
${n}`}return null},setupPermissions(i,t,e,s){let n=nn(i,t,{allowRememberTool:s?.allowRememberTool});return n?Promise.resolve({restore:()=>Nt(n)}):Promise.resolve(null)}};var ie=require("fs"),ha=require("crypto"),vs=require("os"),Qe=require("path");var cl=/^[A-Z][A-Za-z]*$/;function dl(i){let t=i.trim();if(!t)return{error:"empty Bash() matches every command \u2014 use the read-only sandbox instead"};let e=t.split(/\s+/).filter(Boolean),s=[];for(let n=0;n<e.length;n++){let a=e[n],r=n===e.length-1;if(a==="*"){if(r)continue;return{error:"a wildcard in the middle of a command can't be expressed as a prefix rule"}}if(a.includes("*"))return{error:"embedded wildcards aren't supported (execpolicy matches argv prefixes only)"};s.push(a)}return s.length===0?{error:"a bare wildcard matches every command \u2014 use the read-only sandbox instead"}:{tokens:s}}function hl(i,t){return`prefix_rule(pattern=[${i.map(s=>JSON.stringify(s)).join(", ")}], decision="${t}", justification="agent-fleet")`}function ul(i){let t=[],e=[],s=(a,r)=>{let o=a.trim();if(!o||o.startsWith("mcp__"))return;let c=o.match(/^Bash\((.*)\)$/s);if(!c){e.push({rule:o,reason:cl.test(o)?"tool-name rules are governed by the sandbox, not execpolicy":"not a Bash(...) command pattern"});return}let l=dl(c[1]);if("error"in l){e.push({rule:o,reason:l.error});return}t.push(hl(l.tokens,r))};for(let a of i.permissionRules.deny)s(a,"forbidden");for(let a of i.permissionRules.allow)s(a,"allow");return t.length===0?{rulesText:null,dropped:e,emitted:0}:{rulesText:`${`# Generated by Agent Fleet for agent "${i.name}". Do not edit \u2014 regenerated each run.
|
|
11839
|
+
# Source: this agent's permissionRules (allow/deny), translated to Codex execpolicy.
|
|
11840
|
+
`}${t.join(`
|
|
11841
|
+
`)}
|
|
11842
|
+
`,dropped:e,emitted:t.length}}function pl(){let i=process.env.CODEX_HOME?.trim();return i||(0,Qe.join)((0,vs.homedir)(),".codex")}var Fi=(0,Qe.join)((0,vs.tmpdir)(),"agent-fleet-codex");function ml(i){let t=i.name.replace(/[^A-Za-z0-9_-]+/g,"-").replace(/^-+|-+$/g,"")||"agent",e=(0,ha.createHash)("sha1").update(i.filePath).digest("hex").slice(0,8);return(0,Qe.join)(Fi,`${t}-${e}`)}function fl(i,t){let e=`${i}.${process.pid}.${Date.now()}.tmp`;(0,ie.writeFileSync)(e,t,"utf-8"),(0,ie.renameSync)(e,i)}function gl(i,t){try{let e=(0,ie.lstatSync)(t);if(e.isSymbolicLink()){try{if((0,ie.readlinkSync)(t)===i)return}catch{}(0,ie.unlinkSync)(t)}else e.isDirectory()?(0,ie.rmSync)(t,{recursive:!0,force:!0}):(0,ie.unlinkSync)(t)}catch{}(0,ie.symlinkSync)(i,t)}function yl(i,t,e=pl()){if(!(0,ie.existsSync)(e))return null;let s=ml(i);(0,ie.mkdirSync)(s,{recursive:!0});for(let o of(0,ie.readdirSync)(e))o!=="rules"&&gl((0,Qe.join)(e,o),(0,Qe.join)(s,o));let n=(0,Qe.join)(s,"rules");(0,ie.mkdirSync)(n,{recursive:!0});let a=(0,Qe.join)(e,"rules");if((0,ie.existsSync)(a)){for(let o of(0,ie.readdirSync)(a))if(o.endsWith(".rules"))try{(0,ie.copyFileSync)((0,Qe.join)(a,o),(0,Qe.join)(n,`user-${o}`))}catch{}}let r=(0,Qe.join)(n,"agent-fleet.rules");return fl(r,t),{codexHome:s,rulesPath:r}}function Oi(){try{(0,ie.rmSync)(Fi,{recursive:!0,force:!0})}catch{}}var ca=new Map,da=new Map;function Ni(){ca.clear(),da.clear()}function Bi(i,t,e){return new Promise(s=>{let n=!1,a=r=>{n||(n=!0,s(r))};try{let r=qe(i,["execpolicy","check","--rules",t,...e]),o=setTimeout(()=>{try{r.kill()}catch{}a(null)},8e3);r.on("error",()=>{clearTimeout(o),a(null)}),r.on("close",c=>{clearTimeout(o),a(c)})}catch{a(null)}})}function vl(i){let t=ca.get(i);if(t)return t;let e=(async()=>{let s=null;try{s=(0,ie.mkdtempSync)((0,Qe.join)((0,vs.tmpdir)(),"af-codex-probe-"));let n=(0,Qe.join)(s,"probe.rules");return(0,ie.writeFileSync)(n,`prefix_rule(pattern=["ls"], decision="allow")
|
|
11843
|
+
`,"utf-8"),await Bi(i,n,["ls"])===0}catch{return!1}finally{if(s)try{(0,ie.rmSync)(s,{recursive:!0,force:!0})}catch{}}})();return ca.set(i,e),e}async function bl(i,t,e){let s=`${i}:${(0,ha.createHash)("sha1").update(e).digest("hex")}`,n=da.get(s);if(n!==void 0)return n;let r=await Bi(i,t,["true"])===0;return da.set(s,r),r}var Li=new Set;function ys(i,t){Li.has(i)||(Li.add(i),console.warn(`Agent Fleet: ${t}`))}async function Ui(i,t){if(!(i.permissionRules.allow.length>0||i.permissionRules.deny.length>0))return null;let{rulesText:s,dropped:n}=ul(i);if(n.length>0&&ys(`dropped:${i.name}:${n.map(l=>l.rule).join("|")}`,`agent "${i.name}": ${n.length} permission rule(s) can't be enforced by Codex and were ignored \u2014 ${n.map(l=>`"${l.rule}" (${l.reason})`).join("; ")}. File/network access is governed by the sandbox (Permission Mode).`),!s)return null;let a=t.codexCliPath;if(!await vl(a))return ys(`unsupported:${a}`,`this Codex build doesn't support execpolicy rules; agent "${i.name}" runs with sandbox-only enforcement. Update Codex to enforce command allow/deny rules.`),null;let o;try{o=yl(i,s)}catch(l){return ys(`overlay-fail:${i.name}`,`couldn't build the Codex permission overlay for agent "${i.name}" (${l instanceof Error?l.message:String(l)}); falling back to sandbox-only (symlinks may be unavailable on this platform).`),null}return o?await bl(a,o.rulesPath,s)?{restore:()=>{},env:{CODEX_HOME:o.codexHome}}:(ys(`invalid-rules:${i.name}`,`generated Codex rules for agent "${i.name}" failed validation; falling back to sandbox-only.`),null):(ys("no-codex-home",`Codex home (~/.codex) not found; agent "${i.name}" runs with sandbox-only enforcement. Run \`codex login\` first.`),null)}function wl(i){let t=i.trim();return/^(opus|sonnet|haiku|opusplan)$/i.test(t)||/claude|anthropic/i.test(t)}function kl(i){let t=i.trim().toLowerCase();return t?t==="max"?"xhigh":["low","medium","high","xhigh"].includes(t)?t:"":""}function xl(i){switch((i??"").trim()){case"plan":case"read-only":return["--sandbox","read-only"];case"acceptEdits":case"default":case"workspace-write":return["--sandbox","workspace-write"];case"danger-full-access":return["--sandbox","danger-full-access"];default:return["--dangerously-bypass-approvals-and-sandbox"]}}function Sl(i){return/^[A-Za-z0-9_-]+$/.test(i)?i:`"${i.replace(/"/g,'\\"')}"`}function Cl(i,t){let e=["exec","--json","--skip-git-repo-check"],s=i.modelSource==="settings"&&wl(i.model);i.model&&!s&&e.push("-m",i.model);let n=kl(i.effort);n&&e.push("-c",`model_reasoning_effort="${n}"`),e.push(...xl(i.agent.permissionMode));let a=i.agent.mcpServers??[];if(a.length>0&&t&&t.length>0){let r=new Set(a.map(o=>o.trim().toLowerCase()));for(let o of t){let c=r.has(o.trim().toLowerCase());e.push("-c",`mcp_servers.${Sl(o)}.enabled=${c}`)}}return i.resumeSessionId&&e.push("resume",i.resumeSessionId),e.push("-"),{args:e,stdinPayload:i.prompt}}var Tl=5*6e4,bs=null;async function _l(i){if(bs&&bs.cliPath===i&&Date.now()-bs.at<Tl)return bs.names;let t=await new Promise(e=>{let s=!1,n=a=>{s||(s=!0,e(a))};try{let a=qe(i,["mcp","list","--json"]),r="",o=setTimeout(()=>{try{a.kill()}catch{}n(null)},1e4);a.stdout?.on("data",c=>{r+=c.toString()}),a.on("error",()=>{clearTimeout(o),n(null)}),a.on("close",c=>{if(clearTimeout(o),c!==0)return n(null);n(El(r))})}catch{n(null)}});return bs={at:Date.now(),cliPath:i,names:t},t}function El(i){try{let t=JSON.parse(i.trim()),e=s=>s.map(n=>n&&typeof n=="object"&&typeof n.name=="string"?n.name:typeof n=="string"?n:"").filter(Boolean);if(Array.isArray(t))return e(t);if(t&&typeof t=="object"){let s=t;return Array.isArray(s.servers)?e(s.servers):Object.keys(s)}}catch{}return null}function $i(i){let t=i.trim();if(!t)return null;try{let e=JSON.parse(t);return e&&typeof e=="object"&&!Array.isArray(e)?e:null}catch{return null}}function rn(i){let t=i.item;return t&&typeof t=="object"?t:null}function ua(i){let t=typeof i.type=="string"?i.type:"";if(t==="command_execution")return{tool:"shell",command:typeof i.command=="string"?i.command:void 0};if(t==="mcp_tool_call"){let e=typeof i.server=="string"?i.server:"mcp",s=typeof i.tool=="string"?i.tool:"tool";return{tool:`mcp__${e}__${s}`}}return t==="web_search"?{tool:"web_search",command:typeof i.query=="string"?i.query:void 0}:t==="file_change"?{tool:"file_change",command:(Array.isArray(i.changes)?i.changes:[]).map(n=>`${typeof n.kind=="string"?n.kind:"update"} ${typeof n.path=="string"?n.path:""}`.trim()).filter(Boolean).join(", ")||void 0}:null}function ji(i){let t=i.usage;if(!t||typeof t!="object")return 0;let e=typeof t.input_tokens=="number"?t.input_tokens:0,s=typeof t.output_tokens=="number"?t.output_tokens:0;return e+s}function Al(i){let t=i.usage;return!t||typeof t!="object"?0:typeof t.input_tokens=="number"?t.input_tokens:0}function pa(){return{emittedTextLengths:new Map}}function Wi(i,t){let e=typeof i.type=="string"?i.type:"",s=[];if(e==="thread.started"&&typeof i.thread_id=="string"&&i.thread_id)return s.push({kind:"session",sessionId:i.thread_id}),s;if(e==="turn.completed")return s.push({kind:"usage",contextTokens:Al(i),totalTokens:ji(i)}),s;if(e==="turn.failed"){let n=i.error,a=n&&typeof n.message=="string"?n.message:"turn failed";return s.push({kind:"turn-failed",message:a}),s}if(e==="error"){let n=typeof i.message=="string"?i.message:"stream error";return s.push({kind:"error",message:n}),s}if(e==="item.started"||e==="item.updated"||e==="item.completed"){let n=rn(i);if(!n)return s;let a=typeof n.type=="string"?n.type:"";if(a==="agent_message"){let r=typeof n.text=="string"?n.text:"",o=typeof n.id=="string"?n.id:"__single__",c=t.emittedTextLengths.get(o)??0;return r.length>c&&(s.push({kind:"text",text:r.slice(c)}),t.emittedTextLengths.set(o,r.length)),s}if(a==="error"){let r=typeof n.message=="string"?n.message:"item error";return s.push({kind:"error",message:r}),s}if(e==="item.started"){let r=ua(n);r&&s.push({kind:"tool",toolName:r.tool,command:r.command})}return s}return s}var ws={id:"codex",label:"Codex",cliPath(i){return i.codexCliPath},async buildExec(i){let e=(i.agent.mcpServers?.length??0)>0?await _l(i.settings.codexCliPath):null,{args:s,stdinPayload:n}=Cl(i,e);return{cliPath:i.settings.codexCliPath,args:s,stdinPayload:n}},parseExecOutput(i,t,e){let s=[],n=[],a=[],r=0,o,c;for(let h of oe(i)){let u=$i(h);if(!u)continue;let p=typeof u.type=="string"?u.type:"";if(p==="thread.started"&&typeof u.thread_id=="string")o=u.thread_id;else if(p==="turn.completed")r+=ji(u),c=u;else if(p==="turn.failed"){let m=u.error;a.push(m&&typeof m.message=="string"?m.message:"turn failed")}else if(p==="error")a.push(typeof u.message=="string"?u.message:"stream error");else if(p==="item.completed"){let m=rn(u);if(!m)continue;let f=typeof m.type=="string"?m.type:"";if(f==="agent_message"&&typeof m.text=="string"&&m.text.trim())s.push(m.text);else if(f==="error"&&typeof m.message=="string")a.push(m.message);else{let y=ua(m);y&&n.push(y)}}}let l=s.join(`
|
|
11844
|
+
|
|
11845
|
+
`).trim();l||(l=a.join(`
|
|
11846
|
+
`).trim()),l||(l=t.trim()||"(no output)");let d=s[s.length-1];return{outputText:l,finalResult:d?.trim()?d:void 0,tokensUsed:r>0?r:void 0,costUsd:void 0,toolsUsed:n,concreteModel:void 0,rawJson:c,sessionId:o}},extractStreamChunk(i){let t=$i(i);if(!t)return null;let e=typeof t.type=="string"?t.type:"";if(e==="item.completed"){let s=rn(t);return s&&s.type==="agent_message"&&typeof s.text=="string"&&s.text.trim()?s.text:null}if(e==="item.started"){let s=rn(t);if(!s)return null;let n=ua(s);if(n){let a=n.command?`: ${n.command.slice(0,200)}`:"";return`
|
|
11847
|
+
\u25B8 ${n.tool}${a}
|
|
11848
|
+
`}return null}if(e==="turn.failed"){let s=t.error;return`
|
|
11849
|
+
\u2716 ${s&&typeof s.message=="string"?s.message:"turn failed"}
|
|
11850
|
+
`}return null},setupPermissions(i,t,e,s){return Ui(t,e)}};function Bt(i){let t=(i??"").trim().toLowerCase();return t==="codex"||t==="openai-codex"?"codex":"claude-code"}function Hi(i){return Bt(i)==="codex"?ws:Mi}var qi=new Set(["","default","subscription"]);function Ut(i,t,e){let s=ma(i?.model);if(s)return{value:fa(s),source:"task"};let n=ma(t.model);if(n)return{value:fa(n),source:"agent"};let a=ma(e.defaultModel);return a?{value:fa(a),source:"settings"}:{value:"",source:"cli-default"}}function ks(i){return!qi.has(i.trim())}function ma(i){if(!i)return"";let t=i.trim();return t||""}function fa(i){return qi.has(i)?"":i}function on(i,t){let e=i.wikiReferences;if(!e||e.length===0)return null;let s=[];for(let a of e){let r=t.getAgentByName(a.agent);if(!r||!r.wikiKeeper)continue;let o=r.wikiKeeper,c=o.scopeRoot?`${o.scopeRoot.replace(/\/+$/,"")}/`:"";s.push({agentName:r.name,scopeRoot:o.scopeRoot||"(whole vault)",topicsPath:`${c}${o.topicsRoot}`,inboxPath:`${c}${o.inboxPath}`,indexPath:`${c}${o.indexPath}`})}if(s.length===0)return null;let n=["## Wiki Access"];n.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."),n.push("");for(let a of s)n.push(`### Wiki: \`${a.agentName}\``),n.push(`- scope root: \`${a.scopeRoot}\``),n.push(`- topics: \`${a.topicsPath}/\``),n.push(`- index: \`${a.indexPath}\``),n.push(`- inbox: \`${a.inboxPath}/\``),n.push("");return n.push("### Rules"),n.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."),n.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."),n.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."),n.push("- **When the question spans multiple wikis**, be explicit about which scope each cited page belongs to."),n.join(`
|
|
11851
|
+
`)}var pt=require("fs"),ln=require("path");var ga="remember",mh=`mcp__${ga}`,Pl=String.raw`
|
|
11852
|
+
const fs = require("fs");
|
|
11853
|
+
const path = require("path");
|
|
11854
|
+
const PENDING_DIR = process.env.AF_PENDING_DIR;
|
|
11855
|
+
const SOURCE = process.env.AF_SOURCE || "mcp";
|
|
11856
|
+
let seq = 0;
|
|
11857
|
+
|
|
11858
|
+
function send(msg) { process.stdout.write(JSON.stringify(msg) + "\n"); }
|
|
11859
|
+
function ok(id, text) { send({ jsonrpc: "2.0", id, result: { content: [{ type: "text", text }] } }); }
|
|
11860
|
+
function toolError(id, text) { send({ jsonrpc: "2.0", id, result: { isError: true, content: [{ type: "text", text }] } }); }
|
|
11861
|
+
|
|
11862
|
+
let buf = "";
|
|
11863
|
+
process.stdin.on("data", (chunk) => {
|
|
11864
|
+
buf += chunk.toString();
|
|
11865
|
+
let nl;
|
|
11866
|
+
while ((nl = buf.indexOf("\n")) !== -1) {
|
|
11867
|
+
const line = buf.slice(0, nl).trim();
|
|
11868
|
+
buf = buf.slice(nl + 1);
|
|
11869
|
+
if (!line) continue;
|
|
11870
|
+
let req;
|
|
11871
|
+
try { req = JSON.parse(line); } catch { continue; }
|
|
11872
|
+
handle(req);
|
|
11873
|
+
}
|
|
11874
|
+
});
|
|
11875
|
+
|
|
11876
|
+
function handle(req) {
|
|
11877
|
+
const id = req.id;
|
|
11878
|
+
const method = req.method;
|
|
11879
|
+
const params = req.params || {};
|
|
11880
|
+
if (method === "initialize") {
|
|
11881
|
+
send({ jsonrpc: "2.0", id, result: {
|
|
11882
|
+
protocolVersion: params.protocolVersion || "2025-06-18",
|
|
11883
|
+
capabilities: { tools: {} },
|
|
11884
|
+
serverInfo: { name: "af-remember", version: "1.0.0" },
|
|
11885
|
+
}});
|
|
11886
|
+
return;
|
|
11887
|
+
}
|
|
11888
|
+
if (method === "notifications/initialized") return;
|
|
11889
|
+
if (method === "tools/list") {
|
|
11890
|
+
send({ jsonrpc: "2.0", id, result: { tools: [{
|
|
11891
|
+
name: "remember",
|
|
11892
|
+
description: "Persist a durable fact about the user or how to do your work better to long-term memory. Use for standing preferences (set pin=true) and reusable procedures — not transient task details.",
|
|
11893
|
+
inputSchema: {
|
|
11894
|
+
type: "object",
|
|
11895
|
+
properties: {
|
|
11896
|
+
fact: { type: "string", description: "One concise, durable fact to remember." },
|
|
11897
|
+
pin: { type: "boolean", description: "Mark as a standing preference, never summarized away." },
|
|
11898
|
+
section: { type: "string", enum: ["Preferences", "Procedures", "Observations"], description: "Optional category." },
|
|
11899
|
+
},
|
|
11900
|
+
required: ["fact"],
|
|
11901
|
+
},
|
|
11902
|
+
}]}});
|
|
11903
|
+
return;
|
|
11904
|
+
}
|
|
11905
|
+
if (method === "tools/call") {
|
|
11906
|
+
const name = params.name;
|
|
11907
|
+
const args = params.arguments || {};
|
|
11908
|
+
if (name !== "remember") {
|
|
11909
|
+
send({ jsonrpc: "2.0", id, error: { code: -32602, message: "unknown tool: " + name } });
|
|
11910
|
+
return;
|
|
11911
|
+
}
|
|
11912
|
+
// Tool-level failures are returned as isError results (not protocol errors)
|
|
11913
|
+
// so the model can read the reason and retry, rather than the CLI treating
|
|
11914
|
+
// it as a hard tool fault.
|
|
11915
|
+
if (!args.fact || typeof args.fact !== "string") { toolError(id, "Missing required 'fact'."); return; }
|
|
11916
|
+
if (!PENDING_DIR) { toolError(id, "Memory is unavailable for this run."); return; }
|
|
11917
|
+
try {
|
|
11918
|
+
fs.mkdirSync(PENDING_DIR, { recursive: true });
|
|
11919
|
+
const rec = {
|
|
11920
|
+
text: String(args.fact),
|
|
11921
|
+
pinned: !!args.pin,
|
|
11922
|
+
section: typeof args.section === "string" ? args.section : undefined,
|
|
11923
|
+
source: SOURCE,
|
|
11924
|
+
ts: new Date().toISOString(),
|
|
11925
|
+
};
|
|
11926
|
+
const fname = Date.now() + "-" + process.pid + "-" + (seq++) + ".json";
|
|
11927
|
+
// Write to a temp name then rename so a concurrent drain never reads a
|
|
11928
|
+
// half-written file (rename is atomic on the same filesystem).
|
|
11929
|
+
const finalPath = path.join(PENDING_DIR, fname);
|
|
11930
|
+
const tmpPath = finalPath + ".tmp";
|
|
11931
|
+
fs.writeFileSync(tmpPath, JSON.stringify(rec) + "\n");
|
|
11932
|
+
fs.renameSync(tmpPath, finalPath);
|
|
11933
|
+
ok(id, "Remembered.");
|
|
11934
|
+
} catch (e) {
|
|
11935
|
+
toolError(id, "Failed to remember: " + String(e));
|
|
11936
|
+
}
|
|
11937
|
+
return;
|
|
11938
|
+
}
|
|
11939
|
+
if (typeof id !== "undefined") {
|
|
11940
|
+
send({ jsonrpc: "2.0", id, error: { code: -32601, message: "method not found: " + method } });
|
|
11941
|
+
}
|
|
11942
|
+
}
|
|
11943
|
+
`,Rl=0;function xs(i,t,e){if(!t)return null;let s=(0,ln.join)(i,".claude");(0,pt.existsSync)(s)||(0,pt.mkdirSync)(s,{recursive:!0});let n=`${process.pid}-${Date.now()}-${Rl++}`,a=(0,ln.join)(s,`af-remember-mcp.${n}.cjs`),r=(0,ln.join)(s,`af-remember-mcp.${n}.json`);return(0,pt.writeFileSync)(a,Pl,"utf-8"),(0,pt.writeFileSync)(r,Dl(a,t,e),"utf-8"),{mcpConfigPath:r,scriptPath:a,pendingDir:t,source:e,tempFiles:[a,r]}}function Ss(i,t){if(Bt(t)==="codex"){let e=ga;return["-c",`mcp_servers.${e}.command="node"`,"-c",`mcp_servers.${e}.args=${JSON.stringify([i.scriptPath])}`,"-c",`mcp_servers.${e}.env.AF_PENDING_DIR=${JSON.stringify(i.pendingDir)}`,"-c",`mcp_servers.${e}.env.AF_SOURCE=${JSON.stringify(i.source)}`]}return["--mcp-config",i.mcpConfigPath]}function At(i){if(i)for(let t of i.tempFiles)try{(0,pt.existsSync)(t)&&(0,pt.unlinkSync)(t)}catch{}}function Dl(i,t,e){return JSON.stringify({mcpServers:{[ga]:{command:"node",args:[i],env:{AF_PENDING_DIR:t,AF_SOURCE:e}}}},null,2)}function zi(i){let t=[];for(let e of i){let s=e.trim();if(s)try{let n=JSON.parse(s),a=typeof n.text=="string"?n.text.trim():"";if(!a)continue;let r=n.section==="Preferences"||n.section==="Procedures"||n.section==="Observations"?n.section:void 0;t.push({text:a,pinned:n.pinned===!0,section:r,source:typeof n.source=="string"?n.source:"mcp"})}catch{}}return t}var cn=class{constructor(t,e){this.settings=t;this.repository=e}runningProcesses=new Map;abortAgent(t){let e=this.runningProcesses.get(t);return e?(e.kill(),this.runningProcesses.delete(t),!0):!1}async buildPrompt(t,e,s,n=t.memory){let a=[t.body.trim()];for(let o of t.skills){let c=this.repository.getSkillByName(o);if(c){let l=[c.body.trim()];c.toolsBody.trim()&&l.push(`### Tools
|
|
11944
|
+
${c.toolsBody.trim()}`),c.referencesBody.trim()&&l.push(`### References
|
|
11945
|
+
${c.referencesBody.trim()}`),c.examplesBody.trim()&&l.push(`### Examples
|
|
11946
|
+
${c.examplesBody.trim()}`),a.push(`## Skill: ${c.name}
|
|
11782
11947
|
${l.join(`
|
|
11783
11948
|
|
|
11784
11949
|
`)}`)}}if(t.skillsBody.trim()&&a.push(`## Agent Skills
|
|
11785
11950
|
${t.skillsBody.trim()}`),t.contextBody.trim()&&a.push(`## Agent Context
|
|
11786
|
-
${t.contextBody.trim()}`),
|
|
11787
|
-
${i.body.trim()}`)}let n=As(t,this.repository);return n&&a.push(n),a.push(`## Task
|
|
11951
|
+
${t.contextBody.trim()}`),n){let o=await this.repository.readWorkingMemory(t.name),c=Ys(t,o);c&&a.push(c)}let r=on(t,this.repository);return r&&a.push(r),a.push(`## Task
|
|
11788
11952
|
${(s??e.body).trim()}`),a.filter(Boolean).join(`
|
|
11789
11953
|
|
|
11790
|
-
`)}async execute(t,e,s,a){let
|
|
11791
|
-
`).trim()}A||(A=v.trim()||"(no output)");let S=Sa(R),L=Sn(R),E=Tn(R),N=Ta(R),I=wn(R);if((!N||!I)&&l)for(let q of me(_)){let F=q.trim();if(F)try{let $=JSON.parse(F);if(!N){let W=Ta($);W&&(N=W)}if(!I){let W=wn($);W&&(I=W)}if(N&&I)break}catch{}}m({runId:i,prompt:n,exitCode:T,durationSeconds:Math.max(1,Math.round((Date.now()-p)/1e3)),stdout:b,stderr:v,outputText:A,rawJson:R,tokensUsed:L,costUsd:E,toolsUsed:S,timedOut:y,resolvedModel:o.value,modelSource:o.source,concreteModel:N,finalResult:I})})})}finally{f()}}};var as=Ye(require("crypto")),_n=Ye(require("https")),Ps=Ye(require("http")),We=Ye(require("fs")),Et=Ye(require("path"));var Rs=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=Et.join(Qt(),"settings.local.json"),a={};try{let o=We.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),We.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=as.randomBytes(32).toString("base64url"),u=as.createHash("sha256").update(h).digest("base64url"),p=as.randomBytes(16).toString("hex"),f=new URLSearchParams({response_type:"code",client_id:d,code_challenge:u,code_challenge_method:"S256",redirect_uri:c,state:p,resource:o}),m=i??n.scopes_supported;m?.length&&f.set("scope",m.join(" "));let g=new URL(n.authorization_endpoint);for(let[y,x]of f)g.searchParams.set(y,x);let k=g.toString();console.log("McpManager: OAuth DCR client_id:",d),console.log("McpManager: OAuth auth URL:",k),sn(k);let b=await l.waitForCode(p,18e4),v=await this.exchangeOAuthCode(n.token_endpoint,b,c,d,h);this.injectTokenIntoClaudeConfig(t,v.access_token),this.authManager&&this.authManager.storeOAuthToken(t,{accessToken:v.access_token,refreshToken:v.refresh_token,expiresAt:v.expires_in?Date.now()+v.expires_in*1e3:void 0,tokenEndpoint:n.token_endpoint,clientId:d,resource:o}),this.clearNeedsAuthCache(t),this.enableServerInClaudeConfig(t),this.invalidateCache()}finally{l.close()}}async extractTokenFromCli(t){try{return(await this.runCliArgs(["mcp","get",t])).match(/Authorization:\s*Bearer\s+(\S+)/)?.[1]}catch{return}}async refreshProbeTokens(){if(!this.authManager)return;let t=this.authManager.getExpiringTokens();for(let[s,a]of t)if(a.refreshToken)try{let n=new URLSearchParams({grant_type:"refresh_token",refresh_token:a.refreshToken,client_id:a.clientId}).toString(),i=await this.oauthFetch(a.tokenEndpoint,"POST",n,"application/x-www-form-urlencoded");if(i.status===200){let o=JSON.parse(i.body),l=o.access_token;l&&(this.authManager.storeOAuthToken(s,{accessToken:l,refreshToken:o.refresh_token??a.refreshToken,expiresAt:typeof o.expires_in=="number"?Date.now()+o.expires_in*1e3:void 0,tokenEndpoint:a.tokenEndpoint,clientId:a.clientId,resource:a.resource}),this.injectTokenIntoClaudeConfig(s,l),this.clearNeedsAuthCache(s))}}catch(n){console.warn(`McpManager: failed to refresh token for ${s}:`,n)}let e=this.cache??[];for(let s of e)if((s.type==="http"||s.type==="sse")&&s.url&&!this.authManager.hasToken(s.name))try{let a=await this.extractTokenFromCli(s.name);a&&this.authManager.storeProbeToken(s.name,a)}catch{}}async discoverOAuthMetadata(t){let e=t.endsWith("/sse")?t.replace(/\/sse$/,"/mcp"):t,s=new URL(e),a;try{let h=await this.oauthFetch(e,"POST","{}","application/json");h.status===401&&(a=h.headers?.["www-authenticate"]?.match(/resource_metadata="([^"]+)"/)?.[1])}catch{}a||(a=`${s.origin}/.well-known/oauth-protected-resource${s.pathname}`);let n=s.origin,i,o=e;try{let h=await this.oauthFetch(a,"GET");if(h.status===200){let u=JSON.parse(h.body),p=u.authorization_servers;p?.[0]&&(n=p[0]),Array.isArray(u.scopes_supported)&&(i=u.scopes_supported),typeof u.resource=="string"&&(o=u.resource)}}catch{}let l=new URL(n),c=l.pathname==="/"?"":l.pathname,d=`${l.origin}/.well-known/oauth-authorization-server${c}`;try{let h=await this.oauthFetch(d,"GET");if(h.status===200)return{metadata:JSON.parse(h.body),resourceScopes:i,resource:o}}catch{}if(c){let h=`${l.origin}/.well-known/oauth-authorization-server`;try{let u=await this.oauthFetch(h,"GET");if(u.status===200)return{metadata:JSON.parse(u.body),resourceScopes:i,resource:o}}catch{}}return null}async registerOAuthClient(t,e){let s=JSON.stringify({client_name:"Agent Fleet Obsidian Plugin",redirect_uris:[e],grant_types:["authorization_code","refresh_token"],response_types:["code"],token_endpoint_auth_method:"none"}),a=await this.oauthFetch(t,"POST",s,"application/json");if(a.status!==200&&a.status!==201)throw new Error(`Client registration failed (HTTP ${a.status})`);let n=JSON.parse(a.body);if(!n.client_id)throw new Error("Client registration response missing client_id");return n.client_id}async startOAuthCallbackServer(){let t=null,e=null,s=Ps.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=Zt();try{let s=We.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=Zt();try{let a=We.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}`,We.writeFileSync(s,JSON.stringify(n,null,2))}}catch(a){console.warn("McpManager: failed to inject token into ~/.claude.json:",a)}}clearNeedsAuthCache(t){let e=Et.join(Qt(),"mcp-needs-auth-cache.json");try{let s=We.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&&We.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?_n:Ps).request(d,f=>{let m="";f.on("data",g=>{m+=g.toString()}),f.on("end",()=>{let g={};for(let[k,b]of Object.entries(f.headers))typeof b=="string"?g[k]=b:Array.isArray(b)&&(g[k]=b.join(", "));n({status:f.statusCode??0,body:m,headers:g})})});u.on("error",i);let p=setTimeout(()=>{u.destroy(),i(new Error("OAuth request timed out"))},15e3);u.on("close",()=>clearTimeout(p)),s&&u.write(s),u.end()})}async loadServers(){try{this.emitProgress({phase:"list",message:"Scanning MCP servers\u2026"});let t=await this.runCli("mcp list"),e=this.parseListOutput(t);if(e.length===0)return this.emitProgress({phase:"done",serverCount:0,toolCount:0}),[];let s=[];for(let i=0;i<e.length;i++){let o=e[i];this.emitProgress({phase:"details",current:i+1,total:e.length,serverName:o.name});try{let l=await this.runCliArgs(["mcp","get",o.name]),c=this.mergeGetOutput(o,l);c.description||(c.description=this.getPluginDescription(c.name)),s.push(c)}catch{s.push(o)}}if(this.authManager)for(let i of s)i.status==="needs-auth"&&this.authManager.hasToken(i.name)&&(i.status="connected");let a=s.filter(i=>i.enabled&&(i.status==="connected"||i.status==="needs-auth"&&(i.type==="http"||i.type==="sse")&&i.url));a.length>0&&(this.emitProgress({phase:"tools",message:`Probing ${a.length} server${a.length!==1?"s":""} for tools\u2026`}),await Promise.allSettled(a.map(async i=>{try{let o=[];if(i.type==="stdio"&&i.command){let l=await this.probeStdioServer(i.command,i.args);l.description&&!i.description&&(i.description=l.description),o=l.tools}else(i.type==="http"||i.type==="sse")&&i.url&&(o=await this.probeHttpServer(i));o.length>0&&(i.toolDetails=o,i.tools=o.map(l=>l.name),i.status==="needs-auth"&&(i.status="connected"))}catch(o){console.warn(`McpManager: probe failed for ${i.name}:`,o)}})));let n=s.reduce((i,o)=>i+o.toolDetails.length,0);return this.emitProgress({phase:"done",serverCount:s.length,toolCount:n}),s}catch(t){return console.error("McpManager: failed to load servers",t),this.emitProgress({phase:"done",serverCount:0,toolCount:0}),[]}}getPluginDescription(t){let e=t.replace(/^claude\.ai\s+/i,"").toLowerCase().replace(/\s+/g,"-"),s=Et.join(Qt(),"plugins","marketplaces","claude-plugins-official","external_plugins",e,".claude-plugin","plugin.json");try{let a=We.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=ha(a,{env:{...process.env}}),i="",o,l=[],c=!1,d=!1,h=!1,u=()=>{c||(c=!0,n.kill(),s({description:o,tools:l}))},p=setTimeout(u,1e4);n.stdout.on("data",f=>{i+=f.toString();let m=me(i);i=m.pop()??"";for(let g of m){let k=g.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"})+`
|
|
11792
|
-
|
|
11793
|
-
`)}catch{clearTimeout(p),u();return}}else if(b.id===2&&b.result){for(let v of b.result.tools??[])l.push({name:v.name,description:v.description,inputSchema:v.inputSchema});h=!0}}catch{}d&&h&&(clearTimeout(p),u())}}}),n.on("error",()=>{clearTimeout(p),u()}),n.on("close",()=>{clearTimeout(p),u()});try{n.stdin.write(JSON.stringify({jsonrpc:"2.0",id:1,method:"initialize",params:{protocolVersion:"2024-11-05",capabilities:{},clientInfo:{name:"agent-fleet",version:"1.0.0"}}})+`
|
|
11794
|
-
`)}catch{clearTimeout(p),u()}})}async probeHttpServer(t){let e=await this.findServerToken(t);if(!e)return console.log(`McpManager: no auth token for ${t.name}, skipping tool probe`),[];let s=t.url.endsWith("/sse")?t.url.replace(/\/sse$/,"/mcp"):t.url;try{let n=(await this.httpRequest(s,e,{jsonrpc:"2.0",id:1,method:"initialize",params:{protocolVersion:"2025-03-26",capabilities:{},clientInfo:{name:"agent-fleet",version:"1.0.0"}}}))?._sessionId;await this.httpRequest(s,e,{jsonrpc:"2.0",method:"notifications/initialized"},n);let i=await this.httpRequest(s,e,{jsonrpc:"2.0",id:2,method:"tools/list"},n),o=[],d=i?.result?.tools??[];for(let h of d)o.push({name:h.name,description:h.description,inputSchema:h.inputSchema});return o}catch(a){return console.warn(`McpManager: HTTP probe failed for ${t.name}:`,a),[]}}async findServerToken(t){if(this.authManager){let i=this.authManager.getToken(t.name);if(i)return i}let e=this.readTokenFromClaudeConfig(t.name);if(e)return this.authManager?.storeProbeToken(t.name,e),e;let s=t.name.replace(/^claude\.ai\s+/i,"").replace(/\s+/g,"_").toUpperCase(),a=[`${s}_API_KEY`,`${s}_API_KEY_MILO`,`${s}_TOKEN`];for(let i of a){let o=process.env[i];if(o)return o}let n=[Et.join(da(),".openclaw","workspace",".env"),Et.join(da(),".env")];for(let i of n)try{let o=We.readFileSync(i,"utf8");for(let l of me(o)){let c=l.trim().match(/^(?:export\s+)?([A-Za-z_]\w*)=(.*)$/);if(c){let d=c[1],h=c[2].replace(/^["']|["']$/g,"");if(a.includes(d))return h}}}catch{}}httpRequest(t,e,s,a){return new Promise((n,i)=>{let o=JSON.stringify(s),l=new URL(t),c=l.protocol==="https:",d={"Content-Type":"application/json",Accept:"application/json, text/event-stream",Authorization:`Bearer ${e}`,"Content-Length":String(Buffer.byteLength(o))};a&&(d["mcp-session-id"]=a);let h={hostname:l.hostname,port:l.port||(c?443:80),path:l.pathname+l.search,method:"POST",headers:d},p=(c?_n:Ps).request(h,m=>{let g="";m.on("data",k=>{g+=k.toString()}),m.on("end",()=>{let k=m.headers["mcp-session-id"];if((m.headers["content-type"]??"").includes("text/event-stream")){for(let v of me(g))if(v.startsWith("data: "))try{let y=JSON.parse(v.slice(6));k&&(y._sessionId=k),n(y);return}catch{}n(null)}else try{let v=JSON.parse(g);k&&(v._sessionId=k),n(v)}catch{n(null)}})});p.on("error",i);let f=setTimeout(()=>{p.destroy(),n(null)},15e3);p.on("close",()=>clearTimeout(f)),p.write(o),p.end()})}runCli(t){return new Promise((e,s)=>{let a=`${this.settings.claudeCliPath} ${t}`,n=ha(a,{env:{...process.env}}),i="",o="";n.stdout.on("data",l=>{i+=l.toString()}),n.stderr.on("data",l=>{o+=l.toString()}),n.on("error",s),n.on("close",l=>{l!==0&&!i.trim()?s(new Error(o||`Process exited with code ${l}`)):e(i)})})}runCliArgs(t){return new Promise((e,s)=>{let a=ut(this.settings.claudeCliPath,t,{env:{...process.env}}),n="",i="";a.stdout.on("data",o=>{n+=o.toString()}),a.stderr.on("data",o=>{i+=o.toString()}),a.on("error",s),a.on("close",o=>{o!==0&&!n.trim()?s(new Error(i||`Process exited with code ${o}`)):e(n)})})}getDisabledServers(){let t=new Set,e=Et.join(Qt(),"settings.local.json");try{let s=We.readFileSync(e,"utf8"),a=JSON.parse(s);for(let n of a.disabledMcpjsonServers??[])t.add(n)}catch{}try{let s=We.readFileSync(Zt(),"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=Zt();try{let s=We.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&&We.writeFileSync(e,JSON.stringify(a,null,2))}catch{}}parseListOutput(t){let e=[],s=this.getDisabledServers();for(let a of me(t)){let n=a.trim();if(!n||n.startsWith("Checking"))continue;let i=n.indexOf(": ");if(i===-1)continue;let o=n.slice(0,i).trim(),l=n.slice(i+2),c=l.lastIndexOf(" - ");if(c===-1)continue;let d=l.slice(0,c).trim(),h=l.slice(c+3).trim(),u="disconnected";h.includes("Connected")?u="connected":h.includes("authentication")?u="needs-auth":h.toLowerCase().includes("error")&&(u="error");let p="unknown",f,m;if(d.startsWith("http://")||d.startsWith("https://")){let b=d.replace(/\s+\(\w+\)\s*$/,"").trim();p=b.endsWith("/sse")?"sse":"http",f=b}else d&&(p="stdio",m=d);let g=this.toInternalName(o),k=!s.has(g);e.push({name:o,type:p,status:u,scope:"unknown",enabled:k,url:f,command:m,tools:[],toolDetails:[]})}return e}mergeGetOutput(t,e){let s={...t};for(let a of me(e)){let n=a.trim();if(n.startsWith("Type:")){let i=n.slice(5).trim().toLowerCase();i==="stdio"?s.type="stdio":i==="http"?s.type="http":i==="sse"&&(s.type="sse")}else if(n.startsWith("Scope:")){let i=n.slice(6).trim().toLowerCase();i.includes("user")?s.scope="user":i.includes("project")&&(s.scope="project")}else if(n.startsWith("Command:")){let i=n.slice(8).trim();i&&(s.command=i)}else if(n.startsWith("Args:")){let i=n.slice(5).trim();i&&(s.args=i)}}return s}};var wr={"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 * *"},Is=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 wr[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 le(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 le(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 ns=class{constructor(t,e){this.repository=t;this.settings=e;this.executor=new Ds(e,t),this.mcpManager=new Rs(e),this.scheduler=new Is(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 le(t.heartbeatSchedule,{name:`heartbeat:${t.name}`,catch:!0,protect:!0,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone},()=>{this.runHeartbeat(t.name)});this.heartbeatJobs.set(t.name,e)}catch(e){console.error(`Agent Fleet: failed to register heartbeat for "${t.name}":`,e)}}async runHeartbeat(t){if(!(Date.now()-this.heartbeatRegisteredAt<1e4)&&!this.heartbeatsInFlight.has(t)){this.heartbeatsInFlight.add(t);try{let e=this.repository.getAgentByName(t);if(!e||!e.enabled||!e.heartbeatBody.trim())return;let s={filePath:"",taskId:`heartbeat-${Date.now()}`,agent:e.name,type:"immediate",priority:"medium",enabled:!0,created:new Date().toISOString(),runCount:0,catchUp:!1,tags:[...e.tags,"heartbeat"],body:e.heartbeatBody.trim()};await this.scheduler.enqueue({task:s,reason:"heartbeat",promptOverride:e.heartbeatBody.trim()})}finally{this.heartbeatsInFlight.delete(t)}}}getNextHeartbeat(t){let e=this.heartbeatJobs.get(t);return e?e.nextRun()??null:null}async runPendingTask({task:t,promptOverride:e}){let s=this.repository.getAgentByName(t.agent);if(!s||!s.enabled)return;let a=new Date().toISOString();this.runtimeState.set(s.name,{status:"running",currentTaskId:t.taskId,runStarted:a}),this.runOutputBuffers.set(s.name,""),this.emitStatusChange();try{let n=await this.executor.execute(s,t,e,u=>{let p=this.runOutputBuffers.get(s.name)??"";this.runOutputBuffers.set(s.name,p+u);let f=this.runOutputListeners.get(s.name);if(f)for(let m of f)m(u)}),i=this.consumeAborted(s.name),o=i?[]:this.buildApprovals(s,n.toolsUsed),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=Cn(n.outputText);try{await this.repository.appendMemory(s.name,u)}catch(p){console.warn(`Agent Fleet: failed to append memory for "${s.name}"`,p)}}if(t.tags.includes("heartbeat")&&!i&&s.heartbeatChannel&&c.output.trim())try{this.heartbeatResultHandler?.(s.name,s.heartbeatChannel,c.output)}catch(u){console.warn(`Agent Fleet: heartbeat channel delivery failed for ${s.name}`,u)}i&&(c.output="Task was manually stopped."),this.recentRuns=await this.repository.listRecentRuns(),this.runtimeState.set(s.name,{status:i||l==="success"?"idle":l==="pending_approval"?"pending":"error",currentRunId:n.runId,lastRun:{...c,filePath:d}}),i||this.notify(c)}catch(n){let i=this.consumeAborted(s.name),o=i?"cancelled":"failure",l=Nt(t,s,this.settings),c={runId:(0,An.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=(me(t.output).map(n=>n.trim()).find(n=>n&&!n.startsWith("{")&&!n.startsWith("["))??"").slice(0,120)||t.status,a=t.status==="success"?`\u2705 ${t.agent}: ${s}`:t.status==="pending_approval"?`\u{1F535} ${t.agent} needs approval: ${(t.approvals??[])[0]?.tool??"tool action"}`:`\u274C ${t.agent}: ${s}`;new En.Notice(a,t.status==="success"?5e3:0)}emitStatusChange(){for(let t of this.statusChangeListeners)t()}};var Ls=class{tokens=new Map;oauthTokens=new Map;storeProbeToken(t,e){this.tokens.set(t,e)}storeOAuthToken(t,e){this.oauthTokens.set(t,e),this.tokens.set(t,e.accessToken)}getToken(t){return this.tokens.get(t)}getOAuthToken(t){return this.oauthTokens.get(t)}hasToken(t){return this.tokens.has(t)}removeToken(t){this.tokens.delete(t),this.oauthTokens.delete(t)}getExpiringTokens(t=5*6e4){let e=new Map,s=Date.now();for(let[a,n]of this.oauthTokens)n.refreshToken&&n.expiresAt&&n.expiresAt-s<t&&e.set(a,n);return e}};var ct=require("obsidian");var Fs=require("crypto"),tt=require("obsidian");function Ms(){return(0,Fs.randomUUID)()}function Dn(r){let t=`${r.timestamp}|${r.content.slice(0,80)}`;return(0,Fs.createHash)("sha1").update(t).digest("hex").slice(0,16)}var Bt=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;settingsState=null;get pendingTurnCount(){return this.pendingTurns}lastActiveAt=Date.now();process=null;claudeSessionId=null;vault;stdoutBuffer="";processListeners=null;basePromptSent=!1;channelName;conversationId;channelContext;threadAnchorId;parentSession;threadAnchorIndex;threads=new Map;threadIndex={};activeOnEvent=null;turnResponseText="";turnToolCalls=[];pendingTurns=0;turnResolve=null;turnReject=null;needsCompactBeforeNextTurn=!1;lastCompactTriggerAt=0;static WATCHDOG_MS=300*1e3;watchdogTimer=null;armWatchdog(){this.clearWatchdog(),this.watchdogTimer=setTimeout(()=>{if(this.watchdogTimer=null,!this.isStreaming)return;this.activeOnEvent?.({type:"error",content:"",errorMessage:`no response from the CLI for ${Math.round(r.WATCHDOG_MS/6e4)} minutes \u2014 giving up`});let t=new Error("Watchdog timeout");this.handleProcessError(t);try{this.process?.kill()}catch{}},r.WATCHDOG_MS)}clearWatchdog(){this.watchdogTimer&&(clearTimeout(this.watchdogTimer),this.watchdogTimer=null)}currentToolName;activityListeners=new Set;onActivityChange(t){return this.activityListeners.add(t),t(),()=>{this.activityListeners.delete(t)}}emitActivity(){for(let t of this.activityListeners)t()}setStreaming(t){this.isStreaming!==t&&(this.isStreaming=t,t||(this.currentToolName=void 0),this.emitActivity())}setCurrentTool(t){this.currentToolName!==t&&(this.currentToolName=t,this.emitActivity())}stats={costTotalUsd:0,turnCount:0};statsListeners=new Set;onStatsChange(t){return this.statsListeners.add(t),t({...this.stats}),()=>{this.statsListeners.delete(t)}}getStats(){return{...this.stats}}emitStats(){let t={...this.stats};for(let e of this.statsListeners)e(t)}refreshAgent(){let t=this.repository.getAgentByName(this.agent.name);t&&(this.agent=t)}get isThread(){return!!this.threadAnchorId}async loadPersistedState(){let t=this.getChatFilePath(),e=this.vault.getAbstractFileByPath(t);if(!(e instanceof tt.TFile))return!1;try{let s=await this.vault.cachedRead(e);if(this.isThread){let n=JSON.parse(s);return n.messages?.length>0||n.sessionId?(this.messages=(n.messages??[]).map(i=>i.id?i:{...i,id:Dn(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:Dn(n)}),this.claudeSessionId=a.sessionId??null,this.threadIndex=a.threads??{},this.claudeSessionId&&(this.basePromptSent=!0),!0}catch{}return!1}getThreadIndex(){return{...this.threadIndex}}async persist(){let t=new Date().toISOString(),e=this.getChatFilePath(),s;if(this.isThread){let n={anchorMessageId:this.threadAnchorId,anchorIndex:this.threadAnchorIndex??0,sessionId:this.claudeSessionId,messages:this.messages,createdAt:this.parentSession?.threadIndex[this.threadAnchorId]?.createdAt??t,lastActive:t};s=JSON.stringify(n,null,2)}else{let n={sessionId:this.claudeSessionId,messages:this.messages,lastActive:t,threads:Object.keys(this.threadIndex).length>0?this.threadIndex:void 0};s=JSON.stringify(n,null,2)}let a=this.vault.getAbstractFileByPath(e);a instanceof tt.TFile?await this.vault.modify(a,s):(await this.ensureParentFolders(e),await this.vault.create(e,s)),this.isThread&&this.parentSession&&this.threadAnchorId&&await this.parentSession.upsertThreadIndex(this.threadAnchorId,{path:e,createdAt:this.parentSession.threadIndex[this.threadAnchorId]?.createdAt??t,messageCount:this.messages.length,lastActive:t})}async upsertThreadIndex(t,e){this.threadIndex[t]=e,await this.persist()}async ensureParentFolders(t){let e=t.lastIndexOf("/");if(e<=0)return;let a=t.slice(0,e).split("/"),n="";for(let i of a)if(n=n?`${n}/${i}`:i,!this.vault.getAbstractFileByPath(n))try{await this.vault.createFolder(n)}catch(o){if(!(o instanceof Error?o.message:String(o)).includes("already exists"))throw o}}async clearPersistedState(){let t=this.getChatFilePath(),e=this.vault.getAbstractFileByPath(t);e instanceof tt.TFile&&await this.vault.delete(e),this.messages=[],this.claudeSessionId=null,this.basePromptSent=!1}getChatFilePath(){if(this.threadAnchorId&&this.parentSession)return this.parentSession.getThreadFilePath(this.threadAnchorId);if(this.channelName&&this.conversationId){let e=this.settings.fleetFolder,s=xe(this.conversationId)||"conversation";return(0,tt.normalizePath)(`${e}/channels/${this.channelName}/sessions/${s}.json`)}if(this.agent.isFolder){let e=this.agent.filePath.replace(/\/agent\.md$/,"");return(0,tt.normalizePath)(`${e}/chat.json`)}let t=this.repository.getMemoryPath(this.agent.name).replace(/\/[^/]+$/,"");return(0,tt.normalizePath)(`${t}/${this.agent.name}-chat.json`)}getThreadFilePath(t){let e=this.getParentChatFilePath(),s=e.replace(/\/[^/]+$/,""),a=e.slice(s.length+1).replace(/\.json$/,"");return(0,tt.normalizePath)(`${s}/${a}.threads/${t}.json`)}getParentChatFilePath(){if(this.channelName&&this.conversationId){let e=this.settings.fleetFolder,s=xe(this.conversationId)||"conversation";return(0,tt.normalizePath)(`${e}/channels/${this.channelName}/sessions/${s}.json`)}if(this.agent.isFolder){let e=this.agent.filePath.replace(/\/agent\.md$/,"");return(0,tt.normalizePath)(`${e}/chat.json`)}let t=this.repository.getMemoryPath(this.agent.name).replace(/\/[^/]+$/,"");return(0,tt.normalizePath)(`${t}/${this.agent.name}-chat.json`)}async ensureProcess(){if(this.process&&this.isProcessAlive)return;this.refreshAgent();let t=["--input-format","stream-json","--output-format","stream-json","--verbose"];this.claudeSessionId&&(t.push("--resume",this.claudeSessionId),this.basePromptSent=!0);let e=Nt(null,this.agent,this.settings);_s(e.value)&&t.push("--model",e.value);let s=this.agent.permissionMode?.trim();s&&s!=="default"?t.push("--permission-mode",s):t.push("--permission-mode","bypassPermissions"),this.agent.effort&&t.push("--effort",this.agent.effort);let a=this.agent.cwd??this.repository.getVaultBasePath()??".";this.settingsState=Es(a,this.agent);let n=ut(this.settings.claudeCliPath,t,{cwd:a,env:{...process.env,AWS_REGION:this.settings.awsRegion}});this.process=n,this.isProcessAlive=!0,this.stdoutBuffer="",this.processListeners={onStdout:i=>this.handleStdout(i),onStderr:()=>{},onError:i=>this.handleProcessError(i),onClose:()=>this.handleProcessClose()},n.stdout.on("data",this.processListeners.onStdout),n.stderr.on("data",this.processListeners.onStderr),n.on("error",this.processListeners.onError),n.on("close",this.processListeners.onClose)}detachProcessListeners(){this.process&&this.processListeners&&(this.process.stdout?.removeListener("data",this.processListeners.onStdout),this.process.stderr?.removeListener("data",this.processListeners.onStderr),this.process.removeListener("error",this.processListeners.onError),this.process.removeListener("close",this.processListeners.onClose)),this.processListeners=null}handleStdout(t){this.isStreaming&&this.armWatchdog(),this.stdoutBuffer+=t.toString();let e=me(this.stdoutBuffer);this.stdoutBuffer=e.pop()??"";for(let s of e){let a=s.trim();if(a)try{let n=JSON.parse(a);this.handleEvent(n)}catch{}}}handleEvent(t){if(typeof t.session_id=="string"&&(this.claudeSessionId=t.session_id),this.updateStatsFromEvent(t),t.type==="system"&&t.subtype==="compact_boundary"){let s=t.compact_metadata,a=s&&typeof s.pre_tokens=="number"?s.pre_tokens:0,n=s&&typeof s.post_tokens=="number"?s.post_tokens:0;n>0&&(this.stats.contextTokensUsed=n),this.stats.lastCompact={preTokens:a,postTokens:n},this.emitStats(),this.needsCompactBeforeNextTurn=!1,this.activeOnEvent?.({type:"compacted",content:"",compact:{preTokens:a,postTokens:n}});return}if(t.type==="result"){if(t.is_error===!0||typeof t.api_error_status=="string"&&t.api_error_status){let s=this.describeResultError(t);this.activeOnEvent?.({type:"error",content:"",errorMessage:s})}this.handleTurnEnd();return}let e=this.parseStreamEvent(t);if(e){if(e.type==="text"){let s=this.turnResponseText.length===0;this.turnResponseText+=e.content,this.setCurrentTool(void 0),s&&this.turnResponseText.length>0&&this.emitActivity()}else e.type==="tool_use"&&e.toolName&&(this.turnToolCalls.push({name:e.toolName,command:e.content||void 0}),this.setCurrentTool(e.toolName));this.activeOnEvent?.(e)}}get hasCurrentTurnText(){return this.turnResponseText.length>0}describeResultError(t){let e=[],s=typeof t.api_error_status=="string"?t.api_error_status:"",a=typeof t.subtype=="string"?t.subtype:"",n=typeof t.result=="string"?t.result:"";return s?e.push(`API ${s}`):a?e.push(a.replace(/_/g," ")):e.push("unknown error"),n&&e.push(`\u2014 ${n}`),e.join(" ")}updateStatsFromEvent(t){let e=!1,s=typeof t.model=="string"?t.model:void 0,a=t.message,n=a&&typeof a.model=="string"?a.model:void 0,i=s||n;if(i&&i!==this.stats.concreteModel&&(this.stats.concreteModel=i,e=!0),t.type==="rate_limit_event"){let o=t.rate_limit_info;o&&(this.stats.rateLimit={type:typeof o.rateLimitType=="string"?o.rateLimitType:"unknown",resetsAt:typeof o.resetsAt=="number"?o.resetsAt:void 0,status:typeof o.status=="string"?o.status:void 0,isUsingOverage:typeof o.isUsingOverage=="boolean"?o.isUsingOverage:void 0},e=!0)}if(t.type==="assistant"&&a){let o=a.usage;if(o){let l=typeof o.input_tokens=="number"?o.input_tokens:0,c=typeof o.cache_read_input_tokens=="number"?o.cache_read_input_tokens:0,d=typeof o.cache_creation_input_tokens=="number"?o.cache_creation_input_tokens:0,h=l+c+d;h>0&&h!==this.stats.contextTokensUsed&&(this.stats.contextTokensUsed=h,e=!0)}}if(t.type==="result"){let o=typeof t.total_cost_usd=="number"?t.total_cost_usd:0;o>0&&(this.stats.costTotalUsd+=o,e=!0);let l=t.modelUsage;if(l)for(let c of Object.values(l)){let d=c;typeof d.contextWindow=="number"&&d.contextWindow!==this.stats.contextWindow&&(this.stats.contextWindow=d.contextWindow,e=!0)}this.stats.turnCount+=1,e=!0}t.type==="result"&&this.evaluateAutoCompact(),e&&this.emitStats()}evaluateAutoCompact(){let t=this.agent.autoCompactThreshold??0;if(t<=0||t>=100)return;let e=this.stats.contextWindow,s=this.stats.contextTokensUsed;!e||!s||s/e*100<t||Date.now()-this.lastCompactTriggerAt<3e4||(this.needsCompactBeforeNextTurn=!0)}handleTurnEnd(){this.lastActiveAt=Date.now(),this.turnResponseText.trim()&&this.messages.push({id:Ms(),role:"assistant",content:this.turnResponseText,timestamp:new Date().toISOString(),toolCalls:this.turnToolCalls.length>0?[...this.turnToolCalls]:void 0});let t={text:this.turnResponseText,toolCalls:[...this.turnToolCalls]};if(this.activeOnEvent?.({type:"result",content:"",toolCalls:[...this.turnToolCalls]}),this.turnResponseText="",this.turnToolCalls=[],this.pendingTurns--,this.pendingTurns<=0){this.pendingTurns=0,this.clearWatchdog(),this.setStreaming(!1),this.persist();let e=this.turnResolve;this.turnResolve=null,this.turnReject=null,e?.(t)}}handleProcessError(t){this.isProcessAlive=!1,this.process=null,this.pendingTurns=0,this.turnResponseText="",this.turnToolCalls=[],this.clearWatchdog(),this.setStreaming(!1),At(this.settingsState),this.settingsState=null;let e=this.turnReject;this.turnResolve=null,this.turnReject=null,e?.(t)}handleProcessClose(){if(this.isProcessAlive=!1,this.process=null,At(this.settingsState),this.settingsState=null,this.turnResolve){let t={text:this.turnResponseText,toolCalls:[...this.turnToolCalls]};this.turnResponseText.trim()&&this.messages.push({id:Ms(),role:"assistant",content:this.turnResponseText,timestamp:new Date().toISOString(),toolCalls:this.turnToolCalls.length>0?[...this.turnToolCalls]:void 0}),this.pendingTurns=0,this.turnResponseText="",this.turnToolCalls=[],this.clearWatchdog(),this.setStreaming(!1),this.persist();let e=this.turnResolve;this.turnResolve=null,this.turnReject=null,e?.(t)}}async sendMessage(t,e,s,a){this.lastActiveAt=Date.now(),this.messages.push({id:Ms(),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()}
|
|
11954
|
+
`)}async execute(t,e,s,n,a){let r=t.memory&&!a?.suppressMemoryCapture,o=await this.buildPrompt(t,e,s,r),c=(0,Gi.randomUUID)(),l=Ut(e,t,this.settings),d=n!=null,h=Hi(t.adapter),u=await h.buildExec({prompt:o,model:ks(l.value)?l.value:"",modelSource:l.source,effort:e.effort||t.effort||"",agent:t,settings:this.settings,streaming:d}),p=t.cwd??this.repository.getVaultBasePath()??".",m=r?xs(p,this.repository.getPendingDirAbsolutePath(t.name),"mcp"):null;m&&u.args.push(...Ss(m,t.adapter));let f=await h.setupPermissions(p,t,this.settings,{allowRememberTool:!!m}),y=Date.now();try{return await new Promise((w,k)=>{let g=qe(u.cliPath,u.args,{cwd:p,env:{...process.env,AWS_REGION:this.settings.awsRegion,...f?.env??{}}});if(u.stdinPayload!==void 0)try{g.stdin?.write(u.stdinPayload),g.stdin?.end()}catch(D){g.kill(),k(D instanceof Error?D:new Error(String(D)));return}this.runningProcesses.set(t.name,g);let v="",S="",T=!1,_=setTimeout(()=>{T=!0,g.kill()},t.timeout*1e3);g.stdout.on("data",D=>{let E=D.toString();if(v+=E,d&&n)for(let C of oe(E)){let L=h.extractStreamChunk(C);L&&n(L)}}),g.stderr.on("data",D=>{S+=D.toString()}),g.on("error",D=>{clearTimeout(_),k(D)}),g.on("close",D=>{clearTimeout(_),this.runningProcesses.delete(t.name);let E=h.parseExecOutput(v,S,d);w({runId:c,prompt:o,exitCode:D,durationSeconds:Math.max(1,Math.round((Date.now()-y)/1e3)),stdout:v,stderr:S,outputText:E.outputText,rawJson:E.rawJson,tokensUsed:E.tokensUsed,costUsd:E.costUsd,toolsUsed:E.toolsUsed,timedOut:T,resolvedModel:l.value,modelSource:l.source,concreteModel:E.concreteModel,finalResult:E.finalResult})})})}finally{f?.restore(),At(m)}}};function Vi(i){return se(i).slice(0,60)||"candidate"}function Ki(i){let t=Math.max(400,i.tokenBudget*4);return[`You are running a nightly memory **reflection** for the agent "${i.agentName}".`,"Review the raw memory log and your current working memory below, then produce an","updated, consolidated working memory.","","Rules:","- Remove duplicates; resolve contradictions in favor of the NEWEST fact.","- File each fact under exactly one of: Preferences, Procedures, Observations.","- Keep durable user/process preferences, marking them `[pin]` \u2014 never drop or"," summarize pinned Preferences.","- Summarize the Observations section FROM THE RAW LOG (not from prior summaries)",` if needed so the whole memory fits within roughly ${i.tokenBudget} tokens`,` (~${t} characters).`,"- Note any friction that recurred across multiple runs as a skill candidate.","","Reply with EXACTLY these two fenced blocks and nothing else:","","```memory","## Preferences","- [pin] <durable preference> <!-- src:reflection -->","## Procedures","- <how-to learning>","## Observations","- <current fact>","```","","```candidates",'[{"key":"short-stable-key","pattern":"what recurred","evidence":["runs/..."],"suggestedSkill":"optional-name"}]',"```","","If there is nothing worth remembering, still emit a `memory` block preserving the","existing pinned Preferences. Use an empty array `[]` for candidates when none.","","\u2500\u2500 CURRENT WORKING MEMORY \u2500\u2500",i.workingMemoryBody.trim()||"(empty)","","\u2500\u2500 RAW MEMORY LOG (ground truth) \u2500\u2500",i.recentRaw.trim()||"(empty)",i.recentRunsSummary?`
|
|
11955
|
+
\u2500\u2500 RECENT ACTIVITY \u2500\u2500
|
|
11956
|
+
${i.recentRunsSummary.trim()}`:""].join(`
|
|
11957
|
+
`)}function Yi(i,t){let e=new RegExp("```"+t+"(?![A-Za-z0-9-])[^\\n]*\\n([\\s\\S]*?)```","i"),s=i.match(e);return s?s[1]??"":null}function Ji(i){let t=Yi(i,"memory"),e=t!==null?Vs(t):null,s=[],n=Yi(i,"candidates");if(n!==null&&n.trim())try{let a=JSON.parse(n.trim());Array.isArray(a)&&(s=a.map(r=>Il(r)).filter(r=>r!==null))}catch{}return{sections:e,candidates:s}}function Il(i){if(typeof i!="object"||i===null)return null;let t=i,e=typeof t.pattern=="string"?t.pattern.trim():"";return e?{key:typeof t.key=="string"&&t.key.trim()?Vi(t.key):Vi(e),pattern:e,evidence:Array.isArray(t.evidence)?t.evidence.filter(n=>typeof n=="string"):[],suggestedSkill:typeof t.suggestedSkill=="string"?t.suggestedSkill:void 0}:null}function Xi(i,t,e){let s=new Map;for(let a of i)s.set(a.key,{...a,evidence:[...a.evidence]});let n=new Map;for(let a of t){let r=n.get(a.key);r?(r.evidence=Array.from(new Set([...r.evidence??[],...a.evidence??[]])),a.suggestedSkill&&(r.suggestedSkill=a.suggestedSkill)):n.set(a.key,{...a,evidence:[...a.evidence??[]]})}for(let a of n.values()){let r=s.get(a.key);r?(r.occurrences+=1,r.lastSeen=e,r.evidence=Array.from(new Set([...r.evidence,...a.evidence??[]])),a.suggestedSkill&&(r.suggestedSkill=a.suggestedSkill)):s.set(a.key,{key:a.key,pattern:a.pattern,occurrences:1,firstSeen:e,lastSeen:e,evidence:[...a.evidence??[]],proposed:!1,suggestedSkill:a.suggestedSkill})}return[...s.values()]}var Ml=1.5,Jt=class i{constructor(t){this.store=t}static locks=new Map;static __resetLocksForTest(){i.locks.clear()}async capture(t,e,s,n){!t.memory||e.length===0||await this.withLock(t.name,()=>this.applyCaptures(t,e,s,n))}async drainPending(t,e){t.memory&&await this.withLock(t.name,async()=>{let s=await this.store.readAndClearPending(t.name),n=zi(s);if(n.length===0)return;let a=[];for(let r of n){let o=a[a.length-1],c={text:r.text,pinned:r.pinned,section:r.section};o&&o.source===r.source?o.entries.push(c):a.push({source:r.source,entries:[c]})}for(let r of a)await this.applyCaptures(t,r.entries,r.source,e)})}async reflect(t,e,s){return!t.memory||e===null||!e.some(n=>n.entries.length>0)?!1:(await this.withLock(t.name,async()=>{let n=this.store.getWorkingMemoryPath(t.name),a=await this.store.readWorkingMemory(t.name),r=ri(a,e),o={filePath:a?.filePath??n,agent:t.name,schema:a?.schema??2,lastUpdated:s,lastReflection:s,tokenEstimate:0,sections:r};await this.store.writeWorkingMemory(t.name,o)}),!0)}async applyCaptures(t,e,s,n){let a=e.map(p=>({...p,text:Za(p.text)})).filter(p=>p.text);if(a.length===0)return;await this.store.migrateLegacyMemory?.(t.name);let r=this.store.getWorkingMemoryPath(t.name),o=await this.store.readWorkingMemory(t.name)??ei(r,t.name),c=n.slice(0,10),l=a.map(p=>`- ${n} [${s}]${p.pinned?" [pin]":""} ${p.text}`);await this.store.appendRawMemory(t.name,l,n);let d=new Set(o.sections.flatMap(p=>p.entries).map(p=>p.text.trim().toLowerCase())),h=o;for(let p of a){let m=p.text.trim().toLowerCase();if(d.has(m))continue;d.add(m);let f={text:p.text,source:s,date:c,pinned:p.pinned??!1},y=p.section??(p.pinned?"Preferences":Xa);h=ai(h,[f],y,n)}let u=Math.ceil((t.memoryTokenBudget||0)*Ml);if(u>0){let{wm:p,spilled:m}=ii(h,u);m.length>0&&console.info(`Agent Fleet: working memory for "${t.name}" exceeded hard cap; spilled ${m.length} entr${m.length===1?"y":"ies"} to raw-only (still in the archive). A reflection pass will consolidate.`),h=p}await this.store.writeWorkingMemory(t.name,h)}withLock(t,e){let n=(i.locks.get(t)??Promise.resolve()).then(e,e);return i.locks.set(t,n.then(()=>{},()=>{})),n}};var Cs=Ke(require("crypto")),Qi=Ke(require("https")),dn=Ke(require("http")),He=Ke(require("fs")),$t=Ke(require("path"));var hn=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=$t.join(us(),"settings.local.json"),n={};try{let o=He.readFileSync(s,"utf8");n=JSON.parse(o)}catch{}let a=n.disabledMcpjsonServers??[],r=this.toInternalName(t);if(e?(n.disabledMcpjsonServers=a.filter(o=>o!==r),this.enableServerInClaudeConfig(t)):a.includes(r)||(a.push(r),n.disabledMcpjsonServers=a),He.writeFileSync(s,JSON.stringify(n,null,2)),this.cache){let o=this.cache.find(c=>c.name===t);o&&(o.enabled=e)}}toInternalName(t){return t.replace(/\./g,"_").replace(/\s+/g,"_")}async addServer(t){let e=["mcp","add","-s",t.scope??"user"];if(t.transport==="stdio"){if(t.envVars)for(let[s,n]of Object.entries(t.envVars))e.push("-e",`${s}=${n}`);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,n]of Object.entries(t.headers))e.push("-H",`${s}: ${n}`);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 n=await this.discoverOAuthMetadata(e);if(!n)throw new Error("Server does not support OAuth \u2014 no authorization metadata found.");let{metadata:a,resourceScopes:r,resource:o}=n;if(!a.registration_endpoint)throw new Error("Server does not support Dynamic Client Registration.");let c=await this.startOAuthCallbackServer(),l=`http://localhost:${c.port}/callback`;try{let d=await this.registerOAuthClient(a.registration_endpoint,l),h=Cs.randomBytes(32).toString("base64url"),u=Cs.createHash("sha256").update(h).digest("base64url"),p=Cs.randomBytes(16).toString("hex"),m=new URLSearchParams({response_type:"code",client_id:d,code_challenge:u,code_challenge_method:"S256",redirect_uri:l,state:p,resource:o}),f=r??a.scopes_supported;f?.length&&m.set("scope",f.join(" "));let y=new URL(a.authorization_endpoint);for(let[v,S]of m)y.searchParams.set(v,S);let w=y.toString();console.log("McpManager: OAuth DCR client_id:",d),console.log("McpManager: OAuth auth URL:",w),ci(w);let k=await c.waitForCode(p,18e4),g=await this.exchangeOAuthCode(a.token_endpoint,k,l,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:a.token_endpoint,clientId:d,resource:o}),this.clearNeedsAuthCache(t),this.enableServerInClaudeConfig(t),this.invalidateCache()}finally{c.close()}}async extractTokenFromCli(t){try{return(await this.runCliArgs(["mcp","get",t])).match(/Authorization:\s*Bearer\s+(\S+)/)?.[1]}catch{return}}async refreshProbeTokens(){if(!this.authManager)return;let t=this.authManager.getExpiringTokens();for(let[s,n]of t)if(n.refreshToken)try{let a=new URLSearchParams({grant_type:"refresh_token",refresh_token:n.refreshToken,client_id:n.clientId}).toString(),r=await this.oauthFetch(n.tokenEndpoint,"POST",a,"application/x-www-form-urlencoded");if(r.status===200){let o=JSON.parse(r.body),c=o.access_token;c&&(this.authManager.storeOAuthToken(s,{accessToken:c,refreshToken:o.refresh_token??n.refreshToken,expiresAt:typeof o.expires_in=="number"?Date.now()+o.expires_in*1e3:void 0,tokenEndpoint:n.tokenEndpoint,clientId:n.clientId,resource:n.resource}),this.injectTokenIntoClaudeConfig(s,c),this.clearNeedsAuthCache(s))}}catch(a){console.warn(`McpManager: failed to refresh token for ${s}:`,a)}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 n=await this.extractTokenFromCli(s.name);n&&this.authManager.storeProbeToken(s.name,n)}catch{}}async discoverOAuthMetadata(t){let e=t.endsWith("/sse")?t.replace(/\/sse$/,"/mcp"):t,s=new URL(e),n;try{let h=await this.oauthFetch(e,"POST","{}","application/json");h.status===401&&(n=h.headers?.["www-authenticate"]?.match(/resource_metadata="([^"]+)"/)?.[1])}catch{}n||(n=`${s.origin}/.well-known/oauth-protected-resource${s.pathname}`);let a=s.origin,r,o=e;try{let h=await this.oauthFetch(n,"GET");if(h.status===200){let u=JSON.parse(h.body),p=u.authorization_servers;p?.[0]&&(a=p[0]),Array.isArray(u.scopes_supported)&&(r=u.scopes_supported),typeof u.resource=="string"&&(o=u.resource)}}catch{}let c=new URL(a),l=c.pathname==="/"?"":c.pathname,d=`${c.origin}/.well-known/oauth-authorization-server${l}`;try{let h=await this.oauthFetch(d,"GET");if(h.status===200)return{metadata:JSON.parse(h.body),resourceScopes:r,resource:o}}catch{}if(l){let h=`${c.origin}/.well-known/oauth-authorization-server`;try{let u=await this.oauthFetch(h,"GET");if(u.status===200)return{metadata:JSON.parse(u.body),resourceScopes:r,resource:o}}catch{}}return null}async registerOAuthClient(t,e){let s=JSON.stringify({client_name:"Agent Fleet Obsidian Plugin",redirect_uris:[e],grant_types:["authorization_code","refresh_token"],response_types:["code"],token_endpoint_auth_method:"none"}),n=await this.oauthFetch(t,"POST",s,"application/json");if(n.status!==200&&n.status!==201)throw new Error(`Client registration failed (HTTP ${n.status})`);let a=JSON.parse(n.body);if(!a.client_id)throw new Error("Client registration response missing client_id");return a.client_id}async startOAuthCallbackServer(){let t=null,e=null,s=dn.createServer((a,r)=>{let o=new URL(a.url??"/","http://localhost");if(o.pathname!=="/callback"){r.writeHead(404),r.end();return}let c=o.searchParams.get("error");if(c){let h=o.searchParams.get("error_description")??c;r.writeHead(200,{"Content-Type":"text/html"}),r.end("<html><body style='font-family:system-ui;text-align:center;padding:60px'><h2>Authorization Failed</h2><p>"+h+"</p><p style='color:#888'>You can close this tab.</p></body></html>"),e?.(new Error(`OAuth denied: ${h}`));return}let l=o.searchParams.get("code"),d=o.searchParams.get("state");if(!l||!d){r.writeHead(400),r.end("Missing code or state");return}r.writeHead(200,{"Content-Type":"text/html"}),r.end("<html><body style='font-family:system-ui;text-align:center;padding:60px'><h2 style='color:#22c55e'>Authenticated!</h2><p>You can close this tab and return to Obsidian.</p><script>setTimeout(()=>window.close(),2000)</script></body></html>"),t?.(l,d)});return{port:await new Promise((a,r)=>{s.listen(0,"127.0.0.1",()=>{let o=s.address();if(!o||typeof o=="string"){r(new Error("Failed to bind callback server"));return}a(o.port)}),s.on("error",r)}),waitForCode:(a,r)=>new Promise((o,c)=>{let l=setTimeout(()=>{c(new Error("Authentication timed out \u2014 complete authorization in your browser and try again."))},r);t=(d,h)=>{clearTimeout(l),h!==a?c(new Error("OAuth state mismatch \u2014 possible CSRF attack")):o(d)},e=d=>{clearTimeout(l),c(d)}}),close:()=>{try{s.close()}catch{}}}}async exchangeOAuthCode(t,e,s,n,a){let r=new URLSearchParams({grant_type:"authorization_code",code:e,redirect_uri:s,client_id:n,code_verifier:a}).toString(),o=await this.oauthFetch(t,"POST",r,"application/x-www-form-urlencoded");if(o.status!==200)throw new Error(`Token exchange failed (HTTP ${o.status}): ${o.body}`);let c=JSON.parse(o.body);if(!c.access_token)throw new Error("Token response missing access_token");return c}readTokenFromClaudeConfig(t){let e=ps();try{let s=He.readFileSync(e,"utf8"),r=JSON.parse(s).mcpServers?.[t]?.headers,o=r?.Authorization??r?.authorization;if(o?.startsWith("Bearer "))return o.slice(7)}catch{}}injectTokenIntoClaudeConfig(t,e){let s=ps();try{let n=He.readFileSync(s,"utf8"),a=JSON.parse(n),r=a.mcpServers;if(r?.[t]){let o=r[t];(!o.headers||typeof o.headers!="object")&&(o.headers={}),o.headers.Authorization=`Bearer ${e}`,He.writeFileSync(s,JSON.stringify(a,null,2))}}catch(n){console.warn("McpManager: failed to inject token into ~/.claude.json:",n)}}clearNeedsAuthCache(t){let e=$t.join(us(),"mcp-needs-auth-cache.json");try{let s=He.readFileSync(e,"utf8"),n=JSON.parse(s),a=!1;t in n&&(delete n[t],a=!0);let r=`claude.ai ${t}`;r in n&&(delete n[r],a=!0),a&&He.writeFileSync(e,JSON.stringify(n))}catch{}}oauthFetch(t,e,s,n){return new Promise((a,r)=>{let o=new URL(t),c=o.protocol==="https:",l={Accept:"application/json"};s&&(l["Content-Type"]=n??"application/json",l["Content-Length"]=String(Buffer.byteLength(s)));let d={hostname:o.hostname,port:o.port||(c?443:80),path:o.pathname+o.search,method:e,headers:l},u=(c?Qi:dn).request(d,m=>{let f="";m.on("data",y=>{f+=y.toString()}),m.on("end",()=>{let y={};for(let[w,k]of Object.entries(m.headers))typeof k=="string"?y[w]=k:Array.isArray(k)&&(y[w]=k.join(", "));a({status:m.statusCode??0,body:f,headers:y})})});u.on("error",r);let p=setTimeout(()=>{u.destroy(),r(new Error("OAuth request timed out"))},15e3);u.on("close",()=>clearTimeout(p)),s&&u.write(s),u.end()})}async loadServers(){try{this.emitProgress({phase:"list",message:"Scanning MCP servers\u2026"});let t=await this.runCli("mcp list"),e=this.parseListOutput(t);if(e.length===0)return this.emitProgress({phase:"done",serverCount:0,toolCount:0}),[];let s=[];for(let r=0;r<e.length;r++){let o=e[r];this.emitProgress({phase:"details",current:r+1,total:e.length,serverName:o.name});try{let c=await this.runCliArgs(["mcp","get",o.name]),l=this.mergeGetOutput(o,c);l.description||(l.description=this.getPluginDescription(l.name)),s.push(l)}catch{s.push(o)}}if(this.authManager)for(let r of s)r.status==="needs-auth"&&this.authManager.hasToken(r.name)&&(r.status="connected");let n=s.filter(r=>r.enabled&&(r.status==="connected"||r.status==="needs-auth"&&(r.type==="http"||r.type==="sse")&&r.url));n.length>0&&(this.emitProgress({phase:"tools",message:`Probing ${n.length} server${n.length!==1?"s":""} for tools\u2026`}),await Promise.allSettled(n.map(async r=>{try{let o=[];if(r.type==="stdio"&&r.command){let c=await this.probeStdioServer(r.command,r.args);c.description&&!r.description&&(r.description=c.description),o=c.tools}else(r.type==="http"||r.type==="sse")&&r.url&&(o=await this.probeHttpServer(r));o.length>0&&(r.toolDetails=o,r.tools=o.map(c=>c.name),r.status==="needs-auth"&&(r.status="connected"))}catch(o){console.warn(`McpManager: probe failed for ${r.name}:`,o)}})));let a=s.reduce((r,o)=>r+o.toolDetails.length,0);return this.emitProgress({phase:"done",serverCount:s.length,toolCount:a}),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=$t.join(us(),"plugins","marketplaces","claude-plugins-official","external_plugins",e,".claude-plugin","plugin.json");try{let n=He.readFileSync(s,"utf8");return JSON.parse(n).description||void 0}catch{return}}probeStdioServer(t,e){return new Promise(s=>{let n=e?`${t} ${e}`:t,a=Jn(n,{env:{...process.env}}),r="",o,c=[],l=!1,d=!1,h=!1,u=()=>{l||(l=!0,a.kill(),s({description:o,tools:c}))},p=setTimeout(u,1e4);a.stdout.on("data",m=>{r+=m.toString();let f=oe(r);r=f.pop()??"";for(let y of f){let w=y.trim();if(w){try{let k=JSON.parse(w);if(k.id===1&&k.result){o=k.result.instructions??k.result.serverInfo?.description,d=!0;try{a.stdin.write(JSON.stringify({jsonrpc:"2.0",method:"notifications/initialized"})+`
|
|
11958
|
+
`),a.stdin.write(JSON.stringify({jsonrpc:"2.0",id:2,method:"tools/list"})+`
|
|
11959
|
+
`)}catch{clearTimeout(p),u();return}}else if(k.id===2&&k.result){for(let g of k.result.tools??[])c.push({name:g.name,description:g.description,inputSchema:g.inputSchema});h=!0}}catch{}d&&h&&(clearTimeout(p),u())}}}),a.on("error",()=>{clearTimeout(p),u()}),a.on("close",()=>{clearTimeout(p),u()});try{a.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"}}})+`
|
|
11960
|
+
`)}catch{clearTimeout(p),u()}})}async probeHttpServer(t){let e=await this.findServerToken(t);if(!e)return console.log(`McpManager: no auth token for ${t.name}, skipping tool probe`),[];let s=t.url.endsWith("/sse")?t.url.replace(/\/sse$/,"/mcp"):t.url;try{let a=(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"},a);let r=await this.httpRequest(s,e,{jsonrpc:"2.0",id:2,method:"tools/list"},a),o=[],d=r?.result?.tools??[];for(let h of d)o.push({name:h.name,description:h.description,inputSchema:h.inputSchema});return o}catch(n){return console.warn(`McpManager: HTTP probe failed for ${t.name}:`,n),[]}}async findServerToken(t){if(this.authManager){let r=this.authManager.getToken(t.name);if(r)return r}let e=this.readTokenFromClaudeConfig(t.name);if(e)return this.authManager?.storeProbeToken(t.name,e),e;let s=t.name.replace(/^claude\.ai\s+/i,"").replace(/\s+/g,"_").toUpperCase(),n=[`${s}_API_KEY`,`${s}_API_KEY_MILO`,`${s}_TOKEN`];for(let r of n){let o=process.env[r];if(o)return o}let a=[$t.join(Kn(),".openclaw","workspace",".env"),$t.join(Kn(),".env")];for(let r of a)try{let o=He.readFileSync(r,"utf8");for(let c of oe(o)){let l=c.trim().match(/^(?:export\s+)?([A-Za-z_]\w*)=(.*)$/);if(l){let d=l[1],h=l[2].replace(/^["']|["']$/g,"");if(n.includes(d))return h}}}catch{}}httpRequest(t,e,s,n){return new Promise((a,r)=>{let o=JSON.stringify(s),c=new URL(t),l=c.protocol==="https:",d={"Content-Type":"application/json",Accept:"application/json, text/event-stream",Authorization:`Bearer ${e}`,"Content-Length":String(Buffer.byteLength(o))};n&&(d["mcp-session-id"]=n);let h={hostname:c.hostname,port:c.port||(l?443:80),path:c.pathname+c.search,method:"POST",headers:d},p=(l?Qi:dn).request(h,f=>{let y="";f.on("data",w=>{y+=w.toString()}),f.on("end",()=>{let w=f.headers["mcp-session-id"];if((f.headers["content-type"]??"").includes("text/event-stream")){for(let g of oe(y))if(g.startsWith("data: "))try{let v=JSON.parse(g.slice(6));w&&(v._sessionId=w),a(v);return}catch{}a(null)}else try{let g=JSON.parse(y);w&&(g._sessionId=w),a(g)}catch{a(null)}})});p.on("error",r);let m=setTimeout(()=>{p.destroy(),a(null)},15e3);p.on("close",()=>clearTimeout(m)),p.write(o),p.end()})}runCli(t){return new Promise((e,s)=>{let n=`${this.settings.claudeCliPath} ${t}`,a=Jn(n,{env:{...process.env}}),r="",o="";a.stdout.on("data",c=>{r+=c.toString()}),a.stderr.on("data",c=>{o+=c.toString()}),a.on("error",s),a.on("close",c=>{c!==0&&!r.trim()?s(new Error(o||`Process exited with code ${c}`)):e(r)})})}runCliArgs(t){return new Promise((e,s)=>{let n=qe(this.settings.claudeCliPath,t,{env:{...process.env}}),a="",r="";n.stdout.on("data",o=>{a+=o.toString()}),n.stderr.on("data",o=>{r+=o.toString()}),n.on("error",s),n.on("close",o=>{o!==0&&!a.trim()?s(new Error(r||`Process exited with code ${o}`)):e(a)})})}getDisabledServers(){let t=new Set,e=$t.join(us(),"settings.local.json");try{let s=He.readFileSync(e,"utf8"),n=JSON.parse(s);for(let a of n.disabledMcpjsonServers??[])t.add(a)}catch{}try{let s=He.readFileSync(ps(),"utf8"),a=JSON.parse(s).projects;if(a){for(let r of Object.values(a))if(r&&Array.isArray(r.disabledMcpServers))for(let o of r.disabledMcpServers)t.add(o)}}catch{}return t}enableServerInClaudeConfig(t){let e=ps();try{let s=He.readFileSync(e,"utf8"),n=JSON.parse(s),a=n.projects;if(!a)return;let r=!1;for(let o of Object.values(a))if(o&&Array.isArray(o.disabledMcpServers)){let c=o.disabledMcpServers.indexOf(t);c!==-1&&(o.disabledMcpServers.splice(c,1),r=!0)}r&&He.writeFileSync(e,JSON.stringify(n,null,2))}catch{}}parseListOutput(t){let e=[],s=this.getDisabledServers();for(let n of oe(t)){let a=n.trim();if(!a||a.startsWith("Checking"))continue;let r=a.indexOf(": ");if(r===-1)continue;let o=a.slice(0,r).trim(),c=a.slice(r+2),l=c.lastIndexOf(" - ");if(l===-1)continue;let d=c.slice(0,l).trim(),h=c.slice(l+3).trim(),u="disconnected";h.includes("Connected")?u="connected":h.includes("authentication")?u="needs-auth":h.toLowerCase().includes("error")&&(u="error");let p="unknown",m,f;if(d.startsWith("http://")||d.startsWith("https://")){let k=d.replace(/\s+\(\w+\)\s*$/,"").trim();p=k.endsWith("/sse")?"sse":"http",m=k}else d&&(p="stdio",f=d);let y=this.toInternalName(o),w=!s.has(y);e.push({name:o,type:p,status:u,scope:"unknown",enabled:w,url:m,command:f,tools:[],toolDetails:[]})}return e}mergeGetOutput(t,e){let s={...t};for(let n of oe(e)){let a=n.trim();if(a.startsWith("Type:")){let r=a.slice(5).trim().toLowerCase();r==="stdio"?s.type="stdio":r==="http"?s.type="http":r==="sse"&&(s.type="sse")}else if(a.startsWith("Scope:")){let r=a.slice(6).trim().toLowerCase();r.includes("user")?s.scope="user":r.includes("project")&&(s.scope="project")}else if(a.startsWith("Command:")){let r=a.slice(8).trim();r&&(s.command=r)}else if(a.startsWith("Args:")){let r=a.slice(5).trim();r&&(s.args=r)}}return s}};var Ll={"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 * *"},un=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 Ll[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 le(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 le(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 n=s.nextRun();await this.callbacks.onTaskScheduled(t,n?this.toLocalISOString(n):void 0)}}catch(e){console.error(`Agent Fleet: Failed to register task "${t.taskId}":`,e)}}unregisterTask(t){let e=this.jobs.get(t);e&&(e.stop(),this.jobs.delete(t))}async loadTasks(t){for(let e of t)await this.registerTask(e)}async handleStartupCatchUp(t){let e=Date.now();for(let s of t)!s.enabled||!s.catchUp||!s.nextRun||new Date(s.nextRun).getTime()<e&&await this.enqueue({task:s,reason:"catch-up"})}async enqueue(t){this.queue.push(t),await this.processQueue()}pauseAll(){this.paused=!0,this.jobs.forEach(t=>t.pause())}resumeAll(){this.paused=!1,this.jobs.forEach(t=>t.resume()),this.processQueue()}shutdown(){this.paused=!0,this.jobs.forEach(t=>t.stop()),this.jobs.clear(),this.queue.length=0}getQueueSize(){return this.queue.length}async processQueue(){if(!this.paused)for(;this.activeRuns<this.maxConcurrentRuns&&this.queue.length>0;){let t=this.queue.shift();if(!t)return;this.activeRuns+=1,this.callbacks.onTaskTriggered(t).finally(async()=>{this.activeRuns-=1,await this.processQueue()})}}};var Ts=class i{constructor(t,e){this.repository=t;this.settings=e;this.executor=new cn(e,t),this.mcpManager=new hn(e),this.memoryWriter=new Jt(t),this.scheduler=new un(e.maxConcurrentRuns,{onTaskTriggered:s=>this.runPendingTask(s),onTaskScheduled:(s,n)=>this.repository.updateTaskRunMetadata(s,{nextRun:n})})}scheduler;executor;mcpManager;snapshot={agents:[],skills:[],tasks:[],channels:[],validationIssues:[]};runtimeState=new Map;recentRuns=[];chartRuns=[];static CHART_WINDOW_DAYS=14;statusChangeListeners=new Set;runOutputListeners=new Map;runOutputBuffers=new Map;heartbeatJobs=new Map;reflectionJobs=new Map;reflectionsInFlight=new Set;reflectionRunning=0;reflectionWaiters=[];heartbeatRegisteredAt=0;heartbeatsInFlight=new Set;heartbeatResultHandler;memoryWriter;async initialize(){this.snapshot=await this.repository.loadAll(),await this.repository.migrateAllLegacyMemory(),await this.refreshRunCaches();let t=this.snapshot.tasks.filter(e=>this.repository.getAgentByName(e.agent)?.enabled!==!1);await this.scheduler.loadTasks(t),await this.scheduler.handleStartupCatchUp(t),this.registerHeartbeats(),this.registerReflections(),this.emitStatusChange()}onHeartbeatResult(t){this.heartbeatResultHandler=t}async refreshFromVault(){this.snapshot=await this.repository.loadAll(),await this.rebuildSchedules(),await this.refreshRunCaches(),this.emitStatusChange()}getSnapshot(){return this.snapshot}getRecentRuns(){return this.recentRuns}getChartRuns(){return this.chartRuns}async refreshRunCaches(){this.recentRuns=await this.repository.listRecentRuns();let t=new Date;t.setDate(t.getDate()-(i.CHART_WINDOW_DAYS-1)),this.chartRuns=await this.repository.listRunsSince(t)}getAgentState(t){let e=this.runtimeState.get(t),s=this.snapshot.agents.find(n=>n.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())}`,n=this.recentRuns.filter(o=>{let c=new Date(o.started);return`${c.getFullYear()}-${e(c.getMonth()+1)}-${e(c.getDate())}`===s}).length,a=Array.from(this.runtimeState.values()).filter(o=>o.status==="running").length,r=this.recentRuns.flatMap(o=>o.approvals??[]).filter(o=>o.status==="pending").length;return{running:a,pending:r,completedToday:n}}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 n=this.runOutputBuffers.get(t);return n&&e(n),()=>{this.runOutputListeners.get(t)?.delete(e)}}getRunOutputBuffer(t){return this.runOutputBuffers.get(t)??""}async handleVaultChange(t){await this.repository.loadFile(t),this.snapshot=this.repository.getSnapshot(),await this.rebuildSchedules(),await this.refreshRunCaches(),this.emitStatusChange()}async handleVaultDelete(t){this.repository.removeFile(t),this.snapshot=this.repository.getSnapshot(),await this.rebuildSchedules(),await this.refreshRunCaches(),this.emitStatusChange()}abortedAgents=new Set;abortAgentRun(t){let e=this.executor.abortAgent(t);return e&&(this.abortedAgents.add(t),this.runtimeState.set(t,{status:"idle"}),this.emitStatusChange()),e}wasAborted(t){return this.abortedAgents.has(t)}consumeAborted(t){let e=this.abortedAgents.has(t);return this.abortedAgents.delete(t),e}async runTaskNow(t,e){await this.scheduler.enqueue({task:{...t,type:"immediate"},reason:"manual",promptOverride:e})}async runAgentNow(t,e){let s=t.heartbeatBody.trim()&&e==="Run now and summarize the current state."?t.heartbeatBody.trim():e,n=s===t.heartbeatBody.trim()&&t.heartbeatBody.trim().length>0,a={filePath:"",taskId:n?`heartbeat-${Date.now()}`:`manual-${Date.now()}`,agent:t.name,type:"immediate",priority:"medium",enabled:!0,created:new Date().toISOString(),runCount:0,catchUp:!1,tags:n?[...t.tags,"heartbeat"]:t.tags,body:s};await this.scheduler.enqueue({task:a,reason:n?"heartbeat":"manual",promptOverride:s})}async resolveApproval(t,e,s){t.filePath&&(await this.repository.setApprovalDecision(t.filePath,e,s),await this.refreshRunCaches(),this.emitStatusChange())}async pruneOldRuns(){let t=Date.now()-this.settings.runLogRetentionDays*24*60*60*1e3,e=await this.repository.listRecentRuns(500);for(let s of e)s.filePath&&new Date(s.started).getTime()<t&&await this.repository.trashFile(s.filePath)}async rebuildSchedules(){this.scheduler.pauseAll();for(let t of this.snapshot.tasks)this.scheduler.unregisterTask(t.taskId);this.scheduler.setMaxConcurrentRuns(this.settings.maxConcurrentRuns),await this.scheduler.loadTasks(this.snapshot.tasks.filter(t=>this.repository.getAgentByName(t.agent)?.enabled!==!1)),this.scheduler.resumeAll(),this.registerHeartbeats(),this.registerReflections()}shutdown(){for(let[,t]of this.heartbeatJobs)t.stop();this.heartbeatJobs.clear(),this.heartbeatsInFlight.clear();for(let[,t]of this.reflectionJobs)t.stop();this.reflectionJobs.clear(),this.reflectionsInFlight.clear(),this.scheduler.shutdown()}registerHeartbeats(){for(let[,t]of this.heartbeatJobs)t.stop();this.heartbeatJobs.clear(),this.heartbeatRegisteredAt=Date.now();for(let t of this.snapshot.agents)if(!(!t.enabled||!t.heartbeatEnabled||!t.heartbeatSchedule.trim()||!t.heartbeatBody.trim()))try{let e=new le(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}registerReflections(){for(let[,t]of this.reflectionJobs)t.stop();this.reflectionJobs.clear();for(let t of this.snapshot.agents){if(!t.enabled||!t.memory||!t.reflection.enabled)continue;let e=t.reflection.schedule.trim();if(e)try{let s=new le(e,{name:`reflection:${t.name}`,catch:!0,protect:!0,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone},()=>{this.runReflection(t.name)});this.reflectionJobs.set(t.name,s)}catch(s){console.error(`Agent Fleet: failed to register reflection for "${t.name}":`,s)}}}getNextReflection(t){return this.reflectionJobs.get(t)?.nextRun()??null}async runReflectionNow(t){return this.runReflection(t)}async listPendingProposals(){return(await this.repository.listProposals()).filter(e=>e.status==="pending")}async acceptProposal(t){let e=await this.repository.readProposal(t);if(!e)return{ok:!1,message:"Proposal not found."};try{let s=await this.repository.applyProposal(e);return s?(await this.repository.setProposalStatus(t,"accepted"),await this.refreshFromVault(),{ok:!0,message:`Applied \u2192 ${s}`}):{ok:!1,message:"Couldn't apply \u2014 target skill not found; proposal left pending."}}catch(s){return{ok:!1,message:`Failed to apply: ${s instanceof Error?s.message:String(s)}`}}}async rejectProposal(t){await this.repository.setProposalStatus(t,"rejected"),this.emitStatusChange()}async withReflectionSlot(t){let e=Math.max(1,this.settings.maxConcurrentRuns);this.reflectionRunning>=e?await new Promise(s=>this.reflectionWaiters.push(s)):this.reflectionRunning++;try{return await t()}finally{let s=this.reflectionWaiters.shift();s?s():this.reflectionRunning--}}async runReflection(t){let e=this.repository.getAgentByName(t);if(!e||!e.enabled||!e.memory)return{ok:!1,message:"Agent not found, disabled, or memory off."};if(this.reflectionsInFlight.has(t))return{ok:!1,message:"A reflection is already in progress."};this.reflectionsInFlight.add(t);let s=new Date().toISOString();try{await this.repository.migrateLegacyMemory(t);let n=await this.repository.readWorkingMemory(t),a=n?Je(n.sections):"",r=await this.repository.readRecentRaw(t,2),o=Ki({agentName:t,workingMemoryBody:a,recentRaw:r,tokenBudget:e.memoryTokenBudget}),c={filePath:"",taskId:`reflection-${Date.now()}`,agent:e.name,type:"immediate",priority:"low",enabled:!0,created:s,runCount:0,catchUp:!1,tags:[...e.tags,"reflection"],body:o,model:e.reflection.model||void 0},l=await this.withReflectionSlot(()=>this.executor.execute(e,c,o,void 0,{suppressMemoryCapture:!0})),d=Ji(l.outputText),h=await this.memoryWriter.reflect(e,d.sections,s);if(d.candidates.length>0){let u=await this.repository.readCandidates(t),p=Xi(u,d.candidates,s);await this.repository.writeCandidates(t,p),await this.generateProposals(e,p,s)}return await this.refreshRunCaches(),this.emitStatusChange(),h?{ok:!0,message:"Reflection complete \u2014 working memory consolidated."}:{ok:!1,message:"Reflection produced no memory block; working memory left unchanged."}}catch(n){let a=n instanceof Error?n.message:String(n);return console.warn(`Agent Fleet: reflection failed for "${t}":`,n),{ok:!1,message:`Reflection failed: ${a}`}}finally{this.reflectionsInFlight.delete(t)}}async generateProposals(t,e,s){if(!t.reflection.proposeSkills)return;let n=t.reflection.recurrenceThreshold||3,a=!1;for(let r of e){if(r.proposed||r.occurrences<n)continue;let c={id:`prop-${s.slice(0,10)}-${r.key}`.slice(0,80),type:"skill_create",agent:t.name,status:"pending",created:s,targetSkill:r.suggestedSkill||r.key,candidate:r.key,evidence:r.evidence,rationale:r.pattern,body:`This skill was proposed by reflection because the following friction recurred ${r.occurrences} times:
|
|
11961
|
+
|
|
11962
|
+
> ${r.pattern}
|
|
11963
|
+
|
|
11964
|
+
Document the reliable steps to handle this so future runs don't rediscover them.`};try{await this.repository.writeProposal(c),r.proposed=!0,a=!0}catch(l){console.warn(`Agent Fleet: failed to write proposal for "${t.name}"`,l)}}a&&await this.repository.writeCandidates(t.name,e)}async runPendingTask({task:t,promptOverride:e}){let s=this.repository.getAgentByName(t.agent);if(!s||!s.enabled)return;let n=new Date().toISOString();this.runtimeState.set(s.name,{status:"running",currentTaskId:t.taskId,runStarted:n}),this.runOutputBuffers.set(s.name,""),this.emitStatusChange();try{let a=await this.executor.execute(s,t,e,u=>{let p=this.runOutputBuffers.get(s.name)??"";this.runOutputBuffers.set(s.name,p+u);let m=this.runOutputListeners.get(s.name);if(m)for(let f of m)f(u)}),r=this.consumeAborted(s.name),o=r?[]:this.buildApprovals(s,a.toolsUsed),c=r?"cancelled":this.resolveRunStatus(a,o),l={runId:a.runId,agent:s.name,task:t.taskId,status:c,started:n,completed:new Date().toISOString(),durationSeconds:a.durationSeconds,tokensUsed:a.tokensUsed,costUsd:a.costUsd,model:a.resolvedModel||s.model,modelSource:a.modelSource,concreteModel:a.concreteModel,exitCode:a.exitCode,tags:Array.from(new Set([...s.tags,...t.tags])),prompt:a.prompt,output:a.outputText,toolsUsed:a.toolsUsed.map(u=>`${u.tool}${u.command?`: ${u.command}`:""}`),finalResult:a.finalResult,stderr:a.stderr,approvals:o},d=await this.repository.writeRunLog(l);if(await this.repository.updateTaskRunMetadata(t,{lastRun:n,runCount:t.runCount+1}),s.memory){let p=t.tags.includes("heartbeat")?"heartbeat":`task:${t.taskId}`,m=Gs(a.outputText);try{await this.memoryWriter.capture(s,m,p,new Date().toISOString())}catch(f){console.warn(`Agent Fleet: failed to append memory for "${s.name}"`,f)}}if(t.tags.includes("heartbeat")&&!r&&s.heartbeatChannel&&l.output.trim())try{this.heartbeatResultHandler?.(s.name,s.heartbeatChannel,l.output)}catch(u){console.warn(`Agent Fleet: heartbeat channel delivery failed for ${s.name}`,u)}r&&(l.output="Task was manually stopped."),await this.refreshRunCaches(),this.runtimeState.set(s.name,{status:r||c==="success"?"idle":c==="pending_approval"?"pending":"error",currentRunId:a.runId,lastRun:{...l,filePath:d}}),r||this.notify(l)}catch(a){let r=this.consumeAborted(s.name),o=r?"cancelled":"failure",c=Ut(t,s,this.settings),l={runId:(0,Zi.randomUUID)(),agent:s.name,task:t.taskId,status:o,started:n,completed:new Date().toISOString(),durationSeconds:Math.round((Date.now()-new Date(n).getTime())/1e3),model:c.value||s.model,modelSource:c.source,exitCode:r?-1:1,tags:Array.from(new Set([...s.tags,...t.tags])),prompt:e??t.body,output:r?"Task was manually stopped.":a instanceof Error?a.message:String(a),toolsUsed:[]},d=await this.repository.writeRunLog(l);await this.refreshRunCaches(),this.runtimeState.set(s.name,{status:r?"idle":"error",lastRun:{...l,filePath:d}}),r||this.notify(l)}finally{if(s.memory)try{await this.memoryWriter.drainPending(s,new Date().toISOString())}catch(a){console.warn(`Agent Fleet: failed to drain pending memory for "${s.name}"`,a)}this.runOutputBuffers.delete(s.name),this.runOutputListeners.delete(s.name),this.emitStatusChange()}}buildApprovals(t,e){let s=e.filter(n=>t.approvalRequired.includes(n.tool)).map(n=>({tool:n.tool,command:n.command,reason:n.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=(oe(t.output).map(a=>a.trim()).find(a=>a&&!a.startsWith("{")&&!a.startsWith("["))??"").slice(0,120)||t.status,n=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 er.Notice(n,t.status==="success"?5e3:0)}emitStatusChange(){for(let t of this.statusChangeListeners)t()}};var pn=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[n,a]of this.oauthTokens)a.refreshToken&&a.expiresAt&&a.expiresAt-s<t&&e.set(n,a);return e}};var mt=require("obsidian");var mn=require("crypto"),ot=require("obsidian");function _s(){return(0,mn.randomUUID)()}function tr(i){let t=`${i.timestamp}|${i.content.slice(0,80)}`;return(0,mn.createHash)("sha1").update(t).digest("hex").slice(0,16)}var Xt=class i{constructor(t,e,s,n,a){this.agent=t;this.settings=e;this.repository=s;this.vault=n,this.channelName=a?.channelName,this.conversationId=a?.conversationId,this.channelContext=a?.channelContext,this.inAppConversationId=a?.inAppConversationId,this.threadAnchorId=a?.threadAnchorId,this.parentSession=a?.parentSession,this.memoryWriter=new Jt(s)}messages=[];isStreaming=!1;isProcessAlive=!1;settingsState=null;rememberInstall=null;get pendingTurnCount(){return this.pendingTurns}lastActiveAt=Date.now();process=null;claudeSessionId=null;vault;stdoutBuffer="";processListeners=null;basePromptSent=!1;codexTurnState=null;codexQueue=[];codexResumeAttempted=!1;codexTurnErrors=[];codexStderr="";codexPermState=null;get isCodex(){return Bt(this.agent.adapter)==="codex"}channelName;conversationId;channelContext;inAppConversationId;conversationName="";threadAnchorId;parentSession;threadAnchorIndex;threads=new Map;threadIndex={};activeOnEvent=null;turnResponseText="";displayedLen=0;turnToolCalls=[];pendingTurns=0;turnResolve=null;turnReject=null;needsCompactBeforeNextTurn=!1;lastCompactTriggerAt=0;static WATCHDOG_FALLBACK_MINUTES=10;watchdogTimer=null;getWatchdogMs(){let t=this.settings.chatWatchdogMinutes;return(typeof t=="number"&&t>0?t:i.WATCHDOG_FALLBACK_MINUTES)*60*1e3}armWatchdog(){this.clearWatchdog();let t=this.getWatchdogMs();this.watchdogTimer=setTimeout(()=>{if(this.watchdogTimer=null,!this.isStreaming)return;this.activeOnEvent?.({type:"error",content:"",errorMessage:`no response from the CLI for ${Math.round(t/6e4)} minutes \u2014 giving up`});let e=new Error("Watchdog timeout");this.handleProcessError(e);try{this.process?.kill()}catch{}},t)}clearWatchdog(){this.watchdogTimer&&(clearTimeout(this.watchdogTimer),this.watchdogTimer=null)}currentToolName;activityListeners=new Set;onActivityChange(t){return this.activityListeners.add(t),t(),()=>{this.activityListeners.delete(t)}}emitActivity(){for(let t of this.activityListeners)t()}setStreaming(t){this.isStreaming!==t&&(this.isStreaming=t,t||(this.currentToolName=void 0),this.emitActivity())}setCurrentTool(t){this.currentToolName!==t&&(this.currentToolName=t,this.emitActivity())}stats={costTotalUsd:0,turnCount:0};statsListeners=new Set;onStatsChange(t){return this.statsListeners.add(t),t({...this.stats}),()=>{this.statsListeners.delete(t)}}getStats(){return{...this.stats}}emitStats(){let t={...this.stats};for(let e of this.statsListeners)e(t)}memoryWriter;captureSource(){return`chat:${this.inAppConversationId||this.conversationId||"default"}`}getConversationName(){return this.conversationName}async setConversationName(t){this.conversationName=t,await this.persist()}refreshAgent(){let t=this.repository.getAgentByName(this.agent.name);t&&(this.agent=t)}get isThread(){return!!this.threadAnchorId}async loadPersistedState(){let t=this.getChatFilePath(),e=this.vault.getAbstractFileByPath(t);if(!(e instanceof ot.TFile))return!1;try{let s=await this.vault.cachedRead(e);if(this.isThread){let a=JSON.parse(s);return a.messages?.length>0||a.sessionId?(this.messages=(a.messages??[]).map(r=>r.id?r:{...r,id:tr(r)}),this.claudeSessionId=a.sessionId??null,this.threadAnchorIndex=a.anchorIndex,this.claudeSessionId&&(this.basePromptSent=!0),!0):!1}let n=JSON.parse(s);if(n.name&&(this.conversationName=n.name),n.messages?.length>0)return this.messages=n.messages.map(a=>a.id?a:{...a,id:tr(a)}),this.claudeSessionId=n.sessionId??null,this.threadIndex=n.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 a={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(a,null,2)}else{let a={sessionId:this.claudeSessionId,messages:this.messages,lastActive:t,name:this.conversationName||void 0,threads:Object.keys(this.threadIndex).length>0?this.threadIndex:void 0};s=JSON.stringify(a,null,2)}let n=this.vault.getAbstractFileByPath(e);n instanceof ot.TFile?await this.vault.modify(n,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 n=t.slice(0,e).split("/"),a="";for(let r of n)if(a=a?`${a}/${r}`:r,!this.vault.getAbstractFileByPath(a))try{await this.vault.createFolder(a)}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.threadAnchorId&&this.parentSession)return this.parentSession.getThreadFilePath(this.threadAnchorId);if(this.channelName&&this.conversationId){let s=this.settings.fleetFolder,n=se(this.conversationId)||"conversation";return(0,ot.normalizePath)(`${s}/channels/${this.channelName}/sessions/${n}.json`)}if(!this.inAppConversationId)throw new Error("ChatSession requires inAppConversationId (or channel options) to resolve a file path");let t=se(this.inAppConversationId)||"conversation";if(this.agent.isFolder){let s=this.agent.filePath.replace(/\/agent\.md$/,"");return(0,ot.normalizePath)(`${s}/conversations/${t}.json`)}let e=this.repository.getMemoryPath(this.agent.name).replace(/\/[^/]+$/,"");return(0,ot.normalizePath)(`${e}/${this.agent.name}-conversations/${t}.json`)}getThreadFilePath(t){let e=this.getParentChatFilePath(),s=e.replace(/\/[^/]+$/,""),n=e.slice(s.length+1).replace(/\.json$/,"");return(0,ot.normalizePath)(`${s}/${n}.threads/${t}.json`)}getParentChatFilePath(){if(this.channelName&&this.conversationId){let t=this.settings.fleetFolder,e=se(this.conversationId)||"conversation";return(0,ot.normalizePath)(`${t}/channels/${this.channelName}/sessions/${e}.json`)}if(this.inAppConversationId){let t=se(this.inAppConversationId)||"conversation";if(this.agent.isFolder){let s=this.agent.filePath.replace(/\/agent\.md$/,"");return(0,ot.normalizePath)(`${s}/conversations/${t}.json`)}let e=this.repository.getMemoryPath(this.agent.name).replace(/\/[^/]+$/,"");return(0,ot.normalizePath)(`${e}/${this.agent.name}-conversations/${t}.json`)}throw new Error("ChatSession requires inAppConversationId (or channel options) to resolve a parent file path")}async ensureProcess(){if(this.process&&this.isProcessAlive)return;this.refreshAgent();let t=["--input-format","stream-json","--output-format","stream-json","--verbose"];this.claudeSessionId&&(t.push("--resume",this.claudeSessionId),this.basePromptSent=!0);let e=Ut(null,this.agent,this.settings);ks(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 n=this.agent.cwd??this.repository.getVaultBasePath()??".";this.rememberInstall=this.agent.memory?xs(n,this.repository.getPendingDirAbsolutePath(this.agent.name),`mcp:${this.captureSource()}`):null,this.rememberInstall&&t.push(...Ss(this.rememberInstall,this.agent.adapter)),this.settingsState=nn(n,this.agent,{allowRememberTool:!!this.rememberInstall});let a=qe(this.settings.claudeCliPath,t,{cwd:n,env:{...process.env,AWS_REGION:this.settings.awsRegion}});this.process=a,this.isProcessAlive=!0,this.stdoutBuffer="",this.processListeners={onStdout:r=>this.handleStdout(r),onStderr:()=>{},onError:r=>this.handleProcessError(r),onClose:()=>this.handleProcessClose()},a.stdout.on("data",this.processListeners.onStdout),a.stderr.on("data",this.processListeners.onStderr),a.on("error",this.processListeners.onError),a.on("close",this.processListeners.onClose)}detachProcessListeners(){this.process&&this.processListeners&&(this.process.stdout?.removeListener("data",this.processListeners.onStdout),this.process.stderr?.removeListener("data",this.processListeners.onStderr),this.process.removeListener("error",this.processListeners.onError),this.process.removeListener("close",this.processListeners.onClose)),this.processListeners=null}handleStdout(t){this.isStreaming&&this.armWatchdog(),this.stdoutBuffer+=t.toString();let e=oe(this.stdoutBuffer);this.stdoutBuffer=e.pop()??"";for(let s of e){let n=s.trim();if(n)try{let a=JSON.parse(n);this.handleEvent(a)}catch{}}}handleEvent(t){if(this.isCodex){this.handleCodexEvent(t);return}if(typeof t.session_id=="string"&&(this.claudeSessionId=t.session_id),this.updateStatsFromEvent(t),t.type==="system"&&t.subtype==="compact_boundary"){let s=t.compact_metadata,n=s&&typeof s.pre_tokens=="number"?s.pre_tokens:0,a=s&&typeof s.post_tokens=="number"?s.post_tokens:0;a>0&&(this.stats.contextTokensUsed=a),this.stats.lastCompact={preTokens:n,postTokens:a},this.emitStats(),this.needsCompactBeforeNextTurn=!1,this.activeOnEvent?.({type:"compacted",content:"",compact:{preTokens:n,postTokens:a}});return}if(t.type==="result"){if(t.is_error===!0||typeof t.api_error_status=="string"&&t.api_error_status){let s=this.describeResultError(t);this.activeOnEvent?.({type:"error",content:"",errorMessage:s})}this.handleTurnEnd();return}let e=this.parseStreamEvent(t);e&&this.dispatchStreamEvent(e)}dispatchStreamEvent(t){let e=t;if(t.type==="text"){let s=this.turnResponseText.length===0;if(s&&(this.displayedLen=0),this.turnResponseText+=t.content,this.setCurrentTool(void 0),s&&this.turnResponseText.length>0&&this.emitActivity(),this.agent.memory){let n=Qa(this.turnResponseText),a=n.length>this.displayedLen?n.slice(this.displayedLen):"";this.displayedLen=n.length,e={...t,content:a}}}else t.type==="tool_use"&&t.toolName&&(this.turnToolCalls.push({name:t.toolName,command:t.content||void 0}),this.setCurrentTool(t.toolName));this.activeOnEvent?.(e)}handleCodexEvent(t){this.codexTurnState||(this.codexTurnState=pa());let e=Wi(t,this.codexTurnState);for(let s of e)switch(s.kind){case"session":this.claudeSessionId=s.sessionId;break;case"text":this.dispatchStreamEvent({type:"text",content:s.text});break;case"tool":this.dispatchStreamEvent({type:"tool_use",content:s.command?s.command.slice(0,150):"",toolName:s.toolName});break;case"usage":{s.contextTokens>0&&s.contextTokens!==this.stats.contextTokensUsed&&(this.stats.contextTokensUsed=s.contextTokens),this.stats.turnCount+=1,this.emitStats();break}case"turn-failed":case"error":this.codexTurnErrors.push(s.message),this.activeOnEvent?.({type:"error",content:"",errorMessage:s.message});break}}get hasCurrentTurnText(){return this.turnResponseText.length>0}describeResultError(t){let e=[],s=typeof t.api_error_status=="string"?t.api_error_status:"",n=typeof t.subtype=="string"?t.subtype:"",a=typeof t.result=="string"?t.result:"";return s?e.push(`API ${s}`):n?e.push(n.replace(/_/g," ")):e.push("unknown error"),a&&e.push(`\u2014 ${a}`),e.join(" ")}updateStatsFromEvent(t){let e=!1,s=typeof t.model=="string"?t.model:void 0,n=t.message,a=n&&typeof n.model=="string"?n.model:void 0,r=s||a;if(r&&r!==this.stats.concreteModel&&(this.stats.concreteModel=r,e=!0),t.type==="rate_limit_event"){let o=t.rate_limit_info;o&&(this.stats.rateLimit={type:typeof o.rateLimitType=="string"?o.rateLimitType:"unknown",resetsAt:typeof o.resetsAt=="number"?o.resetsAt:void 0,status:typeof o.status=="string"?o.status:void 0,isUsingOverage:typeof o.isUsingOverage=="boolean"?o.isUsingOverage:void 0},e=!0)}if(t.type==="assistant"&&n){let o=n.usage;if(o){let c=typeof o.input_tokens=="number"?o.input_tokens:0,l=typeof o.cache_read_input_tokens=="number"?o.cache_read_input_tokens:0,d=typeof o.cache_creation_input_tokens=="number"?o.cache_creation_input_tokens:0,h=c+l+d;h>0&&h!==this.stats.contextTokensUsed&&(this.stats.contextTokensUsed=h,e=!0)}}if(t.type==="result"){let o=typeof t.total_cost_usd=="number"?t.total_cost_usd:0;o>0&&(this.stats.costTotalUsd+=o,e=!0);let c=t.modelUsage;if(c)for(let l of Object.values(c)){let d=l;typeof d.contextWindow=="number"&&d.contextWindow!==this.stats.contextWindow&&(this.stats.contextWindow=d.contextWindow,e=!0)}this.stats.turnCount+=1,e=!0}t.type==="result"&&this.evaluateAutoCompact(),e&&this.emitStats()}evaluateAutoCompact(){if(this.isCodex)return;let t=this.agent.autoCompactThreshold??0;if(t<=0||t>=100)return;let e=this.stats.contextWindow,s=this.stats.contextTokensUsed;!e||!s||s/e*100<t||Date.now()-this.lastCompactTriggerAt<3e4||(this.needsCompactBeforeNextTurn=!0)}handleTurnEnd(){this.lastActiveAt=Date.now();let t=this.turnResponseText,e=t;if(this.agent.memory){let n=Gs(t);e=ds(t),n.length>0&&this.memoryWriter.capture(this.agent,n,this.captureSource(),new Date().toISOString()).catch(a=>console.warn(`Agent Fleet: chat memory capture failed for "${this.agent.name}"`,a)),this.memoryWriter.drainPending(this.agent,new Date().toISOString()).catch(a=>console.warn(`Agent Fleet: chat pending drain failed for "${this.agent.name}"`,a))}e.trim()&&this.messages.push({id:_s(),role:"assistant",content:e,timestamp:new Date().toISOString(),toolCalls:this.turnToolCalls.length>0?[...this.turnToolCalls]:void 0});let s={text:this.turnResponseText,toolCalls:[...this.turnToolCalls]};if(this.activeOnEvent?.({type:"result",content:"",toolCalls:[...this.turnToolCalls]}),this.turnResponseText="",this.turnToolCalls=[],this.pendingTurns--,this.isCodex){let n=this.codexQueue.shift();if(n!==void 0){this.armWatchdog(),this.startCodexTurn(n).catch(a=>{this.handleProcessError(a instanceof Error?a:new Error(String(a)))});return}this.pendingTurns=0}if(this.pendingTurns<=0){this.pendingTurns=0,this.clearWatchdog(),this.setStreaming(!1),this.persist();let n=this.turnResolve;this.turnResolve=null,this.turnReject=null,n?.(s)}}handleProcessError(t){this.isProcessAlive=!1,this.process=null,this.pendingTurns=0,this.turnResponseText="",this.turnToolCalls=[],this.codexQueue=[],this.clearWatchdog(),this.setStreaming(!1),Nt(this.settingsState),this.settingsState=null,this.codexPermState?.restore(),this.codexPermState=null,At(this.rememberInstall),this.rememberInstall=null;let e=this.turnReject;this.turnResolve=null,this.turnReject=null,e?.(t)}handleProcessClose(){if(this.isProcessAlive=!1,this.process=null,Nt(this.settingsState),this.settingsState=null,At(this.rememberInstall),this.rememberInstall=null,this.turnResolve){let t={text:this.turnResponseText,toolCalls:[...this.turnToolCalls]};this.turnResponseText.trim()&&this.messages.push({id:_s(),role:"assistant",content:this.turnResponseText,timestamp:new Date().toISOString(),toolCalls:this.turnToolCalls.length>0?[...this.turnToolCalls]:void 0}),this.pendingTurns=0,this.turnResponseText="",this.turnToolCalls=[],this.clearWatchdog(),this.setStreaming(!1),this.persist();let e=this.turnResolve;this.turnResolve=null,this.turnReject=null,e?.(t)}}async sendMessage(t,e,s,n){this.lastActiveAt=Date.now(),this.messages.push({id:_s(),role:"user",content:t,timestamp:new Date().toISOString(),attachments:n&&n.length>0?n:void 0});let a=s??t;if(this.basePromptSent||(a=`${await this.buildBasePrompt()}
|
|
11795
11965
|
|
|
11796
11966
|
## Task
|
|
11797
|
-
${
|
|
11798
|
-
`)};try{
|
|
11799
|
-
`)}catch(
|
|
11800
|
-
${
|
|
11801
|
-
${
|
|
11802
|
-
${
|
|
11803
|
-
${
|
|
11967
|
+
${a}`,this.basePromptSent=!0),this.isCodex){this.stats.lastCompact&&(this.stats.lastCompact=void 0,this.emitStats()),this.activeOnEvent=e,this.turnResponseText="",this.turnToolCalls=[],this.pendingTurns=1,this.setStreaming(!0),this.armWatchdog();try{await this.startCodexTurn(a)}catch(c){throw this.pendingTurns=0,this.clearWatchdog(),this.setStreaming(!1),new Error(`Failed to start Codex process: ${c instanceof Error?c.message:String(c)}`)}return new Promise((c,l)=>{this.turnResolve=c,this.turnReject=l})}await this.ensureProcess();let r=this.needsCompactBeforeNextTurn;r&&(this.needsCompactBeforeNextTurn=!1,this.lastCompactTriggerAt=Date.now()),this.stats.lastCompact&&(this.stats.lastCompact=void 0,this.emitStats()),this.activeOnEvent=e,this.turnResponseText="",this.turnToolCalls=[],this.pendingTurns=r?2:1,this.setStreaming(!0),this.armWatchdog();let o=c=>{let l=JSON.stringify({type:"user",message:{role:"user",content:c}});this.process.stdin.write(l+`
|
|
11968
|
+
`)};try{r&&o("/compact"),o(a)}catch(c){throw this.pendingTurns=0,this.setStreaming(!1),new Error(`Failed to write to Claude process stdin: ${c instanceof Error?c.message:String(c)}`)}return new Promise((c,l)=>{this.turnResolve=c,this.turnReject=l})}scheduleCompact(){this.isCodex||(this.needsCompactBeforeNextTurn=!0)}async startCodexTurn(t){this.refreshAgent();let e=Ut(null,this.agent,this.settings),s=await ws.buildExec({prompt:t,model:ks(e.value)?e.value:"",modelSource:e.source,effort:this.agent.effort??"",agent:this.agent,settings:this.settings,streaming:!0,resumeSessionId:this.claudeSessionId});e.value&&this.stats.concreteModel!==e.value&&(this.stats.concreteModel=e.value,this.emitStats()),this.codexTurnState=pa(),this.codexResumeAttempted=!!this.claudeSessionId,this.codexTurnErrors=[],this.codexStderr="";let n=this.agent.cwd??this.repository.getVaultBasePath()??".";this.rememberInstall=this.agent.memory?xs(n,this.repository.getPendingDirAbsolutePath(this.agent.name),`mcp:${this.captureSource()}`):null,this.rememberInstall&&s.args.push(...Ss(this.rememberInstall,this.agent.adapter)),this.codexPermState=await ws.setupPermissions(n,this.agent,this.settings);let a=qe(s.cliPath,s.args,{cwd:n,env:{...process.env,AWS_REGION:this.settings.awsRegion,...this.codexPermState?.env??{}}});this.process=a,this.isProcessAlive=!0,this.stdoutBuffer="",this.processListeners={onStdout:r=>this.handleStdout(r),onStderr:r=>{this.codexStderr+=r.toString()},onError:r=>this.handleProcessError(r),onClose:r=>this.handleCodexProcessClose(r)},a.stdout.on("data",this.processListeners.onStdout),a.stderr.on("data",this.processListeners.onStderr),a.on("error",this.processListeners.onError),a.on("close",this.processListeners.onClose);try{a.stdin.write(s.stdinPayload??t),a.stdin.end()}catch(r){try{a.kill()}catch{}throw r instanceof Error?r:new Error(String(r))}}handleCodexProcessClose(t){if(this.detachProcessListeners(),this.isProcessAlive=!1,this.process=null,this.stdoutBuffer="",this.codexPermState?.restore(),this.codexPermState=null,At(this.rememberInstall),this.rememberInstall=null,t!==0&&!this.turnResponseText.trim()){let s=this.codexTurnErrors.join("; ")||this.codexStderr.trim().slice(-500)||`Codex CLI exited with code ${t??"unknown"}`;this.codexResumeAttempted&&this.clearSessionId(),this.codexQueue=[],this.activeOnEvent?.({type:"error",content:"",errorMessage:s})}this.handleTurnEnd()}injectMessage(t,e,s){if(this.isCodex){if(!this.isStreaming&&this.pendingTurns===0)return;this.messages.push({id:_s(),role:"user",content:t,timestamp:new Date().toISOString(),attachments:s&&s.length>0?s:void 0}),this.codexQueue.push(e??t),this.pendingTurns++;return}if(!this.process||!this.isProcessAlive)return;this.messages.push({id:_s(),role:"user",content:t,timestamp:new Date().toISOString(),attachments:s&&s.length>0?s:void 0});let a=JSON.stringify({type:"user",message:{role:"user",content:e??t}});try{this.process.stdin.write(a+`
|
|
11969
|
+
`)}catch(r){console.warn("Agent Fleet: injectMessage stdin write failed",r);return}this.pendingTurns++}abort(){this.detachProcessListeners(),this.process&&(this.process.kill(),this.process=null),this.isProcessAlive=!1,this.stdoutBuffer="",this.turnResponseText="",this.turnToolCalls=[],this.pendingTurns=0,this.codexQueue=[],this.clearWatchdog(),this.setStreaming(!1),Nt(this.settingsState),this.settingsState=null,this.codexPermState?.restore(),this.codexPermState=null,At(this.rememberInstall),this.rememberInstall=null;let t=this.turnReject;this.turnResolve=null,this.turnReject=null,t?.(new Error("Aborted"))}dispose(){for(let t of this.threads.values())t.abort();this.threads.clear(),this.threadIndex={},this.abort()}hibernate(){this.isStreaming||this.pendingTurns>0||(this.detachProcessListeners(),this.process&&(this.process.kill(),this.process=null),this.isProcessAlive=!1,this.stdoutBuffer="",Nt(this.settingsState),this.settingsState=null,At(this.rememberInstall),this.rememberInstall=null)}clearSessionId(){this.claudeSessionId=null,this.basePromptSent=!1}async buildBasePrompt(){let t=[this.agent.body.trim()];for(let s of this.agent.skills){let n=this.repository.getSkillByName(s);if(n){let a=[n.body.trim()];n.toolsBody.trim()&&a.push(`### Tools
|
|
11970
|
+
${n.toolsBody.trim()}`),n.referencesBody.trim()&&a.push(`### References
|
|
11971
|
+
${n.referencesBody.trim()}`),n.examplesBody.trim()&&a.push(`### Examples
|
|
11972
|
+
${n.examplesBody.trim()}`),t.push(`## Skill: ${n.name}
|
|
11973
|
+
${a.join(`
|
|
11804
11974
|
|
|
11805
11975
|
`)}`)}}if(this.agent.skillsBody.trim()&&t.push(`## Agent Skills
|
|
11806
11976
|
${this.agent.skillsBody.trim()}`),this.agent.contextBody.trim()&&t.push(`## Agent Context
|
|
11807
|
-
${this.agent.contextBody.trim()}`),this.agent.memory){let s=await this.repository.
|
|
11808
|
-
${
|
|
11809
|
-
${this.channelContext.trim()}`);let e=As(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
|
|
11977
|
+
${this.agent.contextBody.trim()}`),this.agent.memory){let s=await this.repository.readWorkingMemory(this.agent.name),n=Ys(this.agent,s);n&&t.push(n)}this.channelContext&&this.channelContext.trim()&&t.push(`## Channel Context
|
|
11978
|
+
${this.channelContext.trim()}`);let e=on(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.",n=this.parentSession.messages.slice(0,this.threadAnchorIndex+1),a=["## Conversation so far"];for(let r of n){let o=r.role==="user"?"User":"Assistant";a.push(`${o}: ${r.content.trim()}`)}t.push(`## Thread Mode
|
|
11810
11979
|
${s}
|
|
11811
11980
|
|
|
11812
|
-
${
|
|
11981
|
+
${a.join(`
|
|
11813
11982
|
`)}`)}return t.filter(Boolean).join(`
|
|
11814
11983
|
|
|
11815
|
-
`)}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(
|
|
11816
|
-
`&&(e+=1),t.push({kind:"code",text:
|
|
11984
|
+
`)}async openOrCreateThread(t){if(this.isThread)throw new Error("Nested threads are not supported.");let e=this.threads.get(t);if(e)return e;let s=this.messages.findIndex(r=>r.id===t);if(s<0)throw new Error(`Thread anchor message "${t}" not found in parent.`);let n=new i(this.agent,this.settings,this.repository,this.vault,{threadAnchorId:t,parentSession:this});return n.threadAnchorIndex=s,await n.loadPersistedState()||(n.threadAnchorIndex=s),this.threads.set(t,n),n}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 n of s.content){if(n.type==="text"&&typeof n.text=="string")return{type:"text",content:n.text};if(n.type==="tool_use"){let a=String(n.name??"tool"),r=n.input,o=r?.command??r?.content??r?.file_path??r?.path??"";return{type:"tool_use",content:o?String(o).slice(0,150):"",toolName:a}}}}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 fn=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,a=(this.buckets.get(t)??[]).filter(r=>r>s);return a.length>=this.config.maxPerWindow?(this.buckets.set(t,a),!1):(a.push(e),this.buckets.set(t,a),!0)}currentCount(t){let s=this.now()-this.config.windowMs;return(this.buckets.get(t)??[]).filter(a=>a>s).length}reset(t){this.buckets.delete(t)}resetAll(){this.buckets.clear()}};function gn(i){let t=Fl(i),e=[];for(let s of t)if(s.kind==="code"){let n=s.text.replace(/^```[^\n]*\n/,"```\n");e.push(n)}else e.push(Ol(s.text));return e.join("")}function Fl(i){let t=[],e=0,s=!1,n=0;for(;e<i.length;)i.startsWith("```",e)?s?(e+=3,i[e]===`
|
|
11985
|
+
`&&(e+=1),t.push({kind:"code",text:i.slice(n,e)}),n=e,s=!1):(e>n&&t.push({kind:"prose",text:i.slice(n,e)}),n=e,s=!0,e+=3):e+=1;return n<i.length&&t.push({kind:s?"code":"prose",text:i.slice(n)}),t}function Ol(i){return Nl(i).map(s=>{if(s.isCode)return s.text;let n=s.text;return n=n.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"),n=n.replace(/\[([^\]]+)\]\(([^)]+)\)/g,(a,r,o)=>`<${o}|${r}>`),n=n.replace(/\*\*([^*]+)\*\*/g,"*$1*"),n=n.replace(/^#{1,6}\s+(.+)$/gm,"*$1*"),n}).join("")}function Nl(i){let t=[],e=/`([^`\n]+)`/g,s=0,n;for(;n=e.exec(i);)n.index>s&&t.push({text:i.slice(s,n.index),isCode:!1}),t.push({text:n[0],isCode:!0}),s=n.index+n[0].length;return s<i.length&&t.push({text:i.slice(s),isCode:!1}),t}function sr(i,t=3e3){if(i.length<=t)return[i];let e=[],s=i;for(;s.length>t;){let n=s.slice(0,t),a=Bl(n)%2===1,r;if(a){let o=Ul(n);if(o>0)r=o;else{e.push(n+"\n```"),s="```\n"+s.slice(t);continue}}else{if(r=n.lastIndexOf(`
|
|
11817
11986
|
|
|
11818
|
-
`),
|
|
11819
|
-
`);o>t/2?
|
|
11987
|
+
`),r<t/2){let o=n.lastIndexOf(`
|
|
11988
|
+
`);o>t/2?r=o:r=t}r<=0&&(r=t)}e.push(s.slice(0,r)),s=s.slice(r).replace(/^\n+/,"")}return s.length>0&&e.push(s),e}function Bl(i){let t=0,e=0;for(;(e=i.indexOf("```",e))!==-1;)t+=1,e+=3;return t}function Ul(i){let t=0,e=0,s=0;for(;s<i.length;){let n=i.indexOf("```",s);if(n===-1)break;t+=1,s=n+3,t%2===0&&(e=s)}return e}function $l(i,t){let e=i.trim(),s=e.toLowerCase();for(let n of t){let a=n.toLowerCase(),r=[`use ${a}:`,`use ${a}`,`@${a}:`,`@${a}`,`${a}:`];for(let o of r)if(s.startsWith(o)){let c=e.slice(o.length).trim();return{agent:n,rest:c}}}return null}var yn=class{constructor(t){this.deps=t;this.now=t.now??(()=>Date.now());let e=t.getSettings();this.rateLimiter=new fn({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,n]of this.adapters){let a=e.get(s),r=a&&this.isChannelRuntimeValid(a,t);if(!a||!a.enabled||!r){await this.tearDownChannel(s);continue}let o=this.adapterConfigs.get(s);o&&this.requiresRestart(o,a)?(await this.tearDownChannel(s),await this.bringUpChannel(a,t)):(this.adapterConfigs.set(s,a),n.config=a)}for(let[s,n]of e)!this.adapters.has(s)&&n.enabled&&this.isChannelRuntimeValid(n,t)&&await this.bringUpChannel(n,t);this.notifyStatusListeners()}getCredentials(){return this.deps.getChannelCredentials?.()??this.deps.getSettings().channelCredentials??{}}isChannelRuntimeValid(t,e){let s=e.agents.find(a=>a.name===t.defaultAgent);if(!s||s.approvalRequired.length>0)return!1;let n=this.getCredentials()[t.credentialRef];return!(!n||n.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 n;try{n=this.deps.adapterFactory(t,s)}catch(r){console.error(`Agent Fleet: failed to build adapter for channel ${t.name}`,r);return}let a=[];a.push(n.onInbound(r=>{this.handleInbound(n,r)})),a.push(n.onStatusChange(()=>this.notifyStatusListeners())),n.onAgentSwitch&&a.push(n.onAgentSwitch((r,o,c)=>{let l=`${t.name}:${r}`;this.threadBindings.set(l,o),this.persistBindings(t.name)})),this.adapters.set(t.name,n),this.adapterConfigs.set(t.name,t),this.adapterUnsubscribes.set(t.name,a),this.ensureMetrics(t.name),await this.loadBindings(t.name);try{await n.start()}catch(r){console.error(`Agent Fleet: channel adapter ${t.name} start() failed`,r)}this.notifyStatusListeners()}async tearDownChannel(t){let e=this.adapters.get(t);if(!e)return;try{await e.stop()}catch(a){console.warn(`Agent Fleet: channel adapter ${t} stop() failed`,a)}let s=this.adapterUnsubscribes.get(t);if(s)for(let a of s)a();this.adapterUnsubscribes.delete(t),this.adapters.delete(t),this.adapterConfigs.delete(t);let n=`${t}:`;for(let[a,r]of this.sessions)if(a.startsWith(n)){try{r.session.isStreaming?r.session.abort():r.session.hibernate()}catch{}this.sessions.delete(a)}}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,n=`${s.name}:${e.conversationId}`;if(!(s.allowedUsers.length>0&&(!e.externalUserId||!s.allowedUsers.includes(e.externalUserId)))){if(!this.rateLimiter.tryConsume(n)){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(a){console.warn(`Agent Fleet: rate-limit reply failed on ${s.name}`,a)}return}await this.withConversationLock(n,async()=>{let a=this.ensureMetrics(s.name);a.messagesReceived+=1,a.lastMessageAt=this.now();let r=this.resolveAllowedAgents(s),o=$l(e.text,r),c,l;if(o){c=o.agent,l=o.rest;let h=this.threadBindings.get(n);if(this.threadBindings.set(n,c),this.persistBindings(s.name),h!==c)try{await t.setThreadTitle?.(e.conversationId,c)}catch{}if(!l){try{await t.send(e.conversationId,`_Now chatting with *${c}*. Send your next message to start._`)}catch{}return}}else{let h=`${s.name}:${e.conversationId.replace(/:thread:[^:]+$/,`:user:${e.externalUserId}`)}`;c=this.threadBindings.get(n)??this.threadBindings.get(h)??s.defaultAgent,l=e.text}try{await t.setTyping(e.conversationId,!0)}catch{}if(e.images&&e.images.length>0){let h=await this.saveInboundImages(e.images);h&&(l=h+(l||"Please analyze this image."))}let d="";try{let u=await(await this.getOrCreateSession(s,e.conversationId,c)).sendMessage(l,()=>{});if(d=u.text.trim(),u.toolCalls.length>0){let p=jl(u.toolCalls);p&&(d+=`
|
|
11820
11989
|
|
|
11821
|
-
_${p}_`)}}catch(h){console.error(`Agent Fleet: channel turn failed on ${s.name}/${e.conversationId}`,h),d=`_Sorry \u2014 the agent run failed. ${h instanceof Error?h.message:String(h)}_`}try{d&&((s.allowedAgents.length>1||s.allowedAgents.length===0&&this.resolveAllowedAgents(s).length>1)&&(d=`*[${
|
|
11822
|
-
${d}`),await this.deliverReply(t,e.conversationId,d),
|
|
11990
|
+
_${p}_`)}}catch(h){console.error(`Agent Fleet: channel turn failed on ${s.name}/${e.conversationId}`,h),d=`_Sorry \u2014 the agent run failed. ${h instanceof Error?h.message:String(h)}_`}try{d&&((s.allowedAgents.length>1||s.allowedAgents.length===0&&this.resolveAllowedAgents(s).length>1)&&(d=`*[${c}]*
|
|
11991
|
+
${d}`),await this.deliverReply(t,e.conversationId,d),a.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 n=sr(s);for(let a of n)await t.send(e,a)}async saveInboundImages(t){let s=`${this.deps.getSettings().fleetFolder}/chat-images`,n=[];for(let a of t)try{this.deps.vault.getAbstractFileByPath((0,mt.normalizePath)(s))||await this.deps.vault.createFolder((0,mt.normalizePath)(s));let r=(0,mt.normalizePath)(`${s}/${a.filename}`),o=r;if(this.deps.vault.getAbstractFileByPath(r)){let h=a.filename.lastIndexOf("."),u=h>0?a.filename.slice(0,h):a.filename,p=h>0?a.filename.slice(h):"";o=(0,mt.normalizePath)(`${s}/${u}_${Date.now()}${p}`)}await this.deps.vault.createBinary(o,a.data);let l=this.deps.vault.adapter.basePath??"",d=l?`${l}/${o}`:o;n.push(`### Image: ${a.filename}
|
|
11823
11992
|
The image file is located at: ${d}
|
|
11824
|
-
Please read and analyze this image.`)}catch(
|
|
11993
|
+
Please read and analyze this image.`)}catch(r){console.warn("Agent Fleet: failed to save inbound image",a.filename,r)}return n.length===0?"":`## Attached Images
|
|
11825
11994
|
|
|
11826
|
-
${
|
|
11995
|
+
${n.join(`
|
|
11827
11996
|
|
|
11828
11997
|
`)}
|
|
11829
11998
|
|
|
11830
11999
|
---
|
|
11831
12000
|
|
|
11832
|
-
`}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 Bt(o,this.deps.getSettings(),i,this.deps.vault,{channelName:t.name,conversationId:`${e}:${s}`,channelContext:t.channelContext||void 0});try{await l.loadPersistedState()}catch{}return this.sessions.set(a,{session:l,channelName:t.name,conversationId:e,sessionKey:a}),l}resolveAllowedAgents(t){return t.allowedAgents.length>0?t.allowedAgents:this.deps.getRepository().getSnapshot().agents.filter(a=>a.enabled).map(a=>a.name)}async persistBindings(t){let e=`${t}:`,s={};for(let[o,l]of this.threadBindings)o.startsWith(e)&&(s[o.slice(e.length)]=l);let a=this.deps.getSettings(),n=(0,ct.normalizePath)(`${a.fleetFolder}/channels/${t}/bindings.json`),i=JSON.stringify(s,null,2);try{let o=this.deps.vault.getAbstractFileByPath(n);if(o instanceof ct.TFile)await this.deps.vault.modify(o,i);else{let l=n.slice(0,n.lastIndexOf("/"));if(!this.deps.vault.getAbstractFileByPath(l))try{await this.deps.vault.createFolder(l)}catch{}await this.deps.vault.create(n,i)}}catch(o){console.warn(`Agent Fleet: failed to persist thread bindings for ${t}`,o)}}async loadBindings(t){let e=this.deps.getSettings(),s=(0,ct.normalizePath)(`${e.fleetFolder}/channels/${t}/bindings.json`);try{let a=this.deps.vault.getAbstractFileByPath(s);if(!(a instanceof ct.TFile))return;let n=await this.deps.vault.cachedRead(a),i=JSON.parse(n);for(let[o,l]of Object.entries(i))typeof l=="string"&&this.threadBindings.set(`${t}:${o}`,l)}catch{}}getThreadAgent(t,e){return this.threadBindings.get(`${t}:${e}`)}enforceHardCap(){let t=Math.max(1,this.deps.getSettings().maxConcurrentChannelSessions),e=Array.from(this.sessions.values()).filter(a=>a.session.isProcessAlive&&!a.session.isStreaming);if(e.length<=t)return;e.sort((a,n)=>a.session.lastActiveAt-n.session.lastActiveAt);let s=e.length-t;for(let a=0;a<s;a+=1){let n=e[a];if(!n)break;try{n.session.hibernate()}catch{}}}async runHibernationSweep(){let t=Math.max(6e4,this.deps.getSettings().channelIdleTimeoutMinutes*6e4),e=this.now()-t;for(let s of this.sessions.values())if(s.session.isProcessAlive&&!s.session.isStreaming&&s.session.lastActiveAt<e)try{s.session.hibernate()}catch{}}async broadcastToChannel(t,e){let s=this.adapters.get(t);if(!s){console.warn(`Agent Fleet: broadcastToChannel \u2014 no adapter for channel ${t}`);return}if(!s.broadcast){console.warn(`Agent Fleet: broadcastToChannel \u2014 adapter ${t} does not support broadcast`);return}await s.broadcast(e)}conversationLockGen=new Map;async withConversationLock(t,e){let s=this.conversationLocks.get(t)??Promise.resolve(),a,n=new Promise(o=>{a=o}),i=(this.conversationLockGen.get(t)??0)+1;this.conversationLockGen.set(t,i),this.conversationLocks.set(t,s.then(()=>n));try{await s,await e()}finally{a(),this.conversationLockGen.get(t)===i&&(this.conversationLocks.delete(t),this.conversationLockGen.delete(t))}}ensureMetrics(t){let e=this.metrics.get(t);return e||(e={messagesReceived:0,messagesSent:0,lastMessageAt:null},this.metrics.set(t,e)),e}getMetrics(t){return{...this.ensureMetrics(t)}}getChannelStatus(t){let e=this.adapters.get(t);return e?e.getStatus():"disabled"}getConnectedCount(){let t=0;for(let e of this.adapters.values())e.getStatus()==="connected"&&(t+=1);return t}getSessionCount(t){let e=0,s=`${t}:`;for(let a of this.sessions.keys())a.startsWith(s)&&(e+=1);return e}onStatusChange(t){return this.statusListeners.add(t),()=>this.statusListeners.delete(t)}notifyStatusListeners(){for(let t of this.statusListeners)try{t()}catch{}}};function Ar(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 Er(r){return r.toLowerCase().replace(/[^a-z0-9-]/g,"-").replace(/-{2,}/g,"-").replace(/^-|-$/g,"")}function is(r,t){return`${r}-${Er(t)}`}var Ut="af-channel-cred",Us=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=is(t,e);this.storage.setSecret(a,JSON.stringify(s))}getJson(t,e){if(!this.storage)return null;let s=is(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=is(t,e);this.storage.setSecret(a,s)}getString(t,e){if(!this.storage)return null;let s=is(t,e);return this.storage.getSecret(s)||null}delete(t,e){if(!this.storage)return;let s=is(t,e);this.storage.setSecret(s,"")}listByPrefix(t){return this.storage?this.storage.listSecrets().filter(e=>e.startsWith(t+"-")):[]}};var $s=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(Ut);for(let s of e){let a=Ut+"-",n=s.startsWith(a)?s.slice(a.length):s,i=this.secretStore.getJson(Ut,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(Ut,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(Ut,t,{...e,_ref:t})}};var Ko=Ye(Ci(),1),Jo=Ye(Ks(),1),Xo=Ye(Ht(),1),Qo=Ye(Fa(),1),Zo=Ye(Ba(),1),el=Ye(Wa(),1),Mi=Ye(Zs(),1),tl=Ye(Li(),1);var Ga=Mi.default;var Fi=require("obsidian");var sl="https://slack.com/api";function al(r){let t=r.team??"unknown",e=r.channel??"unknown",s=r.thread_ts??r.ts??"unknown";return`slack:${t}:${e}:thread:${s}`}function nl(r){let t=r.split(":");return t.length>=3&&t[0]==="slack"?t[2]??null:null}function il(r){let t=r.split(":");if(t[3]==="thread"&&t[4])return t[4]}var ta=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=nl(t);if(!s){console.warn(`Agent Fleet: could not extract channel id from ${t}`);return}let a=il(t),n=Ns(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=Ns(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 Ga(t);e.on("open",()=>{}),e.on("message",n=>{this.handleSocketData(n)}),e.on("error",n=>{console.warn(`Agent Fleet: Slack WebSocket error on ${this.config.name}`,n),this.setStatus("error")}),e.on("close",()=>{this.ws=null,this.stopping||this.scheduleReconnect()}),this.ws=e;let s=setTimeout(()=>{if(this.status==="connecting"||this.status==="reconnecting"){console.warn(`Agent Fleet: Slack WebSocket connect timeout on ${this.config.name}`);try{e.close()}catch{}}},3e4);e.on("close",()=>clearTimeout(s));let a=this.onStatusChange(n=>{n==="connected"&&(clearTimeout(s),a())})}catch(e){console.error("Agent Fleet: Slack WebSocket open failed",e),this.setStatus("error"),this.scheduleReconnect();return}}handleSocketData(t){let e;try{e=JSON.parse(t.toString())}catch{return}if(e.type==="hello"){this.backoffMs=1e3,this.setStatus("connected");return}if(e.type==="disconnect"){try{this.ws?.close()}catch{}return}if(e.type==="events_api"&&e.envelope_id){this.ackEnvelope(e.envelope_id),this.routeEventPayload(e.payload);return}if(e.type==="slash_commands"&&e.envelope_id){this.ackEnvelope(e.envelope_id),this.handleSlashCommand(e.payload);return}if(e.type==="interactive"&&e.envelope_id){this.ackEnvelope(e.envelope_id),this.handleInteraction(e.payload);return}e.envelope_id&&this.ackEnvelope(e.envelope_id)}async handleSlashCommand(t){if(!t)return;let e=t.command,s=t.channel_id,a=t.user_id;if(!(!e||!s||!a)){if(e==="/agents"){let n=this.config.allowedAgents.length>0?this.config.allowedAgents:[];if(n.length===0){await this.slackApi("chat.postEphemeral",{channel:s,user:a,text:"No agents configured. Set `allowed_agents` in the channel file."});return}let i=n.map(l=>({type:"button",text:{type:"plain_text",text:l===this.config.defaultAgent?`${l} \u2713`:l,emoji:!0},action_id:`switch_agent_${l}`,value:l})),o=[{type:"section",text:{type:"mrkdwn",text:"*Select an agent to chat with:*"}}];for(let l=0;l<i.length;l+=5)o.push({type:"actions",elements:i.slice(l,l+5)});try{await this.slackApi("chat.postEphemeral",{channel:s,user:a,text:"Select an agent",blocks:o})}catch(l){console.error("Agent Fleet: /agents response failed",l)}return}try{await this.slackApi("chat.postEphemeral",{channel:s,user:a,text:`Unknown command: ${e}`})}catch(n){console.error(`Agent Fleet: slash command response failed for ${e}`,n)}}}async handleInteraction(t){if(!t||t.type!=="block_actions")return;let s=t.actions,a=t.user,n=t.channel,i=t.message;if(!s?.length||!a||!n)return;let o=s[0];if(!o)return;let l=o.action_id,c=o.value;if(!l?.startsWith("switch_agent_")||!c)return;let d=a.id,h=n.id;if(!d||!h)return;let p=`slack:${t.team?.id??"unknown"}:${h}:user:${d}`;for(let f of this.agentSwitchHandlers)try{f(p,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(f){console.warn("Agent Fleet: agent switch confirmation failed",f)}try{await this.setThreadTitle(p,c)}catch{}}ackEnvelope(t){if(!(!this.ws||this.ws.readyState!==Ga.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=al(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=`${sl}/${t}`,i=await(0,Fi.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 Va=require("obsidian"),Oi="https://api.telegram.org",sa=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=Ni(t);if(!s)return;let a=Bi(t),n=Ui(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=Ni(t);if(!s)return;let a=Bi(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=Ui(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=`${Oi}/file/bot${this.credential.botToken}/${n}`;return{data:(await(0,Va.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=`${Oi}/bot${this.credential.botToken}/${t}`,n=(0,Va.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 Ni(r){return r.startsWith("tg:")?r.split(":")[1]??null:null}function Bi(r){let t=r.split(":");if(t[2]==="topic"&&t[3])return t[3]}function Ui(r,t){if(r.length<=t)return[r];let e=[],s=r;for(;s.length>t;){let a=s.lastIndexOf(`
|
|
12001
|
+
`}async getOrCreateSession(t,e,s){let n=`${t.name}:${e}:${s}`,a=this.sessions.get(n);if(a)return a.session;let r=this.deps.getRepository(),o=r.getAgentByName(s);if(!o)throw new Error(`Channel ${t.name} bound to missing agent ${s}`);let c=new Xt(o,this.deps.getSettings(),r,this.deps.vault,{channelName:t.name,conversationId:`${e}:${s}`,channelContext:t.channelContext||void 0});try{await c.loadPersistedState()}catch{}return this.sessions.set(n,{session:c,channelName:t.name,conversationId:e,sessionKey:n}),c}resolveAllowedAgents(t){return t.allowedAgents.length>0?t.allowedAgents:this.deps.getRepository().getSnapshot().agents.filter(n=>n.enabled).map(n=>n.name)}async persistBindings(t){let e=`${t}:`,s={};for(let[o,c]of this.threadBindings)o.startsWith(e)&&(s[o.slice(e.length)]=c);let n=this.deps.getSettings(),a=(0,mt.normalizePath)(`${n.fleetFolder}/channels/${t}/bindings.json`),r=JSON.stringify(s,null,2);try{let o=this.deps.vault.getAbstractFileByPath(a);if(o instanceof mt.TFile)await this.deps.vault.modify(o,r);else{let c=a.slice(0,a.lastIndexOf("/"));if(!this.deps.vault.getAbstractFileByPath(c))try{await this.deps.vault.createFolder(c)}catch{}await this.deps.vault.create(a,r)}}catch(o){console.warn(`Agent Fleet: failed to persist thread bindings for ${t}`,o)}}async loadBindings(t){let e=this.deps.getSettings(),s=(0,mt.normalizePath)(`${e.fleetFolder}/channels/${t}/bindings.json`);try{let n=this.deps.vault.getAbstractFileByPath(s);if(!(n instanceof mt.TFile))return;let a=await this.deps.vault.cachedRead(n),r=JSON.parse(a);for(let[o,c]of Object.entries(r))typeof c=="string"&&this.threadBindings.set(`${t}:${o}`,c)}catch{}}getThreadAgent(t,e){return this.threadBindings.get(`${t}:${e}`)}enforceHardCap(){let t=Math.max(1,this.deps.getSettings().maxConcurrentChannelSessions),e=Array.from(this.sessions.values()).filter(n=>n.session.isProcessAlive&&!n.session.isStreaming);if(e.length<=t)return;e.sort((n,a)=>n.session.lastActiveAt-a.session.lastActiveAt);let s=e.length-t;for(let n=0;n<s;n+=1){let a=e[n];if(!a)break;try{a.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(),n,a=new Promise(o=>{n=o}),r=(this.conversationLockGen.get(t)??0)+1;this.conversationLockGen.set(t,r),this.conversationLocks.set(t,s.then(()=>a));try{await s,await e()}finally{n(),this.conversationLockGen.get(t)===r&&(this.conversationLocks.delete(t),this.conversationLockGen.delete(t))}}ensureMetrics(t){let e=this.metrics.get(t);return e||(e={messagesReceived:0,messagesSent:0,lastMessageAt:null},this.metrics.set(t,e)),e}getMetrics(t){return{...this.ensureMetrics(t)}}getChannelStatus(t){let e=this.adapters.get(t);return e?e.getStatus():"disabled"}getConnectedCount(){let t=0;for(let e of this.adapters.values())e.getStatus()==="connected"&&(t+=1);return t}getSessionCount(t){let e=0,s=`${t}:`;for(let n of this.sessions.keys())n.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 jl(i){if(i.length===0)return"";let t=new Map;for(let s of i)t.set(s.name,(t.get(s.name)??0)+1);let e=[];for(let[s,n]of t)e.push(n>1?`${s}\xD7${n}`:s);return`Used ${i.length} tool${i.length===1?"":"s"}: ${e.join(", ")}`}function Wl(i){return i.toLowerCase().replace(/[^a-z0-9-]/g,"-").replace(/-{2,}/g,"-").replace(/^-|-$/g,"")}function Es(i,t){return`${i}-${Wl(t)}`}var Qt="af-channel-cred",vn=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 n=Es(t,e);this.storage.setSecret(n,JSON.stringify(s))}getJson(t,e){if(!this.storage)return null;let s=Es(t,e),n=this.storage.getSecret(s);if(!n)return null;try{return JSON.parse(n)}catch{return null}}setString(t,e,s){if(!this.storage)return;let n=Es(t,e);this.storage.setSecret(n,s)}getString(t,e){if(!this.storage)return null;let s=Es(t,e);return this.storage.getSecret(s)||null}delete(t,e){if(!this.storage)return;let s=Es(t,e);this.storage.setSecret(s,"")}listByPrefix(t){return this.storage?this.storage.listSecrets().filter(e=>e.startsWith(t+"-")):[]}};var bn=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(Qt);for(let s of e){let n=Qt+"-",a=s.startsWith(n)?s.slice(n.length):s,r=this.secretStore.getJson(Qt,a);if(r){let o=r._ref??a,c={...r};delete c._ref,this.credentials.set(o,c)}}}if(this.credentials.size===0&&t){for(let[e,s]of Object.entries(t))this.credentials.set(e,s);this.secretStore?.available&&this.credentials.size>0&&this.persistToSecretStore()}}onChanged(t){this.persistCallback=t}get(t){return this.credentials.get(t)}set(t,e){this.credentials.set(t,e),this.persist()}delete(t){this.credentials.delete(t),this.secretStore?.delete(Qt,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(Qt,t,{...e,_ref:t})}};var ld=Ke(Xr(),1),cd=Ke(An(),1),dd=Ke(ts(),1),hd=Ke(Ea(),1),ud=Ke(Ra(),1),pd=Ke(Oa(),1),ro=Ke(In(),1),md=Ke(io(),1);var Ba=ro.default;var oo=require("obsidian");var fd="https://slack.com/api";function gd(i){let t=i.team??"unknown",e=i.channel??"unknown",s=i.thread_ts??i.ts??"unknown";return`slack:${t}:${e}:thread:${s}`}function yd(i){let t=i.split(":");return t.length>=3&&t[0]==="slack"?t[2]??null:null}function vd(i){let t=i.split(":");if(t[3]==="thread"&&t[4])return t[4]}var Ln=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=yd(t);if(!s){console.warn(`Agent Fleet: could not extract channel id from ${t}`);return}let n=vd(t),a=gn(e);await this.enqueueSend(s,async()=>{await this.slackApi("chat.postMessage",{channel:s,text:a,...n?{thread_ts:n}:{}})})}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 n=(await this.slackApi("conversations.open",{users:e})).channel?.id;if(!n){console.warn(`Agent Fleet: broadcast \u2014 conversations.open returned no channel for user ${e}`);return}let a=gn(t);await this.slackApi("chat.postMessage",{channel:n,text:a})}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(n){console.warn(`Agent Fleet: assistant.threads.setTitle failed on ${this.config.name}`,n)}}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(n){console.warn(`Agent Fleet: assistant.threads.setStatus (${e?"on":"off"}) failed on ${this.config.name}`,n)}}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 Ba(t);e.on("open",()=>{}),e.on("message",a=>{this.handleSocketData(a)}),e.on("error",a=>{console.warn(`Agent Fleet: Slack WebSocket error on ${this.config.name}`,a),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 n=this.onStatusChange(a=>{a==="connected"&&(clearTimeout(s),n())})}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,n=t.user_id;if(!(!e||!s||!n)){if(e==="/agents"){let a=this.config.allowedAgents.length>0?this.config.allowedAgents:[];if(a.length===0){await this.slackApi("chat.postEphemeral",{channel:s,user:n,text:"No agents configured. Set `allowed_agents` in the channel file."});return}let r=a.map(c=>({type:"button",text:{type:"plain_text",text:c===this.config.defaultAgent?`${c} \u2713`:c,emoji:!0},action_id:`switch_agent_${c}`,value:c})),o=[{type:"section",text:{type:"mrkdwn",text:"*Select an agent to chat with:*"}}];for(let c=0;c<r.length;c+=5)o.push({type:"actions",elements:r.slice(c,c+5)});try{await this.slackApi("chat.postEphemeral",{channel:s,user:n,text:"Select an agent",blocks:o})}catch(c){console.error("Agent Fleet: /agents response failed",c)}return}try{await this.slackApi("chat.postEphemeral",{channel:s,user:n,text:`Unknown command: ${e}`})}catch(a){console.error(`Agent Fleet: slash command response failed for ${e}`,a)}}}async handleInteraction(t){if(!t||t.type!=="block_actions")return;let s=t.actions,n=t.user,a=t.channel,r=t.message;if(!s?.length||!n||!a)return;let o=s[0];if(!o)return;let c=o.action_id,l=o.value;if(!c?.startsWith("switch_agent_")||!l)return;let d=n.id,h=a.id;if(!d||!h)return;let p=`slack:${t.team?.id??"unknown"}:${h}:user:${d}`;for(let m of this.agentSwitchHandlers)try{m(p,l,d)}catch(f){console.error("Agent Fleet: agent switch handler threw",f)}try{await this.slackApi("chat.postEphemeral",{channel:h,user:d,text:`Switched to *${l}*. Send your next message to start.`})}catch(m){console.warn("Agent Fleet: agent switch confirmation failed",m)}try{await this.setThreadTitle(p,l)}catch{}}ackEnvelope(t){if(!(!this.ws||this.ws.readyState!==Ba.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 n=e,a=s==="message"&&n.subtype===void 0,r=s==="app_mention";if(!a&&!r||n.bot_id||!n.user||!n.text||r&&(n.text=n.text.replace(/^<@[A-Z0-9]+>\s*/,"").trim(),!n.text))return;let o=gd(n),c=n.thread_ts??n.ts;if(n.channel&&c&&(this.threadContext.set(o,{channelId:n.channel,threadTs:c}),this.threadContext.size>500)){let h=this.threadContext.keys().next();h.done||this.threadContext.delete(h.value)}let l={conversationId:o,externalUserId:n.user,text:n.text,timestamp:new Date().toISOString(),meta:{slack_channel:n.channel,slack_ts:n.ts,thread_ts:n.thread_ts}};for(let d of this.inboundHandlers)try{d(l)}catch(h){console.error("Agent Fleet: Slack inbound handler threw",h)}}scheduleReconnect(){if(this.stopping||this.reconnectTimer)return;let t=this.backoffMs;this.backoffMs=Math.min(3e4,this.backoffMs*2),console.warn(`Agent Fleet: Slack channel ${this.config.name} scheduling reconnect in ${t}ms`),this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,!this.stopping&&this.connect()},t)}setStatus(t){if(this.status!==t){this.status=t;for(let e of this.statusHandlers)try{e(t)}catch{}}}async slackApi(t,e,s={}){let n=s.useAppToken?this.credential.appToken:this.credential.botToken,a=`${fd}/${t}`,r=await(0,oo.requestUrl)({url:a,method:"POST",contentType:"application/json; charset=utf-8",headers:{Authorization:`Bearer ${n}`},body:JSON.stringify(e),throw:!1});if(r.status===429){let c=Number(r.headers["retry-after"]??"1");return await new Promise(l=>setTimeout(l,Math.max(1e3,c*1e3))),this.slackApi(t,e,s)}if(r.status<200||r.status>=300)throw new Error(`Slack ${t} HTTP ${r.status}`);let o=r.json;if(o.ok===!1)throw new Error(`Slack ${t} error: ${o.error??"unknown"}`);return o}async enqueueSend(t,e){let n=(this.sendQueues.get(t)??Promise.resolve()).then(async()=>{try{await e()}finally{await new Promise(r=>setTimeout(r,1e3))}}),a=n.catch(r=>{console.warn(`Agent Fleet: Slack send queue error for ${t}`,r)});this.sendQueues.set(t,a),await n,this.sendQueues.get(t)===a&&this.sendQueues.delete(t)}};var Ua=require("obsidian"),lo="https://api.telegram.org",Fn=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=co(t);if(!s)return;let n=ho(t),a=uo(e,4096);for(let r of a)await this.tgApi("sendMessage",{chat_id:s,text:r,parse_mode:"Markdown",...n?{message_thread_id:Number(n)}:{}})}async setTyping(t,e){let s=co(t);if(!s)return;let n=ho(t),a={chat_id:s,action:"typing"};if(n&&(a.message_thread_id=Number(n)),e){let r=this.typingIntervals.get(t);r&&clearInterval(r);try{await this.tgApi("sendChatAction",a)}catch(c){console.warn("Agent Fleet: Telegram sendChatAction failed",c)}let o=setInterval(()=>{this.tgApi("sendChatAction",a).catch(()=>{})},4500);this.typingIntervals.set(t,o)}else{let r=this.typingIntervals.get(t);r&&(clearInterval(r),this.typingIntervals.delete(t))}}async setThreadTitle(t,e){}async broadcast(t){let e=this.config.allowedUsers[0];if(e)try{let s=uo(t,4096);for(let n of s)await this.tgApi("sendMessage",{chat_id:e,text:n,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),n=!!t.text;if(!t.from||!n&&!e&&!s)return;let a=t.text??t.caption??"";if(a==="/agents"||a.startsWith("/agents@")){this.handleAgentsCommand(t);return}if(a.startsWith("/"))return;let r=String(t.from.id),o=String(t.chat.id),c=t.message_thread_id?String(t.message_thread_id):void 0,l=c?`tg:${o}:topic:${c}`:`tg:${o}`;this.buildAndEmitMessage(t,a,l,r,o,c)}async buildAndEmitMessage(t,e,s,n,a,r){let o=[];try{if(t.photo&&t.photo.length>0){let l=t.photo[t.photo.length-1],d=await this.downloadFile(l.file_id,`photo_${t.message_id}.jpg`,"image/jpeg");d&&o.push(d)}if(t.document&&this.isImageMime(t.document.mime_type)){let l=t.document.file_name??`doc_${t.message_id}`,d=t.document.mime_type??"image/jpeg",h=await this.downloadFile(t.document.file_id,l,d);h&&o.push(h)}}catch(l){console.warn("Agent Fleet: Telegram image download failed",l)}let c={conversationId:s,externalUserId:n,text:e,timestamp:new Date(t.date*1e3).toISOString(),meta:{telegram_chat_id:a,telegram_message_id:t.message_id,telegram_thread_id:r,chat_type:t.chat.type},...o.length>0?{images:o}:{}};for(let l of this.inboundHandlers)try{l(c)}catch(d){console.error("Agent Fleet: Telegram inbound handler threw",d)}}async downloadFile(t,e,s){let a=(await this.tgApi("getFile",{file_id:t})).result?.file_path;if(!a)return null;let r=`${lo}/file/bot${this.credential.botToken}/${a}`;return{data:(await(0,Ua.requestUrl)({url:r,method:"GET"})).arrayBuffer,filename:e,mimeType:s}}isImageMime(t){return t?t==="image/jpeg"||t==="image/png"||t==="image/gif"||t==="image/webp"||t==="image/bmp"||t==="image/tiff":!1}async handleAgentsCommand(t){let e=String(t.chat.id),s=t.message_thread_id,n=this.config.allowedAgents.length>0?this.config.allowedAgents:[];if(n.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 a=n.map(r=>[{text:r===this.config.defaultAgent?`${r} \u2713`:r,callback_data:`switch:${r}`}]);await this.tgApi("sendMessage",{chat_id:e,text:"*Select an agent to chat with:*",parse_mode:"Markdown",reply_markup:{inline_keyboard:a},...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),n=String(t.from.id),a=String(t.message?.chat?.id??t.from.id),r=t.message?.message_thread_id?String(t.message.message_thread_id):void 0,o=r?`tg:${a}:topic:${r}`:`tg:${a}`;for(let c of this.agentSwitchHandlers)try{c(o,s,n)}catch(l){console.error("Agent Fleet: Telegram agent switch handler threw",l)}if(await this.tgApi("answerCallbackQuery",{callback_query_id:t.id,text:`Switched to ${s}`}),t.message){let l=(this.config.allowedAgents.length>0?this.config.allowedAgents:[]).map(d=>[{text:d===s?`${d} \u2713`:d,callback_data:`switch:${d}`}]);try{await this.tgApi("editMessageText",{chat_id:a,message_id:t.message.message_id,text:`*Active agent: ${s}*`,parse_mode:"Markdown",reply_markup:{inline_keyboard:l}})}catch{}}}async tgApi(t,e,s){if(s?.aborted)throw new DOMException("Aborted","AbortError");let n=`${lo}/bot${this.credential.botToken}/${t}`,a=(0,Ua.requestUrl)({url:n,method:"POST",contentType:"application/json",body:JSON.stringify(e),throw:!1}),r;if(s?r=await Promise.race([a,new Promise((o,c)=>{s.addEventListener("abort",()=>c(new DOMException("Aborted","AbortError")),{once:!0})})]):r=await a,r.status===401||r.status===403)throw new Error(`Telegram ${t} ${r.status} Unauthorized`);if(r.status===429){let c=r.json?.parameters?.retry_after??1;return await new Promise(l=>setTimeout(l,c*1e3)),this.tgApi(t,e)}if(r.status<200||r.status>=300)throw new Error(`Telegram ${t} HTTP ${r.status}: ${r.text}`);return r.json}setStatus(t){if(this.status!==t){this.status=t;for(let e of this.statusHandlers)try{e(t)}catch{}}}};function co(i){return i.startsWith("tg:")?i.split(":")[1]??null:null}function ho(i){let t=i.split(":");if(t[2]==="topic"&&t[3])return t[3]}function uo(i,t){if(i.length<=t)return[i];let e=[],s=i;for(;s.length>t;){let n=s.lastIndexOf(`
|
|
11833
12002
|
|
|
11834
|
-
`,t);
|
|
11835
|
-
`,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 ps=require("obsidian");var aa=class extends ps.ItemView{constructor(e,s){super(e);this.plugin=s}getViewType(){return St}getDisplayText(){return"Agent Fleet"}getIcon(){return"bot"}async onOpen(){this.plugin.subscribeView(this),await this.render()}async onClose(){this.plugin.unsubscribeView(this)}async render(){let e=this.contentEl;e.empty(),e.addClass("af-sidebar");let s=this.plugin.runtime.getSnapshot(),a=this.plugin.runtime.getFleetStatus(),n=e.createDiv({cls:"af-sidebar-section"});n.createDiv({cls:"af-sidebar-section-header",text:"AGENT FLEET"});let i=[{icon:"layout-dashboard",label:"Dashboard",page:"dashboard"},{icon:"bot",label:"Agents",page:"agents",badge:()=>s.agents.length},{icon:"columns-3",label:"Tasks Board",page:"kanban"},{icon:"scroll-text",label:"Run History",page:"runs"},{icon:"shield-check",label:"Approvals",page:"approvals",badge:()=>a.pending},{icon:"puzzle",label:"Skills",page:"skills",badge:()=>s.skills.length},{icon:"plug",label:"MCP Servers",page:"mcp",badge:()=>this.plugin.mcpManager.getCachedServers()?.length??0},{icon:"radio",label:"Channels",page:"channels",badge:()=>this.plugin.channelManager?.getConnectedCount()??s.channels.length}];for(let c of i){let d=n.createDiv({cls:"af-sidebar-nav-item"}),h=d.createSpan({cls:"af-sidebar-nav-icon"});(0,ps.setIcon)(h,c.icon),d.createSpan({cls:"af-sidebar-nav-label",text:c.label});let u=c.badge?.();u!==void 0&&u>0&&d.createSpan({cls:"af-sidebar-badge",text:String(u)}),d.setAttribute("role","button"),d.setAttribute("tabindex","0"),d.onclick=()=>void this.plugin.navigateDashboard(c.page),d.onkeydown=p=>{(p.key==="Enter"||p.key===" ")&&(p.preventDefault(),this.plugin.navigateDashboard(c.page))}}let o=e.createDiv({cls:"af-sidebar-section"});o.createDiv({cls:"af-sidebar-section-header",text:"AGENTS"}),s.agents.length===0&&o.createDiv({cls:"af-sidebar-empty",text:"No agents configured"});for(let c of s.agents){let d=this.plugin.runtime.getAgentState(c.name),h=o.createDiv({cls:"af-sidebar-agent-item"});h.createSpan({cls:`af-sidebar-agent-dot ${this.healthToClass(d.status)}`}),h.createSpan({cls:"af-sidebar-agent-name",text:c.name}),h.setAttribute("role","button"),h.setAttribute("tabindex","0"),h.onclick=()=>void this.plugin.navigateDashboard("agent-detail",c.name),h.onkeydown=u=>{(u.key==="Enter"||u.key===" ")&&(u.preventDefault(),this.plugin.navigateDashboard("agent-detail",c.name))}}let l=e.createDiv({cls:"af-sidebar-section"});l.createDiv({cls:"af-sidebar-section-header",text:"QUICK ACTIONS"}),this.renderQuickAction(l,"plus","New Agent",()=>void this.plugin.createAgentTemplate()),this.renderQuickAction(l,"plus","New Task",()=>void this.plugin.openCreateTask()),this.renderQuickAction(l,"plus","New Skill",()=>void this.plugin.createSkillTemplate())}renderQuickAction(e,s,a,n){let i=e.createDiv({cls:"af-sidebar-action-item"}),o=i.createSpan({cls:"af-sidebar-action-icon"});(0,ps.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 Yt=require("obsidian"),rl=["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"],na=class extends Yt.Modal{constructor(e,s,a){super(e);this.onSelect=a;this.selectedIcon=s}searchQuery="";selectedIcon;allIcons=[];gridContainer;onOpen(){this.allIcons=(0,Yt.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",rl),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,Yt.setIcon)(a,s),a.addEventListener("click",()=>{this.selectedIcon=s,this.onSelect(s),this.close()})}onClose(){this.contentEl.empty()}};var $i=require("obsidian");function D(r,t,e){let s=r.createSpan({cls:e??"af-icon"});return(0,$i.setIcon)(s,t),s}function Qe(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 ol(r){try{return new Date(r+"T12:00:00").toLocaleDateString(void 0,{month:"short",day:"numeric"})}catch{return r.slice(5)}}function ji(r,t){let e=t.length||1,s=1e3,a=6,n=4,i=4,o=s-n-i-a*(e-1),l=Math.floor(o/e),c=140,d=36,h=18,u=h+c+d,p=Math.max(1,...t.map(m=>m.success+m.failure+m.cancelled)),f=Qe("svg",{viewBox:`0 0 ${s} ${u}`,width:"100%",height:String(u),class:"af-chart-bar"});for(let m=0;m<=4;m++){let g=h+c-m/4*c;f.appendChild(Qe("line",{x1:String(n),y1:String(g),x2:String(s-i),y2:String(g),stroke:"var(--af-text-faint)","stroke-opacity":"0.15","stroke-width":"1"}))}for(let m=0;m<t.length;m++){let g=t[m],k=n+m*(l+a),b=g.success+g.failure+g.cancelled,v=b/p*c,y=g.success/p*c,x=g.cancelled/p*c,T=g.failure/p*c;if(g.success>0&&f.appendChild(Qe("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"})),g.cancelled>0&&f.appendChild(Qe("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"})),g.failure>0&&f.appendChild(Qe("rect",{x:String(k),y:String(h+c-v),width:String(l),height:String(Math.max(T,2)),fill:"var(--af-red)",opacity:"0.85"})),b===0&&f.appendChild(Qe("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 R=Qe("text",{x:String(k+l/2),y:String(h+c-v-5),"text-anchor":"middle","font-size":"10","font-weight":"600",fill:"var(--af-text-secondary)"});R.textContent=String(b),f.appendChild(R)}let _=Qe("text",{x:String(k+l/2),y:String(h+c+16),"text-anchor":"middle","font-size":"10",fill:"var(--af-text-muted)"});_.textContent=ol(g.date),f.appendChild(_)}r.appendChild(f)}function Hi(r,t,e){let l=2*Math.PI*46,c=e>0?t/e:0,d=l*c,h=l-d,u=Qe("svg",{viewBox:"0 0 130 130",width:String(130),height:String(130),class:"af-chart-donut"});u.appendChild(Qe("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(Qe("circle",{cx:String(65),cy:String(65),r:String(46),fill:"none",stroke:"var(--af-green)","stroke-width":String(12),"stroke-dasharray":`${d} ${h}`,"stroke-dashoffset":String(l*.25),"stroke-linecap":"round"}));let p=Qe("text",{x:String(65),y:String(61),"text-anchor":"middle","dominant-baseline":"middle","font-size":"24","font-weight":"700",fill:"var(--af-text-primary)"});p.textContent=`${Math.round(c*100)}%`,u.appendChild(p);let f=Qe("text",{x:String(65),y:String(83),"text-anchor":"middle","font-size":"10",fill:"var(--af-text-muted)"});f.textContent=`${t}/${e} runs`,u.appendChild(f),r.appendChild(u)}function qi(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 Wi(r,t){r.addEventListener("dragover",e=>{e.preventDefault(),r.addClass("af-drag-over")}),r.addEventListener("dragleave",e=>{let s=e.relatedTarget;(!s||!r.contains(s))&&r.removeClass("af-drag-over")}),r.addEventListener("drop",e=>{e.preventDefault();let s=e.dataTransfer?.getData("text/plain");r.removeClass("af-drag-over"),s&&t(s)})}function zi(r){let t=/^##\s+Lint\s+(\d{4}-\d{2}-\d{2})\s*$/gm,e,s=-1,a="";for(;(e=t.exec(r))!==null;)e.index>s&&(s=e.index,a=e[1]??"");if(s<0)return null;let n=r.slice(s),i=n.search(/\n##\s+(?!\s*#)/),o=i<0?n:n.slice(0,i);return{date:a,summary:ia(o,"Summary"),autoApplied:ia(o,"Auto-applied"),needsReview:ia(o,"Needs review"),refreshChained:ia(o,"Refresh chained")}}function ia(r,t){let e=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),a=new RegExp(`^###\\s+${e}\\s*$`,"m").exec(r);if(!a)return[];let n=a.index+a[0].length,i=r.slice(n),o=i.search(/\n###\s+/),c=(o<0?i:i.slice(0,o)).split(/\r?\n/).map(h=>h.trimEnd()),d=[];for(let h of c){let u=h.replace(/^\s+/,"");u.startsWith("- ")?d.push(u.slice(2).trim()):d.length>0&&(h.startsWith(" ")||h.startsWith(" "))&&(d[d.length-1]+=" "+u)}return d}var Gi={dashboard:"Dashboard",agents:"Agents",kanban:"Tasks Board",runs:"Run History",skills:"Skills Library",approvals:"Approvals",mcp:"MCP Servers",channels:"Channels","wiki-keepers":"Wiki Keepers","agent-detail":"Agent Details","task-detail":"Task Details","create-agent":"Create Agent","create-task":"Create Task","create-skill":"Create Skill","edit-agent":"Edit Agent","edit-task":"Edit Task","edit-skill":"Edit Skill","create-channel":"Create Channel","edit-channel":"Edit Channel","add-mcp-server":"Add MCP Server"},ll={dashboard:"layout-dashboard",agents:"bot",kanban:"columns-3",runs:"scroll-text",skills:"puzzle",approvals:"shield-check",mcp:"plug",channels:"radio","wiki-keepers":"library","agent-detail":"bot","task-detail":"circle-dot","create-agent":"plus","create-task":"plus","create-skill":"plus","edit-agent":"edit","edit-task":"edit","edit-skill":"edit","create-channel":"plus","edit-channel":"edit","add-mcp-server":"plus"},cl=["dashboard","agents","kanban","runs","approvals","skills","wiki-keepers","mcp","channels"],ms=class extends w.ItemView{constructor(e,s){super(e);this.plugin=s}currentPage="dashboard";detailContext;agentDetailTab="overview";streamingUnsubscribes=[];channelStatusUnsubscribe;authenticatingServers=new Set;getViewType(){return bt}getDisplayText(){return"Agent Fleet"}getIcon(){return"bot"}async onOpen(){this.plugin.subscribeView(this),this.channelStatusUnsubscribe=this.plugin.channelManager?.onStatusChange(()=>{this.currentPage==="channels"&&this.render()}),await this.render()}async onClose(){this.cleanupStreaming(),this.channelStatusUnsubscribe?.(),this.channelStatusUnsubscribe=void 0,this.plugin.unsubscribeView(this)}navigateTo(e,s){this.currentPage=e,this.detailContext=s,e!=="agent-detail"&&(this.agentDetailTab="overview"),this.render()}async render(){this.cleanupStreaming();let e=this.contentEl;e.empty(),e.addClass("af-root");let a=e.createDiv({cls:"af-app"}).createDiv({cls:"af-main-content"});this.renderTopBar(a),this.renderTabBar(a);let n=a.createDiv({cls:"af-page"});switch(this.currentPage){case"dashboard":this.renderDashboardPage(n);break;case"agents":this.renderAgentsPage(n);break;case"kanban":this.renderKanbanPage(n);break;case"runs":this.renderRunsPage(n);break;case"skills":this.renderSkillsPage(n);break;case"approvals":this.renderApprovalsPage(n);break;case"mcp":this.renderMcpPage(n);break;case"channels":this.renderChannelsPage(n);break;case"wiki-keepers":this.renderWikiKeepersPage(n);break;case"agent-detail":this.renderAgentDetailPage(n);break;case"task-detail":this.renderTaskDetailPage(n);break;case"create-agent":this.renderCreateAgentPage(n);break;case"create-task":this.renderCreateTaskPage(n);break;case"create-skill":this.renderCreateSkillPage(n);break;case"edit-agent":this.renderEditAgentPage(n);break;case"edit-task":this.renderEditTaskPage(n);break;case"edit-skill":this.renderEditSkillPage(n);break;case"create-channel":this.renderCreateChannelPage(n);break;case"edit-channel":this.renderEditChannelPage(n);break;case"add-mcp-server":this.renderAddMcpServerPage(n);break}}navigate(e,s){this.navigateTo(e,s)}cleanupStreaming(){for(let e of this.streamingUnsubscribes)e();this.streamingUnsubscribes=[]}renderTopBar(e){let s=e.createDiv({cls:"af-top-bar"}),a=s.createDiv({cls:"af-top-bar-title"});D(a,"bot","af-top-bar-icon"),a.createSpan({text:"Agent Fleet"});let n=s.createDiv({cls:"af-breadcrumb"}),i=n.createSpan({cls:"af-breadcrumb-sep"});(0,w.setIcon)(i,"chevron-right");let o=(f,m,g)=>{let k=n.createSpan({cls:m?"af-breadcrumb-link":"",text:f});m&&(k.onclick=()=>this.navigate(m,g))},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(Gi[this.currentPage])}s.createDiv({cls:"af-top-bar-spacer"});let c=s.createDiv({cls:"af-search-wrap"});D(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 f=u.createSpan({cls:"af-pill yellow"});f.createSpan({cls:"af-dot pulse"}),f.appendText(` ${h.running} Running`)}if(h.pending>0){let f=u.createSpan({cls:"af-pill blue"});f.createSpan({cls:"af-dot"}),f.appendText(` ${h.pending} Pending`)}let p=u.createSpan({cls:"af-pill green"});p.createSpan({cls:"af-dot"}),p.appendText(` ${h.completedToday} Today`)}renderTabBar(e){let s=e.createDiv({cls:"af-tab-bar"}),a=this.plugin.runtime.getSnapshot(),n=this.plugin.runtime.getFleetStatus();for(let i of cl){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,ll[i]),l.appendText(i==="dashboard"?"Overview":Gi[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),p=u.filter(y=>y.status==="success").length,f=u.filter(y=>y.status==="failure"||y.status==="timeout").length;this.renderStatCard(l,"Runs Today",String(u.length),"","activity",`${p} passed \xB7 ${f} failed \xB7 ${i.running} running`);let m=u.reduce((y,x)=>y+(x.tokensUsed??0),0),g=u.reduce((y,x)=>y+(x.costUsd??0),0),k=g>0?` \xB7 $${g.toFixed(2)}`:"";this.renderStatCard(l,"Tokens Used",Ya(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 v=s.createDiv({cls:"af-dash-split"});this.renderActivityTimeline(v,n),this.renderFleetStatusPanel(v,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"});D(o,"activity"),o.appendText(" Run Activity (14d)");let l=n.createDiv({cls:"af-chart-body"}),c=this.buildChartData(s,14);c.some(g=>g.success+g.failure+g.cancelled>0)?ji(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"});D(u,"target"),u.appendText(" Success Rate");let p=d.createDiv({cls:"af-chart-body af-chart-body-center"}),f=s.length,m=s.filter(g=>g.status==="success").length;f>0?Hi(p,m,f):this.renderEmptyState(p,"target","No data","")}toLocalDateStr(e){let s=a=>String(a).padStart(2,"0");return`${e.getFullYear()}-${s(e.getMonth()+1)}-${s(e.getDate())}`}runToLocalDate(e){return this.toLocalDateStr(new Date(e))}buildChartData(e,s){let a=[],n=new Date;for(let i=s-1;i>=0;i--){let o=new Date(n);o.setDate(o.getDate()-i);let l=this.toLocalDateStr(o),c=e.filter(d=>this.runToLocalDate(d.started)===l);a.push({date:l,success:c.filter(d=>d.status==="success").length,failure:c.filter(d=>d.status==="failure"||d.status==="timeout").length,cancelled:c.filter(d=>d.status==="cancelled").length})}return a}renderStreamingCards(e){let a=this.plugin.runtime.getSnapshot().agents.filter(l=>this.plugin.runtime.getAgentState(l.name).status==="running");if(a.length===0)return;let n=e.createDiv({cls:"af-streaming-section"}),o=n.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});D(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),u=me(h).slice(-4);d.setText(u.join(`
|
|
11836
|
-
`));let p=this.plugin.runtime.onRunOutput(s,()=>{let
|
|
11837
|
-
`)),d.scrollTop=d.scrollHeight});this.streamingUnsubscribes.push(p)}renderApprovalBanner(e,s,a){let n=e.createDiv({cls:"af-approval-banner"}),i=n.createDiv({cls:"af-approval-icon"});(0,w.setIcon)(i,"shield-check");let o=n.createDiv({cls:"af-approval-text"});o.createDiv({cls:"af-approval-title",text:`${s.agent} wants to run: ${a}`}),o.createDiv({cls:"af-approval-desc",text:`Approval required for tool: ${a}`});let 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"});D(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"});D(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:Ft(s.output,100)});let c=o.createDiv({cls:"af-tl-meta"}),d=c.createSpan();if(D(d,"clock","af-meta-icon"),d.appendText(` ${this.formatStarted(s.started)} \xB7 ${this.formatDuration(s.durationSeconds)}`),s.tokensUsed){let h=c.createSpan();D(h,"zap","af-meta-icon"),h.appendText(` ${s.tokensUsed.toLocaleString()} tokens`)}a.onclick=()=>this.openSlideover(s)}renderFleetStatusPanel(e,s){let a=e.createDiv({cls:"af-section-card"}),n=a.createDiv({cls:"af-section-header"}),i=n.createDiv({cls:"af-section-title"});D(i,"bot"),i.appendText(" Fleet Status");let l=n.createDiv({cls:"af-section-actions"}).createEl("button",{cls:"af-btn-sm primary"});D(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"});D(u,"zap","af-meta-icon"),u.appendText(" Quick Run");let p=h.createDiv({cls:"af-quick-run-row"}),f=p.createEl("select",{cls:"af-select"});for(let g of d)f.createEl("option",{text:g.name,attr:{value:g.name}});let m=p.createEl("button",{cls:"af-btn-sm primary"});D(m,"play","af-btn-icon"),m.appendText(" Run"),m.onclick=()=>void this.plugin.runAgentPrompt(f.value)}}renderAgentMini(e,s,a){let n=this.plugin.runtime.getAgentState(s.name),i=a.filter(u=>u.agent===s.name),o=this.healthToClass(n.status),l=e.createDiv({cls:"af-agent-mini"}),c=l.createDiv({cls:`af-agent-avatar ${o}`});s.avatar?.trim()?this.renderAgentAvatar(c,s):c.setText(this.getInitials(s.name));let d=l.createDiv({cls:"af-agent-info"});d.createDiv({cls:"af-agent-name",text:s.name});let h="";if(n.status==="running")h=`Running now \xB7 ${i.length} task${i.length!==1?"s":""}`;else if(!s.enabled)h=`Disabled \xB7 ${i.length} task${i.length!==1?"s":""} paused`;else{let u=i.map(p=>p.nextRun).filter(Boolean).sort()[0];h=u?`Next: ${this.formatNextRun(u)} \xB7 ${i.length} task${i.length!==1?"s":""}`:`${i.length} task${i.length!==1?"s":""}`}d.createDiv({cls:"af-agent-desc",text:h}),l.createDiv({cls:`af-agent-status-dot ${o}`}),l.onclick=()=>this.navigate("agent-detail",s.name)}renderAgentsPage(e){let s=e.createDiv({cls:"af-agents-page"}),a=this.plugin.runtime.getSnapshot(),n=s.createDiv({cls:"af-agents-toolbar"});n.createDiv({cls:"af-page-title",text:"Agents"}),n.createDiv({cls:"af-toolbar-spacer"});let i=n.createEl("button",{cls:"af-btn-sm primary"});D(i,"plus","af-btn-icon"),i.appendText(" New Agent"),i.onclick=()=>void this.plugin.createAgentTemplate();let o=s.createDiv({cls:"af-agents-grid"});if(a.agents.length===0){this.renderEmptyState(o,"bot","No agents configured","Create your first agent to get started");return}for(let 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(A=>A.agent===s.name),o=a.tasks.filter(A=>A.agent===s.name),l=e.createDiv({cls:`af-agent-card${s.enabled?"":" disabled"}`}),c=l.createDiv({cls:"af-agent-card-header"}),d=s.enabled?this.healthToClass(n.status):"disabled",h=c.createDiv({cls:`af-agent-card-avatar ${d}`});this.renderAgentAvatar(h,s);let u=c.createDiv({cls:"af-agent-card-titleblock"}),p=u.createDiv({cls:"af-agent-card-name"});if(p.appendText(s.name),s.heartbeatEnabled&&s.heartbeatSchedule){let A=p.createSpan({cls:"af-heartbeat-indicator"});(0,w.setIcon)(A,"heart-pulse"),A.title=`Heartbeat: ${s.heartbeatSchedule}`}u.createDiv({cls:"af-agent-card-desc",text:s.description??"No description"});let f=c.createDiv({cls:`af-agent-card-toggle${s.enabled?" on":""}`});f.onclick=A=>{A.stopPropagation(),this.plugin.toggleAgent(s.name,!s.enabled)};let m=l.createDiv({cls:"af-agent-card-stats"}),g=i.length,k=i.filter(A=>A.status==="success").length,b=g>0?Math.round(k/g*100):0,v=g>0?Math.round(i.reduce((A,S)=>A+S.durationSeconds,0)/g):0,y=i.reduce((A,S)=>A+(S.tokensUsed??0),0);if(this.renderAgentStat(m,String(g),"Runs"),this.renderAgentStat(m,`${b}%`,"Success"),this.renderAgentStat(m,`${v}s`,"Avg Time"),this.renderAgentStat(m,Ya(y),"Tokens"),s.skills.length>0){let A=l.createDiv({cls:"af-agent-card-skills"});for(let S of s.skills)A.createSpan({cls:"af-skill-tag",text:S})}let x=l.createDiv({cls:"af-agent-card-footer"}),T=[`Model: ${s.model}`];s.approvalRequired.length>0&&T.push(`Approval: ${s.approvalRequired.join(", ")}`),s.memory&&T.push("Memory: on"),s.enabled||T.unshift("Disabled"),x.createSpan({cls:"af-agent-card-meta",text:T.join(" \xB7 ")});let _=x.createDiv({cls:"af-agent-card-actions"});if(!s.enabled){let A=_.createEl("button",{cls:"af-btn-sm",text:"Enable"});A.onclick=S=>{S.stopPropagation(),this.plugin.toggleAgent(s.name,!0)}}let R=_.createEl("button",{cls:"af-btn-sm"});if(D(R,"edit","af-btn-icon"),R.appendText(" Edit"),R.onclick=A=>{A.stopPropagation(),this.navigate("edit-agent",s.name)},s.enabled){let A=_.createEl("button",{cls:"af-btn-sm primary"});D(A,"play","af-btn-icon"),A.appendText(" Run"),A.onclick=S=>{S.stopPropagation(),this.plugin.runAgentPrompt(s.name)}}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(v=>v.name===a);if(!n){this.renderEmptyState(s,"bot","Agent not found",`Agent "${a}" was not found`);return}let i=this.plugin.runtime.getAgentState(n.name),o=this.plugin.runtime.getRecentRuns().filter(v=>v.agent===n.name),l=s.createDiv({cls:"af-detail-header"}),c=l.createDiv({cls:"af-detail-header-left"}),d=c.createDiv({cls:`af-agent-card-avatar ${this.healthToClass(i.status)}`});this.renderAgentAvatar(d,n);let h=c.createDiv();h.createDiv({cls:"af-detail-header-name",text:n.name}),h.createDiv({cls:"af-detail-header-desc",text:n.description??"No description"});let u=l.createDiv({cls:"af-detail-header-actions"}),p=u.createEl("button",{cls:"af-btn-sm primary"});if(D(p,"message-circle","af-btn-icon"),p.appendText(" Chat"),p.onclick=()=>this.openChatSlideover(n),n.enabled){let v=u.createEl("button",{cls:"af-btn-sm"});D(v,"play","af-btn-icon"),v.appendText(" Run Now"),v.onclick=()=>void this.plugin.runAgentPrompt(n.name);let y=u.createEl("button",{cls:"af-btn-sm"});D(y,"pause","af-btn-icon"),y.appendText(" Disable"),y.onclick=()=>void this.plugin.toggleAgent(n.name,!1)}else{let v=u.createEl("button",{cls:"af-btn-sm"});D(v,"play","af-btn-icon"),v.appendText(" Enable"),v.onclick=()=>void this.plugin.toggleAgent(n.name,!0)}let f=u.createEl("button",{cls:"af-btn-sm"});D(f,"edit","af-btn-icon"),f.appendText(" Edit"),f.onclick=()=>this.navigate("edit-agent",n.name);let m=u.createEl("button",{cls:"af-btn-sm danger"});D(m,"trash-2","af-btn-icon"),m.appendText(" Delete"),m.onclick=()=>void this.plugin.deleteAgent(n.name);let g=s.createDiv({cls:"af-detail-tabs"}),k=[{id:"overview",label:"Overview",icon:"layout-dashboard"},{id:"config",label:"Config",icon:"settings"},{id:"runs",label:"Runs",icon:"scroll-text"},{id:"memory",label:"Memory",icon:"file-text"}];for(let v of k){let y=g.createEl("button",{cls:`af-detail-tab${this.agentDetailTab===v.id?" active":""}`});D(y,v.icon,"af-tab-icon"),y.appendText(` ${v.label}`),y.onclick=()=>{this.agentDetailTab=v.id,this.render()}}let b=s.createDiv({cls:"af-detail-tab-content"});switch(this.agentDetailTab){case"overview":this.renderAgentOverviewTab(b,n,o);break;case"config":this.renderAgentConfigTab(b,n);break;case"runs":this.renderAgentRunsTab(b,o);break;case"memory":this.renderAgentMemoryTab(b,n);break}}renderAgentOverviewTab(e,s,a){let n=e.createDiv({cls:"af-dash-grid"}),i=a.length,o=a.filter(v=>v.status==="success").length,l=i>0?Math.round(o/i*100):0,c=i>0?Math.round(a.reduce((v,y)=>v+y.durationSeconds,0)/i):0,d=a.reduce((v,y)=>v+(y.tokensUsed??0),0),h=a.reduce((v,y)=>v+(y.costUsd??0),0),u=h>0?` \xB7 $${h.toFixed(2)}`:"";if(this.renderStatCard(n,"Total Runs",String(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",Ya(d),"","zap",`all time${u}`),s.isFolder&&(s.heartbeatBody.trim()||s.heartbeatEnabled)){let v=e.createDiv({cls:"af-section-card"}),y=v.createDiv({cls:"af-section-header"}),x=y.createDiv({cls:"af-section-title"});D(x,"heart-pulse"),x.appendText(" Heartbeat");let _=y.createDiv({cls:"af-detail-header-actions"}).createDiv({cls:`af-agent-card-toggle${s.heartbeatEnabled?" on":""}`});_.onclick=async()=>{let S=_.hasClass("on");await this.plugin.repository.updateHeartbeat(s.name,{enabled:!S}),await this.plugin.refreshFromVault(),new w.Notice(`Heartbeat ${S?"paused":"enabled"} for ${s.name}`)};let R=v.createDiv({cls:"af-config-form"});this.renderConfigRow(R,"Schedule",dl(s.heartbeatSchedule));let A=this.plugin.runtime.getNextHeartbeat(s.name);A&&s.heartbeatEnabled&&this.renderConfigRow(R,"Next run",this.timeUntil(A)),s.heartbeatChannel&&this.renderConfigRow(R,"Channel",s.heartbeatChannel)}if(s.skills.length>0){let v=e.createDiv({cls:"af-section-card"}),x=v.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});D(x,"puzzle"),x.appendText(" Skills");let T=v.createDiv({cls:"af-detail-skills-list"});for(let _ of s.skills)T.createSpan({cls:"af-skill-tag",text:_})}let p=s.mcpServers??[];if(p.length>0){let v=e.createDiv({cls:"af-section-card"}),x=v.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});D(x,"plug"),x.appendText(" MCP Servers");let T=v.createDiv({cls:"af-mcp-overview-list"}),_=this.plugin.mcpManager.getCachedServers();for(let R of p){let A=_?.find(N=>N.name===R),S=T.createDiv({cls:"af-mcp-overview-row"}),L=S.createSpan({cls:`af-mcp-status-dot ${A?A.enabled?A.status:"disabled":"disconnected"}`});L.title=A?A.enabled?A.status:"disabled":"unknown",S.createSpan({cls:"af-mcp-overview-name",text:R});let E=A?.toolDetails.length??A?.tools.length??0;E>0?S.createSpan({cls:"af-mcp-overview-tools",text:`${E} tools`}):A&&!A.enabled?S.createSpan({cls:"af-mcp-overview-tools af-muted",text:"disabled"}):A?.status==="needs-auth"&&S.createSpan({cls:"af-mcp-overview-tools af-muted",text:"needs auth"})}}if(s.permissionRules.allow.length>0||s.permissionRules.deny.length>0||s.permissionMode&&s.permissionMode!=="default"){let v=e.createDiv({cls:"af-section-card"}),x=v.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});D(x,"shield-check"),x.appendText(" Permissions");let T=v.createDiv({cls:"af-config-form"});this.renderConfigRow(T,"Mode",s.permissionMode||"default"),s.permissionRules.allow.length>0&&this.renderConfigRow(T,"Allowed",s.permissionRules.allow.join(", ")),s.permissionRules.deny.length>0&&this.renderConfigRow(T,"Denied",s.permissionRules.deny.join(", "))}let m=e.createDiv({cls:"af-section-card"}),k=m.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});D(k,"scroll-text"),k.appendText(" Recent Runs");let b=m.createDiv({cls:"af-timeline"});if(a.length===0)this.renderEmptyState(b,"scroll-text","No runs yet","");else for(let v of a.slice(0,5))this.renderTimelineItem(b,v)}renderAgentConfigTab(e,s){let a=e.createDiv({cls:"af-config-form"});this.renderConfigRow(a,"Name",s.name),this.renderConfigRow(a,"Description",s.description??""),this.renderConfigRow(a,"Model",s.model),this.renderConfigRow(a,"Timeout",`${s.timeout}s`),this.renderConfigRow(a,"Working Directory",s.cwd??"(vault root)"),this.renderConfigRow(a,"Permission Mode",s.permissionMode||"default"),this.renderConfigRow(a,"Approval Required",s.approvalRequired.join(", ")||"none"),s.permissionRules.allow.length>0&&this.renderConfigRow(a,"Allowed Commands",s.permissionRules.allow.join(", ")),s.permissionRules.deny.length>0&&this.renderConfigRow(a,"Blocked Commands",s.permissionRules.deny.join(", ")),this.renderConfigRow(a,"Memory",s.memory?"Enabled":"Disabled"),this.renderConfigRow(a,"Auto-compact",s.autoCompactThreshold&&s.autoCompactThreshold>0?`at ${s.autoCompactThreshold}% context`:"disabled"),s.wikiReferences&&s.wikiReferences.length>0&&this.renderConfigRow(a,"Wiki access",s.wikiReferences.map(l=>l.agent).join(", ")),this.renderConfigRow(a,"Tags",s.tags.join(", ")||"none");let n=a.createDiv({cls:"af-config-prompt-section"});if(n.createDiv({cls:"af-slideover-section-title",text:"SYSTEM PROMPT"}),n.createDiv({cls:"af-output-block",text:s.body||"(empty)"}),s.heartbeatBody.trim()){let l=a.createDiv({cls:"af-config-prompt-section"});l.createDiv({cls:"af-slideover-section-title",text:"HEARTBEAT INSTRUCTION"}),l.createDiv({cls:"af-output-block",text:s.heartbeatBody})}let o=a.createDiv({cls:"af-slideover-actions"}).createEl("button",{cls:"af-btn-sm primary"});D(o,"edit","af-btn-icon"),o.appendText(" Edit Agent"),o.onclick=()=>this.navigate("edit-agent",s.name)}renderConfigRow(e,s,a){let n=e.createDiv({cls:"af-detail-row"});n.createSpan({cls:"af-detail-label",text:s}),n.createSpan({cls:"af-detail-value af-mono",text:a})}renderAgentRunsTab(e,s){if(s.length===0){this.renderEmptyState(e,"scroll-text","No runs yet","Run this agent to see history");return}for(let a of s){let n=e.createDiv({cls:"af-run-list-item"}),i=n.createDiv({cls:`af-tl-icon ${this.statusToTimelineClass(a.status)}`});(0,w.setIcon)(i,this.statusToIconName(a.status));let o=n.createDiv({cls:"af-tl-body"}),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:Ft(a.output,120)}),n.onclick=()=>this.openSlideover(a)}}async renderAgentMemoryTab(e,s){if(!s.memory){this.renderEmptyState(e,"file-text","Memory disabled","Enable memory in agent config");return}let a=await this.plugin.repository.getMemory(s.name);if(!a||!a.body.trim()){this.renderEmptyState(e,"file-text","No memories yet","Agent will learn from runs");return}e.createDiv({cls:"af-output-block"}).setText(a.body);let o=e.createDiv({cls:"af-slideover-actions"}).createEl("button",{cls:"af-btn-sm"});D(o,"external-link","af-btn-icon"),o.appendText(" Open in Editor"),o.onclick=()=>void this.plugin.openPath(this.plugin.repository.getMemoryPath(s.name))}timeAgo(e){let s=Math.round((Date.now()-e.getTime())/1e3);if(s<60)return"just now";let a=Math.round(s/60);if(a<60)return`${a}m ago`;let n=Math.round(a/60);return n<24?`${n}h ago`:`${Math.round(n/24)}d ago`}timeUntil(e){let s=Math.round((e.getTime()-Date.now())/1e3);if(s<60)return"< 1m";let a=Math.round(s/60);if(a<60)return`in ${a}m`;let n=Math.round(a/60);return n<24?`in ${n}h`:`in ${Math.round(n/24)}d`}renderKanbanPage(e){let s=e.createDiv({cls:"af-kanban-page"}),a=this.plugin.runtime.getSnapshot(),n=this.plugin.runtime.getRecentRuns(),i=s.createDiv({cls:"af-kanban-toolbar"});i.createDiv({cls:"af-page-title",text:"Tasks Board"}),i.createDiv({cls:"af-toolbar-spacer"});let o=i.createEl("button",{cls:"af-btn-sm primary"});D(o,"plus","af-btn-icon"),o.appendText(" New Task"),o.onclick=()=>this.navigate("create-task");let l=s.createDiv({cls:"af-kanban-board"}),c=[],d=[],h=[],u=[],p=[],f=new Set;for(let b of a.agents){let v=this.plugin.runtime.getAgentState(b.name);v.status==="running"&&v.currentTaskId&&f.add(v.currentTaskId)}let m=this.toLocalDateStr(new Date);for(let b of n)b.status==="success"&&this.runToLocalDate(b.started)===m?u.push(b):(b.status==="failure"||b.status==="timeout"||b.status==="cancelled")&&this.runToLocalDate(b.started)===m&&p.push(b);let g=new Set(u.map(b=>b.task)),k=new Set(p.map(b=>b.task));for(let b of a.tasks){let v=g.has(b.taskId)||k.has(b.taskId)||b.lastRun&&this.runToLocalDate(b.lastRun)===m;if(f.has(b.taskId))h.push(b);else{if(v&&!b.schedule)continue;b.schedule&&b.enabled?d.push(b):c.push(b)}}this.renderKanbanColumn(l,"Backlog","inbox",c.length,b=>{for(let v of c)this.renderKanbanTaskCard(b,v,a,!0)},"backlog"),this.renderKanbanColumn(l,"Scheduled","clock",d.length,b=>{for(let v of d)this.renderKanbanTaskCard(b,v,a,!0)},"scheduled"),this.renderKanbanColumn(l,"Running","loader-2",h.length,b=>{for(let v of h)this.renderKanbanRunningCard(b,v)},"running",!1,"running"),this.renderKanbanColumn(l,"Done","check-circle-2",u.length,b=>{for(let v of u.slice(0,10))this.renderKanbanCompletedCard(b,v)},"completed"),this.renderKanbanColumn(l,"Failed","x-circle",p.length,b=>{for(let v of p)this.renderKanbanFailedCard(b,v)},"failed",!1,"failed")}renderKanbanColumn(e,s,a,n,i,o,l=!1,c){let d=e.createDiv({cls:`af-kanban-column${c?` af-kanban-${c}`:""}`});(o==="backlog"||o==="scheduled")&&Wi(d,f=>this.handleTaskDrop(f,o));let u=d.createDiv({cls:"af-kanban-col-header"}).createDiv({cls:"af-kanban-col-title"});D(u,a),u.appendText(` ${s} `),u.createSpan({cls:"af-kanban-col-count",text:String(n)});let p=d.createDiv({cls:"af-kanban-col-body"});if(i(p),n===0&&p.createDiv({cls:"af-kanban-empty",text:"No items"}),l){let m=d.createDiv({cls:"af-kanban-col-add"}).createEl("button");D(m,"plus","af-btn-icon"),m.appendText(" Add task"),m.onclick=()=>{this.navigate("create-task")}}}handleTaskDrop(e,s){let a=this.plugin.runtime.getSnapshot().tasks.find(n=>n.taskId===e);if(a){if(s==="backlog")this.setTaskEnabled(a,!1).then(()=>{new w.Notice(`Task "${e}" moved to backlog (disabled)`)});else if(s==="scheduled"){if(!a.schedule&&!a.runAt){new w.Notice(`Task "${e}" needs a schedule. Open task details to set one.`),this.navigate("task-detail",e);return}this.setTaskEnabled(a,!0).then(()=>{new w.Notice(`Task "${e}" moved to scheduled (enabled)`)})}}}async setTaskEnabled(e,s){let a=this.plugin.app.vault.getAbstractFileByPath(e.filePath);if(!a||!(a instanceof w.TFile))return;let n=await this.plugin.app.vault.cachedRead(a),{frontmatter:i,body:o}=ae(n);i.enabled=s,await this.plugin.app.vault.modify(a,K(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){qi(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 f=i.createDiv({cls:"af-kanban-card-footer"}).createSpan({cls:"af-kanban-card-schedule"});c?s.schedule?(D(f,"refresh-cw","af-meta-icon"),f.appendText(` ${this.humanizeCron(s.schedule)}`)):f.appendText(s.runAt??"Manual"):(D(f,"pause","af-meta-icon"),f.appendText(" Paused")),i.onclick=()=>this.navigate("task-detail",s.taskId)}renderKanbanRunningCard(e,s){let a=e.createDiv({cls:"af-kanban-card af-kanban-card-running"});a.createDiv({cls:"af-kanban-card-title",text:s.taskId});let n=a.createDiv({cls:"af-kanban-card-agent"}),i=n.createSpan({cls:"af-kanban-card-agent-icon"});(0,w.setIcon)(i,"bot"),n.createSpan({cls:"af-kanban-card-agent-name",text:s.agent});let l=this.plugin.runtime.getSnapshot().agents.find(x=>x.name===s.agent)?.timeout??300,c=this.plugin.runtime.getAgentState(s.agent),d=c.runStarted?new Date(c.runStarted).getTime():Date.now(),p=a.createDiv({cls:"af-kanban-progress"}).createDiv({cls:"af-kanban-progress-track"}).createDiv({cls:"af-kanban-progress-bar af-kanban-progress-bar-real"}),f=(Date.now()-d)/1e3,m=Math.min(95,f/l*100);p.style.width=`${m}%`;let g=a.createDiv({cls:"af-kanban-card-footer"}),k=g.createSpan({cls:"af-kanban-card-schedule"});D(k,"loader-2","af-meta-icon");let b=Math.round(f);k.appendText(` ${b}s / ${l}s`);let v=g.createEl("button",{cls:"af-kanban-stop-btn"});(0,w.setIcon)(v,"square"),v.title="Stop task",v.onclick=x=>{x.stopPropagation(),this.plugin.runtime.abortAgentRun(s.agent),new w.Notice(`Stopped task "${s.taskId}"`)};let y=setInterval(()=>{let x=(Date.now()-d)/1e3,T=Math.min(95,x/l*100);p.style.width=`${T}%`;let _=Math.round(x);k.textContent="",(0,w.setIcon)(k,"loader-2"),k.appendText(` ${_}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"});D(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`:Ft(s.output,60),c=n.createDiv({cls:"af-kanban-card-error"});D(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(D(h,a?"square":"x-circle","af-meta-icon"),h.appendText(` ${this.formatStarted(s.started)}`),!a){let u=d.createEl("button",{cls:"af-btn-sm"});D(u,"refresh-cw","af-btn-icon"),u.appendText(" Retry"),u.onclick=p=>{p.stopPropagation(),this.plugin.runAgentPrompt(s.agent)}}n.onclick=()=>this.openSlideover(s)}renderRunsPage(e){let s=e.createDiv({cls:"af-runs-page"}),a=this.plugin.runtime.getRecentRuns(),n=s.createDiv({cls:"af-runs-toolbar"});n.createDiv({cls:"af-page-title",text:"Run History"}),n.createDiv({cls:"af-toolbar-spacer"});let i=s.createDiv({cls:"af-runs-table"});if(a.length===0){this.renderEmptyState(i,"scroll-text","No runs yet","Run an agent to see history here");return}let o=i.createEl("table"),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"});D(i,"plus","af-btn-icon"),i.appendText(" New Skill"),i.onclick=()=>void this.plugin.createSkillTemplate();let o=s.createDiv({cls:"af-skills-grid"});if(a.skills.length===0){this.renderEmptyState(o,"puzzle","No skills yet","Create skills to give agents specialized abilities");return}for(let 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"});D(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"});D(i,"plus","af-btn-icon"),i.appendText(" New Channel"),i.onclick=()=>this.navigate("create-channel");let o=s.createDiv({cls:"af-agents-grid"});if(a.channels.length===0){this.renderEmptyState(o,"radio","No channels configured","Connect an agent to Slack or another chat platform");return}for(let l of a.channels)this.renderChannelCard(o,l,a.validationIssues)}async renderWikiKeepersPage(e){let s=e.createDiv({cls:"af-agents-page"}),a=this.plugin.runtime.getSnapshot(),n=s.createDiv({cls:"af-agents-toolbar"});n.createDiv({cls:"af-page-title",text:"Wiki Keepers"}),n.createDiv({cls:"af-toolbar-spacer"});let i=a.agents.filter(l=>l.wikiKeeper!==void 0);if(i.length===0){this.renderEmptyState(s,"library","No Wiki Keepers yet","Open Settings \u2192 Agent Fleet \u2192 Wiki Keepers \u2192 + Add to create one.");return}let o=s.createDiv({cls:"af-wk-list"});o.style.display="flex",o.style.flexDirection="column",o.style.gap="16px";for(let l of i)await this.renderWikiKeeperCard(o,l)}async renderWikiKeeperCard(e,s){let a=s.wikiKeeper,n=e.createDiv({cls:"af-card"});n.style.padding="16px",n.style.border="1px solid var(--background-modifier-border)",n.style.borderRadius="8px";let i=n.createDiv();i.style.display="flex",i.style.alignItems="center",i.style.gap="12px",i.style.marginBottom="12px";let o=i.createDiv();o.style.flex="1",o.createEl("strong",{text:s.name});let l=a.scopeRoot||"(whole vault)";o.createEl("div",{text:`Scope: ${l} \xB7 topics: ${a.topicsRoot}/ \xB7 log: ${a.logPath}`,cls:"af-form-hint"});let c=i.createEl("button",{cls:"af-btn-sm"});c.appendText("Open log"),c.onclick=()=>{let g=a.scopeRoot?`${a.scopeRoot}/${a.logPath}`:a.logPath,k=this.plugin.app.vault.getAbstractFileByPath(g);k instanceof w.TFile?this.plugin.app.workspace.getLeaf().openFile(k):new w.Notice(`Log file not found: ${g}`)};let d=a.scopeRoot?`${a.scopeRoot}/${a.logPath}`:a.logPath,h=this.plugin.app.vault.getAbstractFileByPath(d),u=null;if(h instanceof w.TFile)try{let g=await this.plugin.app.vault.cachedRead(h);u=zi(g)}catch{u=null}if(!u){n.createDiv({cls:"af-form-hint"}).setText("No lint report yet. Run wiki-lint manually or wait for the weekly task to fire.");return}let p=n.createDiv();if(p.style.display="flex",p.style.alignItems="baseline",p.style.gap="12px",p.style.marginBottom="8px",p.createEl("strong",{text:`Lint ${u.date}`}),p.createSpan({cls:"af-form-hint",text:`${u.summary.length} summary lines \xB7 ${u.autoApplied.length} auto-applied \xB7 ${u.needsReview.length} needs review`}),u.summary.length>0){let g=n.createEl("details");g.createEl("summary",{text:"Summary"});let k=g.createEl("ul");k.style.marginTop="4px";for(let b of u.summary)k.createEl("li",{text:b})}if(u.autoApplied.length>0){let g=n.createEl("details");g.createEl("summary",{text:`Auto-applied (${u.autoApplied.length})`});let k=g.createEl("ul");k.style.marginTop="4px";for(let b of u.autoApplied)k.createEl("li",{text:b})}if(u.refreshChained.length>0){let g=n.createEl("details");g.createEl("summary",{text:`Refresh chained (${u.refreshChained.length})`});let k=g.createEl("ul");k.style.marginTop="4px";for(let b of u.refreshChained)k.createEl("li",{text:b})}let f=n.createDiv();if(f.style.marginTop="12px",f.createEl("strong",{text:`Needs review (${u.needsReview.length})`}),u.needsReview.length===0){f.createDiv({cls:"af-form-hint",text:"All clear. Nothing requires manual review from this lint pass."});return}let m=f.createDiv();m.style.display="flex",m.style.flexDirection="column",m.style.gap="6px",m.style.marginTop="8px";for(let g of u.needsReview){let k=m.createDiv();k.style.display="flex",k.style.alignItems="flex-start",k.style.gap="8px",k.style.padding="8px 10px",k.style.background="var(--background-secondary)",k.style.borderRadius="4px",k.style.fontSize="13px";let b=k.createDiv();b.style.flex="1",b.setText(g);let v=k.createEl("button",{cls:"af-btn-sm",text:"Dismiss"});v.title="Hide this item from the dashboard until the next lint pass (does not modify log.md).",v.onclick=()=>{k.remove()}}}renderChannelCard(e,s,a){let n=this.plugin.channelManager?.getChannelStatus(s.name)??"disabled",i=Vi(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 ${hl(n)}`});if(u.createSpan({cls:"af-dot"}),u.appendText(` ${n}`),s.allowedAgents.length>0){let T=l.createDiv({cls:"af-agent-card-skills"});for(let _ of s.allowedAgents){let R=T.createSpan({cls:"af-skill-tag",text:_});_===s.defaultAgent&&(R.style.fontWeight="700")}}let p=l.createDiv({cls:"af-agent-card-stats"}),f=this.plugin.channelManager?.getSessionCount(s.name)??0,m=this.plugin.channelManager?.getMetrics(s.name),g=s.allowedAgents.length>0?String(s.allowedAgents.length):"all";this.renderAgentStat(p,g,"Agents"),this.renderAgentStat(p,String(f),"Sessions"),this.renderAgentStat(p,String(m?.messagesReceived??0),"In"),this.renderAgentStat(p,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"});D(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 _ of x)T.createDiv({cls:"af-channel-issue-row",text:_.message})}}renderCreateChannelPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),a=this.plugin.runtime.getSnapshot(),n=this.plugin.channelCredentials.list(),i=s.createDiv({cls:"af-detail-header"}),o=i.createDiv({cls:"af-detail-header-left"}),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,tags:"",body:"",transportJson:""},h=s.createDiv({cls:"af-create-form"}),u=h.createDiv({cls:"af-create-section"}),p=u.createDiv({cls:"af-create-section-header"}),f=p.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(f,"radio"),p.createSpan({text:"Channel Details"}),this.createFormField(u,"Name","my-slack","Unique identifier for this channel",H=>{d.name=H});let m=u.createDiv({cls:"af-form-row"});m.createDiv({cls:"af-form-label",text:"Type"});let g=m.createEl("select",{cls:"af-form-select"});g.createEl("option",{text:"slack",attr:{value:"slack"}}),g.createEl("option",{text:"telegram",attr:{value:"telegram"}}),g.addEventListener("change",()=>{d.type=g.value});let k=u.createDiv({cls:"af-form-row"}),b=k.createDiv({cls:"af-form-label"});b.setText("Credential"),this.addTooltip(b,"Configured in Settings \u2192 Agent Fleet \u2192 Channel Credentials");let v=k.createEl("select",{cls:"af-form-select"});n.length===0&&v.createEl("option",{text:"(no credentials configured)",attr:{value:""}});for(let H of n)v.createEl("option",{text:`${H.ref} (${H.entry.type})`,attr:{value:H.ref}});v.addEventListener("change",()=>{d.credentialRef=v.value});let 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 H=x.hasClass("on");x.toggleClass("on",!H),d.enabled=!H};let T=h.createDiv({cls:"af-create-section"}),_=T.createDiv({cls:"af-create-section-header"}),R=_.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(R,"bot"),_.createSpan({text:"Agent Routing"});let A=T.createDiv({cls:"af-form-row"}),S=A.createDiv({cls:"af-form-label"});S.setText("Default agent"),this.addTooltip(S,"Used when no @agent-name prefix is given in a message");let L=A.createEl("select",{cls:"af-form-select"});for(let H of a.agents)L.createEl("option",{text:H.name,attr:{value:H.name}});L.addEventListener("change",()=>{d.defaultAgent=L.value});let E=T.createDiv({cls:"af-form-row"}),N=E.createDiv({cls:"af-form-label"});N.setText("Allowed agents"),this.addTooltip(N,"Agents reachable via @prefix. Leave unchecked to allow all agents.");let I=E.createDiv({cls:"af-form-checkboxes"});for(let H of a.agents){let be=I.createEl("label",{cls:"af-form-checkbox-label"}),we=be.createEl("input",{attr:{type:"checkbox",value:H.name}});be.appendText(` ${H.name}`),we.addEventListener("change",()=>{we.checked?d.allowedAgents.includes(H.name)||d.allowedAgents.push(H.name):d.allowedAgents=d.allowedAgents.filter(fe=>fe!==H.name)})}let q=T.createDiv({cls:"af-form-row af-form-row-toggle"}),F=q.createDiv({cls:"af-form-label"});F.setText("Per-user sessions"),this.addTooltip(F,"Each external user gets their own isolated Claude session");let $=q.createDiv({cls:"af-agent-card-toggle on"});$.onclick=()=>{let H=$.hasClass("on");$.toggleClass("on",!H),d.perUserSessions=!H};let W=h.createDiv({cls:"af-create-section"}),ie=W.createDiv({cls:"af-create-section-header"}),ue=ie.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(ue,"shield-check"),ie.createSpan({text:"Access Control"});let ee=W.createDiv({cls:"af-form-label"});ee.setText("Allowed users"),this.addTooltip(ee,"Slack user IDs (U...), one per line. Only listed users can reach the bot.");let V=W.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`U0AQW6P37N1
|
|
11838
|
-
U0BXYZ12345`,rows:"4"}});
|
|
12003
|
+
`,t);n<t/2&&(n=s.lastIndexOf(`
|
|
12004
|
+
`,t)),n<t/2&&(n=t),e.push(s.slice(0,n)),s=s.slice(n).replace(/^\n+/,"")}return s&&e.push(s),e}var Fs=require("obsidian");var On=class extends Fs.ItemView{constructor(e,s){super(e);this.plugin=s}getViewType(){return Lt}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(),n=this.plugin.runtime.getFleetStatus(),a=e.createDiv({cls:"af-sidebar-section"});a.createDiv({cls:"af-sidebar-section-header",text:"AGENT FLEET"});let r=[{icon:"layout-dashboard",label:"Dashboard",page:"dashboard"},{icon:"bot",label:"Agents",page:"agents",badge:()=>s.agents.length},{icon:"columns-3",label:"Tasks Board",page:"kanban"},{icon:"scroll-text",label:"Run History",page:"runs"},{icon:"shield-check",label:"Approvals",page:"approvals",badge:()=>n.pending},{icon:"puzzle",label:"Skills",page:"skills",badge:()=>s.skills.length},{icon:"plug",label:"MCP Servers",page:"mcp",badge:()=>this.plugin.mcpManager.getCachedServers()?.length??0},{icon:"radio",label:"Channels",page:"channels",badge:()=>this.plugin.channelManager?.getConnectedCount()??s.channels.length}];for(let l of r){let d=a.createDiv({cls:"af-sidebar-nav-item"}),h=d.createSpan({cls:"af-sidebar-nav-icon"});(0,Fs.setIcon)(h,l.icon),d.createSpan({cls:"af-sidebar-nav-label",text:l.label});let u=l.badge?.();u!==void 0&&u>0&&d.createSpan({cls:"af-sidebar-badge",text:String(u)}),d.setAttribute("role","button"),d.setAttribute("tabindex","0"),d.onclick=()=>void this.plugin.navigateDashboard(l.page),d.onkeydown=p=>{(p.key==="Enter"||p.key===" ")&&(p.preventDefault(),this.plugin.navigateDashboard(l.page))}}let o=e.createDiv({cls:"af-sidebar-section"});o.createDiv({cls:"af-sidebar-section-header",text:"AGENTS"}),s.agents.length===0&&o.createDiv({cls:"af-sidebar-empty",text:"No agents configured"});for(let l of s.agents){let d=this.plugin.runtime.getAgentState(l.name),h=o.createDiv({cls:"af-sidebar-agent-item"});h.createSpan({cls:`af-sidebar-agent-dot ${this.healthToClass(d.status)}`}),h.createSpan({cls:"af-sidebar-agent-name",text:l.name}),h.setAttribute("role","button"),h.setAttribute("tabindex","0"),h.onclick=()=>void this.plugin.navigateDashboard("agent-detail",l.name),h.onkeydown=u=>{(u.key==="Enter"||u.key===" ")&&(u.preventDefault(),this.plugin.navigateDashboard("agent-detail",l.name))}}let c=e.createDiv({cls:"af-sidebar-section"});c.createDiv({cls:"af-sidebar-section-header",text:"QUICK ACTIONS"}),this.renderQuickAction(c,"plus","New Agent",()=>void this.plugin.createAgentTemplate()),this.renderQuickAction(c,"plus","New Task",()=>void this.plugin.openCreateTask()),this.renderQuickAction(c,"plus","New Skill",()=>void this.plugin.createSkillTemplate())}renderQuickAction(e,s,n,a){let r=e.createDiv({cls:"af-sidebar-action-item"}),o=r.createSpan({cls:"af-sidebar-action-icon"});(0,Fs.setIcon)(o,s),r.createSpan({text:n}),r.setAttribute("role","button"),r.setAttribute("tabindex","0"),r.onclick=a,r.onkeydown=c=>{(c.key==="Enter"||c.key===" ")&&(c.preventDefault(),a())}}healthToClass(e){switch(e){case"running":return"running";case"error":return"error";case"pending":return"pending";case"disabled":return"disabled";default:return"idle"}}};var b=require("obsidian");var os=require("obsidian"),bd=["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"],Nn=class extends os.Modal{constructor(e,s,n){super(e);this.onSelect=n;this.selectedIcon=s}searchQuery="";selectedIcon;allIcons=[];gridContainer;onOpen(){this.allIcons=(0,os.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",bd),this.renderSection(e,"All Icons",this.allIcons);else{let n=this.allIcons.filter(r=>r.includes(s)),a=n.length===0?"No results":`${n.length} result${n.length!==1?"s":""}`;this.renderSection(e,a,n)}}renderSection(e,s,n){let a=e.createDiv({cls:"af-icon-picker-section"});a.createDiv({cls:"af-icon-picker-section-label",text:s});let r=a.createDiv({cls:"af-icon-picker-grid"});for(let o of n)this.renderIconItem(r,o)}renderIconItem(e,s){let n=e.createDiv({cls:`af-icon-picker-item${this.selectedIcon===s?" selected":""}`});n.setAttribute("title",s),n.setAttribute("aria-label",s),(0,os.setIcon)(n,s),n.addEventListener("click",()=>{this.selectedIcon=s,this.onSelect(s),this.close()})}onClose(){this.contentEl.empty()}};var po=require("obsidian");function A(i,t,e){let s=i.createSpan({cls:e??"af-icon"});return(0,po.setIcon)(s,t),s}function et(i,t={}){let e=document.createElementNS("http://www.w3.org/2000/svg",i);for(let[s,n]of Object.entries(t))e.setAttribute(s,n);return e}function wd(i){try{return new Date(i+"T12:00:00").toLocaleDateString(void 0,{month:"short",day:"numeric"})}catch{return i.slice(5)}}function mo(i,t){let e=t.length||1,s=1e3,n=6,a=4,r=4,o=s-a-r-n*(e-1),c=Math.floor(o/e),l=140,d=36,h=18,u=h+l+d,p=Math.max(1,...t.map(f=>f.success+f.failure+f.cancelled)),m=et("svg",{viewBox:`0 0 ${s} ${u}`,width:"100%",height:String(u),class:"af-chart-bar"});for(let f=0;f<=4;f++){let y=h+l-f/4*l;m.appendChild(et("line",{x1:String(a),y1:String(y),x2:String(s-r),y2:String(y),stroke:"var(--af-text-faint)","stroke-opacity":"0.15","stroke-width":"1"}))}for(let f=0;f<t.length;f++){let y=t[f],w=a+f*(c+n),k=y.success+y.failure+y.cancelled,g=k/p*l,v=y.success/p*l,S=y.cancelled/p*l,T=y.failure/p*l;if(y.success>0&&m.appendChild(et("rect",{x:String(w),y:String(h+l-v),width:String(c),height:String(Math.max(v,2)),fill:"var(--af-green)",opacity:"0.85"})),y.cancelled>0&&m.appendChild(et("rect",{x:String(w),y:String(h+l-v-S),width:String(c),height:String(Math.max(S,2)),fill:"var(--af-yellow)",opacity:"0.85"})),y.failure>0&&m.appendChild(et("rect",{x:String(w),y:String(h+l-g),width:String(c),height:String(Math.max(T,2)),fill:"var(--af-red)",opacity:"0.85"})),k===0&&m.appendChild(et("rect",{x:String(w),y:String(h+l-3),width:String(c),height:"3",rx:"1.5",fill:"var(--af-text-faint)",opacity:"0.2"})),k>0){let D=et("text",{x:String(w+c/2),y:String(h+l-g-5),"text-anchor":"middle","font-size":"10","font-weight":"600",fill:"var(--af-text-secondary)"});D.textContent=String(k),m.appendChild(D)}let _=et("text",{x:String(w+c/2),y:String(h+l+16),"text-anchor":"middle","font-size":"10",fill:"var(--af-text-muted)"});_.textContent=wd(y.date),m.appendChild(_)}i.appendChild(m)}function fo(i,t,e){let c=2*Math.PI*46,l=e>0?t/e:0,d=c*l,h=c-d,u=et("svg",{viewBox:"0 0 130 130",width:String(130),height:String(130),class:"af-chart-donut"});u.appendChild(et("circle",{cx:String(65),cy:String(65),r:String(46),fill:"none",stroke:e>0?"var(--af-red)":"var(--af-text-faint)","stroke-width":String(12),opacity:"0.2"})),l>0&&u.appendChild(et("circle",{cx:String(65),cy:String(65),r:String(46),fill:"none",stroke:"var(--af-green)","stroke-width":String(12),"stroke-dasharray":`${d} ${h}`,"stroke-dashoffset":String(c*.25),"stroke-linecap":"round"}));let p=et("text",{x:String(65),y:String(61),"text-anchor":"middle","dominant-baseline":"middle","font-size":"24","font-weight":"700",fill:"var(--af-text-primary)"});p.textContent=`${Math.round(l*100)}%`,u.appendChild(p);let m=et("text",{x:String(65),y:String(83),"text-anchor":"middle","font-size":"10",fill:"var(--af-text-muted)"});m.textContent=`${t}/${e} runs`,u.appendChild(m),i.appendChild(u)}function go(i,t){i.draggable=!0,i.addEventListener("dragstart",e=>{e.dataTransfer?.setData("text/plain",t),i.addClass("af-dragging")}),i.addEventListener("dragend",()=>{i.removeClass("af-dragging")})}function yo(i,t){i.addEventListener("dragover",e=>{e.preventDefault(),i.addClass("af-drag-over")}),i.addEventListener("dragleave",e=>{let s=e.relatedTarget;(!s||!i.contains(s))&&i.removeClass("af-drag-over")}),i.addEventListener("drop",e=>{e.preventDefault();let s=e.dataTransfer?.getData("text/plain");i.removeClass("af-drag-over"),s&&t(s)})}function vo(i){let t=/^##\s+Lint\s+(\d{4}-\d{2}-\d{2})\s*$/gm,e,s=-1,n="";for(;(e=t.exec(i))!==null;)e.index>s&&(s=e.index,n=e[1]??"");if(s<0)return null;let a=i.slice(s),r=a.search(/\n##\s+(?!\s*#)/),o=r<0?a:a.slice(0,r);return{date:n,summary:Bn(o,"Summary"),autoApplied:Bn(o,"Auto-applied"),needsReview:Bn(o,"Needs review"),refreshChained:Bn(o,"Refresh chained")}}function Bn(i,t){let e=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),n=new RegExp(`^###\\s+${e}\\s*$`,"m").exec(i);if(!n)return[];let a=n.index+n[0].length,r=i.slice(a),o=r.search(/\n###\s+/),l=(o<0?r:r.slice(0,o)).split(/\r?\n/).map(h=>h.trimEnd()),d=[];for(let h of l){let u=h.replace(/^\s+/,"");u.startsWith("- ")?d.push(u.slice(2).trim()):d.length>0&&(h.startsWith(" ")||h.startsWith(" "))&&(d[d.length-1]+=" "+u)}return d}var bo={dashboard:"Dashboard",agents:"Agents",kanban:"Tasks Board",runs:"Run History",skills:"Skills Library",approvals:"Approvals",mcp:"MCP Servers",channels:"Channels","wiki-keepers":"Wiki Keepers","agent-detail":"Agent Details","task-detail":"Task Details","create-agent":"Create Agent","create-task":"Create Task","create-skill":"Create Skill","edit-agent":"Edit Agent","edit-task":"Edit Task","edit-skill":"Edit Skill","create-channel":"Create Channel","edit-channel":"Edit Channel","add-mcp-server":"Add MCP Server"},kd={dashboard:"layout-dashboard",agents:"bot",kanban:"columns-3",runs:"scroll-text",skills:"puzzle",approvals:"shield-check",mcp:"plug",channels:"radio","wiki-keepers":"library","agent-detail":"bot","task-detail":"circle-dot","create-agent":"plus","create-task":"plus","create-skill":"plus","edit-agent":"edit","edit-task":"edit","edit-skill":"edit","create-channel":"plus","edit-channel":"edit","add-mcp-server":"plus"},xd=["dashboard","agents","kanban","runs","approvals","skills","wiki-keepers","mcp","channels"],wo=[["claude-code","Claude Code",!1],["codex","Codex",!1],["process","Process (coming soon)",!0],["http","HTTP (coming soon)",!0]],xo=[["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"]],So=[["bypassPermissions","Bypass (no sandbox)","No sandbox, auto-approve everything"],["workspace-write","Workspace Write","Sandboxed: writes only inside the working dir"],["read-only","Read Only","Sandboxed: no writes or side-effect commands"]];function Os(i){let t=i.trim().toLowerCase();return t==="codex"||t==="openai-codex"}function Un(i){return Os(i)?So:xo}function $n(i,t){if(Os(t))switch(i){case"acceptEdits":case"default":return"workspace-write";case"plan":return"read-only";case"dontAsk":return"bypassPermissions";default:return So.some(([e])=>e===i)?i:"bypassPermissions"}switch(i){case"workspace-write":return"acceptEdits";case"read-only":return"plan";case"danger-full-access":return"bypassPermissions";default:return xo.some(([e])=>e===i)?i:"bypassPermissions"}}var Ns=class extends b.ItemView{constructor(e,s){super(e);this.plugin=s}currentPage="dashboard";detailContext;agentDetailTab="overview";streamingUnsubscribes=[];channelStatusUnsubscribe;authenticatingServers=new Set;getViewType(){return Et}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 n=e.createDiv({cls:"af-app"}).createDiv({cls:"af-main-content"});this.renderTopBar(n),this.renderTabBar(n);let a=n.createDiv({cls:"af-page"});switch(this.currentPage){case"dashboard":this.renderDashboardPage(a);break;case"agents":this.renderAgentsPage(a);break;case"kanban":this.renderKanbanPage(a);break;case"runs":this.renderRunsPage(a);break;case"skills":this.renderSkillsPage(a);break;case"approvals":this.renderApprovalsPage(a);break;case"mcp":this.renderMcpPage(a);break;case"channels":this.renderChannelsPage(a);break;case"wiki-keepers":this.renderWikiKeepersPage(a);break;case"agent-detail":this.renderAgentDetailPage(a);break;case"task-detail":this.renderTaskDetailPage(a);break;case"create-agent":this.renderCreateAgentPage(a);break;case"create-task":this.renderCreateTaskPage(a);break;case"create-skill":this.renderCreateSkillPage(a);break;case"edit-agent":this.renderEditAgentPage(a);break;case"edit-task":this.renderEditTaskPage(a);break;case"edit-skill":this.renderEditSkillPage(a);break;case"create-channel":this.renderCreateChannelPage(a);break;case"edit-channel":this.renderEditChannelPage(a);break;case"add-mcp-server":this.renderAddMcpServerPage(a);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"}),n=s.createDiv({cls:"af-top-bar-title"});A(n,"bot","af-top-bar-icon"),n.createSpan({text:"Agent Fleet"});let a=s.createDiv({cls:"af-breadcrumb"}),r=a.createSpan({cls:"af-breadcrumb-sep"});(0,b.setIcon)(r,"chevron-right");let o=(m,f,y)=>{let w=a.createSpan({cls:f?"af-breadcrumb-link":"",text:m});f&&(w.onclick=()=>this.navigate(f,y))},c=()=>{let m=a.createSpan({cls:"af-breadcrumb-sep"});(0,b.setIcon)(m,"chevron-right")};switch(this.currentPage){case"agent-detail":o("Agents","agents"),c(),o(this.detailContext??"Agent");break;case"task-detail":o("Tasks Board","kanban"),c(),o(this.detailContext??"Task");break;case"edit-agent":o("Agents","agents"),c(),o(this.detailContext??"Agent","agent-detail",this.detailContext),c(),o("Edit");break;case"edit-task":o("Tasks Board","kanban"),c(),o(this.detailContext??"Task","task-detail",this.detailContext),c(),o("Edit");break;case"create-agent":o("Agents","agents"),c(),o("New Agent");break;case"create-task":o("Tasks Board","kanban"),c(),o("New Task");break;case"create-skill":o("Skills Library","skills"),c(),o("New Skill");break;case"edit-skill":o("Skills Library","skills"),c(),o(this.detailContext??"Skill"),c(),o("Edit");break;case"create-channel":o("Channels","channels"),c(),o("New Channel");break;case"edit-channel":o("Channels","channels"),c(),o(this.detailContext??"Channel"),c(),o("Edit");break;case"add-mcp-server":o("MCP Servers","mcp"),c(),o("Add Server");break;default:o(bo[this.currentPage])}s.createDiv({cls:"af-top-bar-spacer"});let l=s.createDiv({cls:"af-search-wrap"});A(l,"search","af-search-icon");let d=l.createEl("input",{cls:"af-search-input",attr:{type:"text",placeholder:"Search agents, tasks, runs..."}});d.addEventListener("input",()=>{this.handleSearch(d.value,l)}),d.addEventListener("blur",()=>{setTimeout(()=>l.querySelector(".af-search-results")?.remove(),200)});let h=this.plugin.runtime.getFleetStatus(),u=s.createDiv({cls:"af-status-pills"});if(h.running>0){let m=u.createSpan({cls:"af-pill yellow"});m.createSpan({cls:"af-dot pulse"}),m.appendText(` ${h.running} Running`)}if(h.pending>0){let m=u.createSpan({cls:"af-pill blue"});m.createSpan({cls:"af-dot"}),m.appendText(` ${h.pending} Pending`)}let p=u.createSpan({cls:"af-pill green"});p.createSpan({cls:"af-dot"}),p.appendText(` ${h.completedToday} Today`)}renderTabBar(e){let s=e.createDiv({cls:"af-tab-bar"}),n=this.plugin.runtime.getSnapshot(),a=this.plugin.runtime.getFleetStatus();for(let r of xd){let o=this.currentPage===r||r==="agents"&&(this.currentPage==="agent-detail"||this.currentPage==="edit-agent"||this.currentPage==="create-agent")||r==="kanban"&&(this.currentPage==="task-detail"||this.currentPage==="edit-task")||r==="skills"&&(this.currentPage==="edit-skill"||this.currentPage==="create-skill")||r==="mcp"&&this.currentPage==="add-mcp-server",c=s.createEl("button",{cls:`af-tab-item${o?" active":""}`}),l=c.createSpan({cls:"af-tab-icon"});if((0,b.setIcon)(l,kd[r]),c.appendText(r==="dashboard"?"Overview":bo[r]),r==="agents")c.createSpan({cls:"af-badge",text:String(n.agents.length)});else if(r==="skills")c.createSpan({cls:"af-badge",text:String(n.skills.length)});else if(r==="mcp"){let d=this.plugin.mcpManager.getCachedServers()?.length??0;c.createSpan({cls:"af-badge",text:String(d)})}else r==="approvals"&&a.pending>0&&c.createSpan({cls:"af-badge af-badge-warn",text:String(a.pending)});c.onclick=()=>this.navigate(r)}}renderDashboardPage(e){let s=e.createDiv({cls:"af-dashboard"}),n=this.plugin.runtime.getSnapshot(),a=this.plugin.runtime.getRecentRuns(),r=this.plugin.runtime.getFleetStatus(),o=a.filter(v=>(v.approvals??[]).some(S=>S.status==="pending"));for(let v of o)for(let S of v.approvals??[])S.status==="pending"&&this.renderApprovalBanner(s,v,S.tool);let c=s.createDiv({cls:"af-dash-grid"}),l=n.agents.filter(v=>v.enabled).length,d=n.agents.length;this.renderStatCard(c,"Active Agents",`${l}`,`/ ${d}`,"bot",`${l} of ${d} enabled`);let h=this.toLocalDateStr(new Date),u=a.filter(v=>this.runToLocalDate(v.started)===h),p=u.filter(v=>v.status==="success").length,m=u.filter(v=>v.status==="failure"||v.status==="timeout").length;this.renderStatCard(c,"Runs Today",String(u.length),"","activity",`${p} passed \xB7 ${m} failed \xB7 ${r.running} running`);let f=u.reduce((v,S)=>v+(S.tokensUsed??0),0),y=u.reduce((v,S)=>v+(S.costUsd??0),0),w=y>0?` \xB7 $${y.toFixed(2)}`:"";this.renderStatCard(c,"Tokens Used",$a(f),"","zap",`today${w}`);let k=n.tasks.filter(v=>v.enabled&&v.schedule);this.renderStatCard(c,"Scheduled Tasks",String(k.length),"","clock",k.length>0?`Next: ${this.getNextTaskLabel(k)}`:"No scheduled tasks"),this.renderChartsRow(s,a,this.plugin.runtime.getChartRuns()),this.renderStreamingCards(s);let g=s.createDiv({cls:"af-dash-split"});this.renderActivityTimeline(g,a),this.renderFleetStatusPanel(g,n)}renderChartsRow(e,s,n){let a=e.createDiv({cls:"af-charts-row"}),r=a.createDiv({cls:"af-section-card af-chart-section"}),c=r.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});A(c,"activity"),c.appendText(" Run Activity (14d)");let l=r.createDiv({cls:"af-chart-body"}),d=this.buildChartData(n,14);d.some(w=>w.success+w.failure+w.cancelled>0)?mo(l,d):this.renderEmptyState(l,"activity","No run data","Run agents to see activity");let h=a.createDiv({cls:"af-section-card af-chart-section"}),p=h.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});A(p,"target"),p.appendText(" Success Rate");let m=h.createDiv({cls:"af-chart-body af-chart-body-center"}),f=s.length,y=s.filter(w=>w.status==="success").length;f>0?fo(m,y,f):this.renderEmptyState(m,"target","No data","")}toLocalDateStr(e){let s=n=>String(n).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 n=[],a=new Date;for(let r=s-1;r>=0;r--){let o=new Date(a);o.setDate(o.getDate()-r);let c=this.toLocalDateStr(o),l=e.filter(d=>this.runToLocalDate(d.started)===c);n.push({date:c,success:l.filter(d=>d.status==="success").length,failure:l.filter(d=>d.status==="failure"||d.status==="timeout").length,cancelled:l.filter(d=>d.status==="cancelled").length})}return n}renderStreamingCards(e){let n=this.plugin.runtime.getSnapshot().agents.filter(c=>this.plugin.runtime.getAgentState(c.name).status==="running");if(n.length===0)return;let a=e.createDiv({cls:"af-streaming-section"}),o=a.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});A(o,"terminal"),o.appendText(" Active Agents");for(let c of n)this.renderStreamingCard(a,c.name)}renderStreamingCard(e,s){let n=e.createDiv({cls:"af-streaming-card"}),a=this.plugin.runtime.getAgentState(s),r=this.plugin.runtime.getSnapshot(),o=a.currentTaskId?r.tasks.find(m=>m.taskId===a.currentTaskId):void 0,c=o?` \u2192 ${o.taskId}`:a.status==="running"?" \u2192 Heartbeat":"",l=n.createDiv({cls:"af-streaming-card-header"});l.createSpan({cls:"af-dot pulse",attr:{style:"background: var(--af-yellow)"}}),l.createSpan({cls:"af-streaming-card-agent",text:` ${s}`}),c&&l.createSpan({cls:"af-streaming-card-task",text:c});let d=n.createDiv({cls:"af-streaming-output"}),h=this.plugin.runtime.getRunOutputBuffer(s),u=oe(h).slice(-4);d.setText(u.join(`
|
|
12005
|
+
`));let p=this.plugin.runtime.onRunOutput(s,()=>{let m=this.plugin.runtime.getRunOutputBuffer(s),f=oe(m).slice(-4);d.setText(f.join(`
|
|
12006
|
+
`)),d.scrollTop=d.scrollHeight});this.streamingUnsubscribes.push(p)}renderApprovalBanner(e,s,n){let a=e.createDiv({cls:"af-approval-banner"}),r=a.createDiv({cls:"af-approval-icon"});(0,b.setIcon)(r,"shield-check");let o=a.createDiv({cls:"af-approval-text"});o.createDiv({cls:"af-approval-title",text:`${s.agent} wants to run: ${n}`}),o.createDiv({cls:"af-approval-desc",text:`Approval required for tool: ${n}`});let c=a.createDiv({cls:"af-approval-actions"}),l=c.createEl("button",{cls:"af-btn-approve",text:"Approve"});l.onclick=()=>void this.plugin.runtime.resolveApproval(s,n,"approved").then(()=>this.render());let d=c.createEl("button",{cls:"af-btn-reject",text:"Reject"});d.onclick=()=>void this.plugin.runtime.resolveApproval(s,n,"rejected").then(()=>this.render())}renderStatCard(e,s,n,a,r,o){let c=e.createDiv({cls:"af-stat-card"}),l=c.createDiv({cls:"af-stat-label"});A(l,r,"af-stat-icon"),l.appendText(` ${s}`);let d=c.createDiv({cls:"af-stat-value"});d.appendText(n),a&&d.createSpan({cls:"af-stat-value-suffix",text:a}),c.createDiv({cls:"af-stat-sub",text:o})}renderActivityTimeline(e,s){let n=e.createDiv({cls:"af-section-card"}),r=n.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});A(r,"inbox"),r.appendText(" Recent Activity");let o=n.createDiv({cls:"af-timeline"}),c=s.slice(0,8);if(c.length===0){this.renderEmptyState(o,"inbox","No runs yet","Run an agent to see activity here");return}for(let l of c)this.renderTimelineItem(o,l)}renderTimelineItem(e,s){let n=e.createDiv({cls:"af-timeline-item"}),a=this.statusToTimelineClass(s.status),r=n.createDiv({cls:`af-tl-icon ${a}`});(0,b.setIcon)(r,this.statusToIconName(s.status));let o=n.createDiv({cls:"af-tl-body"}),c=o.createDiv({cls:"af-tl-title"});c.createSpan({cls:"af-agent-tag",text:s.agent}),c.appendText(` ${s.task}`),o.createDiv({cls:"af-tl-desc",text:Vt(s.output,100)});let l=o.createDiv({cls:"af-tl-meta"}),d=l.createSpan();if(A(d,"clock","af-meta-icon"),d.appendText(` ${this.formatStarted(s.started)} \xB7 ${this.formatDuration(s.durationSeconds)}`),s.tokensUsed){let h=l.createSpan();A(h,"zap","af-meta-icon"),h.appendText(` ${s.tokensUsed.toLocaleString()} tokens`)}n.onclick=()=>this.openSlideover(s)}renderFleetStatusPanel(e,s){let n=e.createDiv({cls:"af-section-card"}),a=n.createDiv({cls:"af-section-header"}),r=a.createDiv({cls:"af-section-title"});A(r,"bot"),r.appendText(" Fleet Status");let c=a.createDiv({cls:"af-section-actions"}).createEl("button",{cls:"af-btn-sm primary"});A(c,"plus","af-btn-icon"),c.appendText(" New Agent"),c.onclick=()=>void this.plugin.createAgentTemplate();let l=n.createDiv({cls:"af-agent-mini-list"});if(s.agents.length===0){this.renderEmptyState(l,"bot","No agents yet","Create your first agent to get started");return}for(let h of s.agents)this.renderAgentMini(l,h,s.tasks);let d=s.agents.filter(h=>h.enabled);if(d.length>0){let h=n.createDiv({cls:"af-quick-run"}),u=h.createDiv({cls:"af-quick-run-label"});A(u,"zap","af-meta-icon"),u.appendText(" Quick Run");let p=h.createDiv({cls:"af-quick-run-row"}),m=p.createEl("select",{cls:"af-select"});for(let y of d)m.createEl("option",{text:y.name,attr:{value:y.name}});let f=p.createEl("button",{cls:"af-btn-sm primary"});A(f,"play","af-btn-icon"),f.appendText(" Run"),f.onclick=()=>void this.plugin.runAgentPrompt(m.value)}}renderAgentMini(e,s,n){let a=this.plugin.runtime.getAgentState(s.name),r=n.filter(u=>u.agent===s.name),o=this.healthToClass(a.status),c=e.createDiv({cls:"af-agent-mini"}),l=c.createDiv({cls:`af-agent-avatar ${o}`});s.avatar?.trim()?this.renderAgentAvatar(l,s):l.setText(this.getInitials(s.name));let d=c.createDiv({cls:"af-agent-info"});d.createDiv({cls:"af-agent-name",text:s.name});let h="";if(a.status==="running")h=`Running now \xB7 ${r.length} task${r.length!==1?"s":""}`;else if(!s.enabled)h=`Disabled \xB7 ${r.length} task${r.length!==1?"s":""} paused`;else{let u=r.map(p=>p.nextRun).filter(Boolean).sort()[0];h=u?`Next: ${this.formatNextRun(u)} \xB7 ${r.length} task${r.length!==1?"s":""}`:`${r.length} task${r.length!==1?"s":""}`}d.createDiv({cls:"af-agent-desc",text:h}),c.createDiv({cls:`af-agent-status-dot ${o}`}),c.onclick=()=>this.navigate("agent-detail",s.name)}renderAgentsPage(e){let s=e.createDiv({cls:"af-agents-page"}),n=this.plugin.runtime.getSnapshot(),a=s.createDiv({cls:"af-agents-toolbar"});a.createDiv({cls:"af-page-title",text:"Agents"}),a.createDiv({cls:"af-toolbar-spacer"});let r=a.createEl("button",{cls:"af-btn-sm primary"});A(r,"plus","af-btn-icon"),r.appendText(" New Agent"),r.onclick=()=>void this.plugin.createAgentTemplate();let o=s.createDiv({cls:"af-agents-grid"});if(n.agents.length===0){this.renderEmptyState(o,"bot","No agents configured","Create your first agent to get started");return}for(let c of n.agents)this.renderAgentCard(o,c,n)}renderAgentCard(e,s,n){let a=this.plugin.runtime.getAgentState(s.name),r=this.plugin.runtime.getRecentRuns().filter(E=>E.agent===s.name),o=n.tasks.filter(E=>E.agent===s.name),c=e.createDiv({cls:`af-agent-card${s.enabled?"":" disabled"}`}),l=c.createDiv({cls:"af-agent-card-header"}),d=s.enabled?this.healthToClass(a.status):"disabled",h=l.createDiv({cls:`af-agent-card-avatar ${d}`});this.renderAgentAvatar(h,s);let u=l.createDiv({cls:"af-agent-card-titleblock"}),p=u.createDiv({cls:"af-agent-card-name"});if(p.appendText(s.name),s.heartbeatEnabled&&s.heartbeatSchedule){let E=p.createSpan({cls:"af-heartbeat-indicator"});(0,b.setIcon)(E,"heart-pulse"),E.title=`Heartbeat: ${s.heartbeatSchedule}`}u.createDiv({cls:"af-agent-card-desc",text:s.description??"No description"});let m=l.createDiv({cls:`af-agent-card-toggle${s.enabled?" on":""}`});m.onclick=E=>{E.stopPropagation(),this.plugin.toggleAgent(s.name,!s.enabled)};let f=c.createDiv({cls:"af-agent-card-stats"}),y=r.length,w=r.filter(E=>E.status==="success").length,k=y>0?Math.round(w/y*100):0,g=y>0?Math.round(r.reduce((E,C)=>E+C.durationSeconds,0)/y):0,v=r.reduce((E,C)=>E+(C.tokensUsed??0),0);if(this.renderAgentStat(f,String(y),"Runs"),this.renderAgentStat(f,`${k}%`,"Success"),this.renderAgentStat(f,`${g}s`,"Avg Time"),this.renderAgentStat(f,$a(v),"Tokens"),s.skills.length>0){let E=c.createDiv({cls:"af-agent-card-skills"});for(let C of s.skills)E.createSpan({cls:"af-skill-tag",text:C})}let S=c.createDiv({cls:"af-agent-card-footer"}),T=[`Model: ${s.model}`];s.approvalRequired.length>0&&T.push(`Approval: ${s.approvalRequired.join(", ")}`),s.memory&&T.push("Memory: on"),s.enabled||T.unshift("Disabled"),S.createSpan({cls:"af-agent-card-meta",text:T.join(" \xB7 ")});let _=S.createDiv({cls:"af-agent-card-actions"});if(!s.enabled){let E=_.createEl("button",{cls:"af-btn-sm",text:"Enable"});E.onclick=C=>{C.stopPropagation(),this.plugin.toggleAgent(s.name,!0)}}let D=_.createEl("button",{cls:"af-btn-sm"});if(A(D,"edit","af-btn-icon"),D.appendText(" Edit"),D.onclick=E=>{E.stopPropagation(),this.navigate("edit-agent",s.name)},s.enabled){let E=_.createEl("button",{cls:"af-btn-sm primary"});A(E,"play","af-btn-icon"),E.appendText(" Run"),E.onclick=C=>{C.stopPropagation(),this.plugin.runAgentPrompt(s.name)}}c.onclick=()=>this.navigate("agent-detail",s.name)}renderAgentStat(e,s,n){let a=e.createDiv({cls:"af-agent-stat"});a.createSpan({cls:"af-agent-stat-value",text:s}),a.createSpan({cls:"af-agent-stat-label",text:n})}renderAgentDetailPage(e){let s=e.createDiv({cls:"af-agent-detail-page"}),n=this.detailContext;if(!n){this.renderEmptyState(s,"bot","No agent selected","Select an agent from the list");return}let a=this.plugin.runtime.getSnapshot().agents.find(g=>g.name===n);if(!a){this.renderEmptyState(s,"bot","Agent not found",`Agent "${n}" was not found`);return}let r=this.plugin.runtime.getAgentState(a.name),o=this.plugin.runtime.getRecentRuns().filter(g=>g.agent===a.name),c=s.createDiv({cls:"af-detail-header"}),l=c.createDiv({cls:"af-detail-header-left"}),d=l.createDiv({cls:`af-agent-card-avatar ${this.healthToClass(r.status)}`});this.renderAgentAvatar(d,a);let h=l.createDiv();h.createDiv({cls:"af-detail-header-name",text:a.name}),h.createDiv({cls:"af-detail-header-desc",text:a.description??"No description"});let u=c.createDiv({cls:"af-detail-header-actions"}),p=u.createEl("button",{cls:"af-btn-sm primary"});if(A(p,"message-circle","af-btn-icon"),p.appendText(" Chat"),p.onclick=()=>this.openChatSlideover(a),a.enabled){let g=u.createEl("button",{cls:"af-btn-sm"});A(g,"play","af-btn-icon"),g.appendText(" Run Now"),g.onclick=()=>void this.plugin.runAgentPrompt(a.name);let v=u.createEl("button",{cls:"af-btn-sm"});A(v,"pause","af-btn-icon"),v.appendText(" Disable"),v.onclick=()=>void this.plugin.toggleAgent(a.name,!1)}else{let g=u.createEl("button",{cls:"af-btn-sm"});A(g,"play","af-btn-icon"),g.appendText(" Enable"),g.onclick=()=>void this.plugin.toggleAgent(a.name,!0)}let m=u.createEl("button",{cls:"af-btn-sm"});A(m,"edit","af-btn-icon"),m.appendText(" Edit"),m.onclick=()=>this.navigate("edit-agent",a.name);let f=u.createEl("button",{cls:"af-btn-sm danger"});A(f,"trash-2","af-btn-icon"),f.appendText(" Delete"),f.onclick=()=>void this.plugin.deleteAgent(a.name);let y=s.createDiv({cls:"af-detail-tabs"}),w=[{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 w){let v=y.createEl("button",{cls:`af-detail-tab${this.agentDetailTab===g.id?" active":""}`});A(v,g.icon,"af-tab-icon"),v.appendText(` ${g.label}`),v.onclick=()=>{this.agentDetailTab=g.id,this.render()}}let k=s.createDiv({cls:"af-detail-tab-content"});switch(this.agentDetailTab){case"overview":this.renderAgentOverviewTab(k,a,o);break;case"config":this.renderAgentConfigTab(k,a);break;case"runs":this.renderAgentRunsTab(k,o);break;case"memory":this.renderAgentMemoryTab(k,a);break}}renderAgentOverviewTab(e,s,n){let a=e.createDiv({cls:"af-dash-grid"}),r=n.length,o=n.filter(g=>g.status==="success").length,c=r>0?Math.round(o/r*100):0,l=r>0?Math.round(n.reduce((g,v)=>g+v.durationSeconds,0)/r):0,d=n.reduce((g,v)=>g+(v.tokensUsed??0),0),h=n.reduce((g,v)=>g+(v.costUsd??0),0),u=h>0?` \xB7 $${h.toFixed(2)}`:"";if(this.renderStatCard(a,"Total Runs",String(r),"","activity","all time"),this.renderStatCard(a,"Success Rate",`${c}%`,"","check-circle-2",`${o}/${r}`),this.renderStatCard(a,"Avg Time",`${l}s`,"","clock","per run"),this.renderStatCard(a,"Total Tokens",$a(d),"","zap",`all time${u}`),s.isFolder&&(s.heartbeatBody.trim()||s.heartbeatEnabled)){let g=e.createDiv({cls:"af-section-card"}),v=g.createDiv({cls:"af-section-header"}),S=v.createDiv({cls:"af-section-title"});A(S,"heart-pulse"),S.appendText(" Heartbeat");let _=v.createDiv({cls:"af-detail-header-actions"}).createDiv({cls:`af-agent-card-toggle${s.heartbeatEnabled?" on":""}`});_.onclick=async()=>{let C=_.hasClass("on");await this.plugin.repository.updateHeartbeat(s.name,{enabled:!C}),await this.plugin.refreshFromVault(),new b.Notice(`Heartbeat ${C?"paused":"enabled"} for ${s.name}`)};let D=g.createDiv({cls:"af-config-form"});this.renderConfigRow(D,"Schedule",Sd(s.heartbeatSchedule));let E=this.plugin.runtime.getNextHeartbeat(s.name);E&&s.heartbeatEnabled&&this.renderConfigRow(D,"Next run",this.timeUntil(E)),s.heartbeatChannel&&this.renderConfigRow(D,"Channel",s.heartbeatChannel)}if(s.skills.length>0){let g=e.createDiv({cls:"af-section-card"}),S=g.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});A(S,"puzzle"),S.appendText(" Skills");let T=g.createDiv({cls:"af-detail-skills-list"});for(let _ of s.skills)T.createSpan({cls:"af-skill-tag",text:_})}let p=s.mcpServers??[];if(p.length>0){let g=e.createDiv({cls:"af-section-card"}),S=g.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});A(S,"plug"),S.appendText(" MCP Servers");let T=g.createDiv({cls:"af-mcp-overview-list"}),_=this.plugin.mcpManager.getCachedServers();for(let D of p){let E=_?.find(B=>B.name===D),C=T.createDiv({cls:"af-mcp-overview-row"}),L=C.createSpan({cls:`af-mcp-status-dot ${E?E.enabled?E.status:"disabled":"disconnected"}`});L.title=E?E.enabled?E.status:"disabled":"unknown",C.createSpan({cls:"af-mcp-overview-name",text:D});let R=E?.toolDetails.length??E?.tools.length??0;R>0?C.createSpan({cls:"af-mcp-overview-tools",text:`${R} tools`}):E&&!E.enabled?C.createSpan({cls:"af-mcp-overview-tools af-muted",text:"disabled"}):E?.status==="needs-auth"&&C.createSpan({cls:"af-mcp-overview-tools af-muted",text:"needs auth"})}}if(s.permissionRules.allow.length>0||s.permissionRules.deny.length>0||s.permissionMode&&s.permissionMode!=="default"){let g=e.createDiv({cls:"af-section-card"}),S=g.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});A(S,"shield-check"),S.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 f=e.createDiv({cls:"af-section-card"}),w=f.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});A(w,"scroll-text"),w.appendText(" Recent Runs");let k=f.createDiv({cls:"af-timeline"});if(n.length===0)this.renderEmptyState(k,"scroll-text","No runs yet","");else for(let g of n.slice(0,5))this.renderTimelineItem(k,g)}renderAgentConfigTab(e,s){let n=e.createDiv({cls:"af-config-form"});this.renderConfigRow(n,"Name",s.name),this.renderConfigRow(n,"Description",s.description??""),this.renderConfigRow(n,"Model",s.model),this.renderConfigRow(n,"Timeout",`${s.timeout}s`),this.renderConfigRow(n,"Working Directory",s.cwd??"(vault root)"),this.renderConfigRow(n,"Permission Mode",s.permissionMode||"default"),this.renderConfigRow(n,"Approval Required",s.approvalRequired.join(", ")||"none"),s.permissionRules.allow.length>0&&this.renderConfigRow(n,"Allowed Commands",s.permissionRules.allow.join(", ")),s.permissionRules.deny.length>0&&this.renderConfigRow(n,"Blocked Commands",s.permissionRules.deny.join(", ")),this.renderConfigRow(n,"Memory",s.memory?"Enabled":"Disabled"),this.renderConfigRow(n,"Auto-compact",s.autoCompactThreshold&&s.autoCompactThreshold>0?`at ${s.autoCompactThreshold}% context`:"disabled"),s.wikiReferences&&s.wikiReferences.length>0&&this.renderConfigRow(n,"Wiki access",s.wikiReferences.map(c=>c.agent).join(", ")),this.renderConfigRow(n,"Tags",s.tags.join(", ")||"none");let a=n.createDiv({cls:"af-config-prompt-section"});if(a.createDiv({cls:"af-slideover-section-title",text:"SYSTEM PROMPT"}),a.createDiv({cls:"af-output-block",text:s.body||"(empty)"}),s.heartbeatBody.trim()){let c=n.createDiv({cls:"af-config-prompt-section"});c.createDiv({cls:"af-slideover-section-title",text:"HEARTBEAT INSTRUCTION"}),c.createDiv({cls:"af-output-block",text:s.heartbeatBody})}let o=n.createDiv({cls:"af-slideover-actions"}).createEl("button",{cls:"af-btn-sm primary"});A(o,"edit","af-btn-icon"),o.appendText(" Edit Agent"),o.onclick=()=>this.navigate("edit-agent",s.name)}renderConfigRow(e,s,n){let a=e.createDiv({cls:"af-detail-row"});a.createSpan({cls:"af-detail-label",text:s}),a.createSpan({cls:"af-detail-value af-mono",text:n})}renderAgentRunsTab(e,s){if(s.length===0){this.renderEmptyState(e,"scroll-text","No runs yet","Run this agent to see history");return}for(let n of s){let a=e.createDiv({cls:"af-run-list-item"}),r=a.createDiv({cls:`af-tl-icon ${this.statusToTimelineClass(n.status)}`});(0,b.setIcon)(r,this.statusToIconName(n.status));let o=a.createDiv({cls:"af-tl-body"}),c=o.createDiv({cls:"af-tl-title"});c.createSpan({text:n.task}),c.createSpan({cls:`af-status-badge ${this.statusToBadgeClass(n.status)}`,text:this.statusToBadgeText(n.status)});let l=o.createDiv({cls:"af-tl-meta"});l.createSpan({text:`${this.formatStarted(n.started)} \xB7 ${this.formatDuration(n.durationSeconds)}`}),n.tokensUsed&&l.createSpan({text:`${n.tokensUsed.toLocaleString()} tokens`}),o.createDiv({cls:"af-tl-desc",text:Vt(n.output,120)}),a.onclick=()=>this.openSlideover(n)}}async renderAgentMemoryTab(e,s){if(!s.memory){this.renderEmptyState(e,"file-text","Memory disabled","Enable memory in agent config");return}let n=await this.plugin.repository.readWorkingMemory(s.name),a=e.createDiv({cls:"af-form-help"}),r=n?.tokenEstimate??0,o=s.reflection.enabled?`reflection on (${s.reflection.schedule})`:"reflection off";a.setText(`~${r} / ${s.memoryTokenBudget} tokens \xB7 ${o}`),!n||n.sections.length===0?this.renderEmptyState(e,"file-text","No memories yet","Agent will learn from runs"):e.createDiv({cls:"af-output-block"}).setText(Je(n.sections));let c=e.createDiv({cls:"af-slideover-actions"}),l=c.createEl("button",{cls:"af-btn-sm"});A(l,"moon","af-btn-icon"),l.appendText(" Reflect now"),l.onclick=async()=>{l.disabled=!0,l.setText(" Reflecting\u2026");let h=await this.plugin.runtime.runReflectionNow(s.name);new b.Notice(`Agent Fleet: ${h.message}`),await this.renderAgentMemoryTab((e.empty(),e),s)};let d=c.createEl("button",{cls:"af-btn-sm"});A(d,"external-link","af-btn-icon"),d.appendText(" Open in Editor"),d.onclick=()=>void this.plugin.openPath(this.plugin.repository.getWorkingMemoryPath(s.name))}timeAgo(e){let s=Math.round((Date.now()-e.getTime())/1e3);if(s<60)return"just now";let n=Math.round(s/60);if(n<60)return`${n}m ago`;let a=Math.round(n/60);return a<24?`${a}h ago`:`${Math.round(a/24)}d ago`}timeUntil(e){let s=Math.round((e.getTime()-Date.now())/1e3);if(s<60)return"< 1m";let n=Math.round(s/60);if(n<60)return`in ${n}m`;let a=Math.round(n/60);return a<24?`in ${a}h`:`in ${Math.round(a/24)}d`}renderKanbanPage(e){let s=e.createDiv({cls:"af-kanban-page"}),n=this.plugin.runtime.getSnapshot(),a=this.plugin.runtime.getRecentRuns(),r=s.createDiv({cls:"af-kanban-toolbar"});r.createDiv({cls:"af-page-title",text:"Tasks Board"}),r.createDiv({cls:"af-toolbar-spacer"});let o=r.createEl("button",{cls:"af-btn-sm primary"});A(o,"plus","af-btn-icon"),o.appendText(" New Task"),o.onclick=()=>this.navigate("create-task");let c=s.createDiv({cls:"af-kanban-board"}),l=[],d=[],h=[],u=[],p=[],m=new Set;for(let k of n.agents){let g=this.plugin.runtime.getAgentState(k.name);g.status==="running"&&g.currentTaskId&&m.add(g.currentTaskId)}let f=this.toLocalDateStr(new Date);for(let k of a)k.status==="success"&&this.runToLocalDate(k.started)===f?u.push(k):(k.status==="failure"||k.status==="timeout"||k.status==="cancelled")&&this.runToLocalDate(k.started)===f&&p.push(k);let y=new Set(u.map(k=>k.task)),w=new Set(p.map(k=>k.task));for(let k of n.tasks){let g=y.has(k.taskId)||w.has(k.taskId)||k.lastRun&&this.runToLocalDate(k.lastRun)===f;if(m.has(k.taskId))h.push(k);else{if(g&&!k.schedule)continue;k.schedule&&k.enabled?d.push(k):l.push(k)}}this.renderKanbanColumn(c,"Backlog","inbox",l.length,k=>{for(let g of l)this.renderKanbanTaskCard(k,g,n,!0)},"backlog"),this.renderKanbanColumn(c,"Scheduled","clock",d.length,k=>{for(let g of d)this.renderKanbanTaskCard(k,g,n,!0)},"scheduled"),this.renderKanbanColumn(c,"Running","loader-2",h.length,k=>{for(let g of h)this.renderKanbanRunningCard(k,g)},"running",!1,"running"),this.renderKanbanColumn(c,"Done","check-circle-2",u.length,k=>{for(let g of u.slice(0,10))this.renderKanbanCompletedCard(k,g)},"completed"),this.renderKanbanColumn(c,"Failed","x-circle",p.length,k=>{for(let g of p)this.renderKanbanFailedCard(k,g)},"failed",!1,"failed")}renderKanbanColumn(e,s,n,a,r,o,c=!1,l){let d=e.createDiv({cls:`af-kanban-column${l?` af-kanban-${l}`:""}`});(o==="backlog"||o==="scheduled")&&yo(d,m=>this.handleTaskDrop(m,o));let u=d.createDiv({cls:"af-kanban-col-header"}).createDiv({cls:"af-kanban-col-title"});A(u,n),u.appendText(` ${s} `),u.createSpan({cls:"af-kanban-col-count",text:String(a)});let p=d.createDiv({cls:"af-kanban-col-body"});if(r(p),a===0&&p.createDiv({cls:"af-kanban-empty",text:"No items"}),c){let f=d.createDiv({cls:"af-kanban-col-add"}).createEl("button");A(f,"plus","af-btn-icon"),f.appendText(" Add task"),f.onclick=()=>{this.navigate("create-task")}}}handleTaskDrop(e,s){let n=this.plugin.runtime.getSnapshot().tasks.find(a=>a.taskId===e);if(n){if(s==="backlog")this.setTaskEnabled(n,!1).then(()=>{new b.Notice(`Task "${e}" moved to backlog (disabled)`)});else if(s==="scheduled"){if(!n.schedule&&!n.runAt){new b.Notice(`Task "${e}" needs a schedule. Open task details to set one.`),this.navigate("task-detail",e);return}this.setTaskEnabled(n,!0).then(()=>{new b.Notice(`Task "${e}" moved to scheduled (enabled)`)})}}}async setTaskEnabled(e,s){let n=this.plugin.app.vault.getAbstractFileByPath(e.filePath);if(!n||!(n instanceof b.TFile))return;let a=await this.plugin.app.vault.cachedRead(n),{frontmatter:r,body:o}=Q(a);r.enabled=s,await this.plugin.app.vault.modify(n,z(r,o)),await this.plugin.refreshFromVault()}renderKanbanTaskCard(e,s,n,a){let r=e.createDiv({cls:`af-kanban-card af-priority-${s.priority}`});if(a){go(r,s.taskId);let f=r.createDiv({cls:"af-kanban-card-grip"});(0,b.setIcon)(f,"grip-vertical")}let o=r.createDiv({cls:"af-kanban-card-header"});o.createDiv({cls:"af-kanban-card-title",text:s.taskId});let l=(n.agents.find(f=>f.name===s.agent)?.enabled??!1)&&s.enabled,d=o.createSpan({cls:`af-kanban-card-status ${l?"active":"inactive"}`});d.title=l?"Active":"Inactive";let h=r.createDiv({cls:"af-kanban-card-agent"}),u=h.createSpan({cls:"af-kanban-card-agent-icon"});(0,b.setIcon)(u,"bot"),h.createSpan({cls:"af-kanban-card-agent-name",text:s.agent});let m=r.createDiv({cls:"af-kanban-card-footer"}).createSpan({cls:"af-kanban-card-schedule"});l?s.schedule?(A(m,"refresh-cw","af-meta-icon"),m.appendText(` ${this.humanizeCron(s.schedule)}`)):m.appendText(s.runAt??"Manual"):(A(m,"pause","af-meta-icon"),m.appendText(" Paused")),r.onclick=()=>this.navigate("task-detail",s.taskId)}renderKanbanRunningCard(e,s){let n=e.createDiv({cls:"af-kanban-card af-kanban-card-running"});n.createDiv({cls:"af-kanban-card-title",text:s.taskId});let a=n.createDiv({cls:"af-kanban-card-agent"}),r=a.createSpan({cls:"af-kanban-card-agent-icon"});(0,b.setIcon)(r,"bot"),a.createSpan({cls:"af-kanban-card-agent-name",text:s.agent});let c=this.plugin.runtime.getSnapshot().agents.find(S=>S.name===s.agent)?.timeout??300,l=this.plugin.runtime.getAgentState(s.agent),d=l.runStarted?new Date(l.runStarted).getTime():Date.now(),p=n.createDiv({cls:"af-kanban-progress"}).createDiv({cls:"af-kanban-progress-track"}).createDiv({cls:"af-kanban-progress-bar af-kanban-progress-bar-real"}),m=(Date.now()-d)/1e3,f=Math.min(95,m/c*100);p.style.width=`${f}%`;let y=n.createDiv({cls:"af-kanban-card-footer"}),w=y.createSpan({cls:"af-kanban-card-schedule"});A(w,"loader-2","af-meta-icon");let k=Math.round(m);w.appendText(` ${k}s / ${c}s`);let g=y.createEl("button",{cls:"af-kanban-stop-btn"});(0,b.setIcon)(g,"square"),g.title="Stop task",g.onclick=S=>{S.stopPropagation(),this.plugin.runtime.abortAgentRun(s.agent),new b.Notice(`Stopped task "${s.taskId}"`)};let v=setInterval(()=>{let S=(Date.now()-d)/1e3,T=Math.min(95,S/c*100);p.style.width=`${T}%`;let _=Math.round(S);w.textContent="",(0,b.setIcon)(w,"loader-2"),w.appendText(` ${_}s / ${c}s`)},1e3);this.streamingUnsubscribes.push(()=>clearInterval(v))}renderKanbanCompletedCard(e,s){let n=e.createDiv({cls:"af-kanban-card"});n.createDiv({cls:"af-kanban-card-title",text:s.task});let a=n.createDiv({cls:"af-kanban-card-agent"}),r=a.createSpan({cls:"af-kanban-card-agent-icon"});(0,b.setIcon)(r,"bot"),a.createSpan({cls:"af-kanban-card-agent-name",text:s.agent});let c=n.createDiv({cls:"af-kanban-card-footer"}).createSpan({cls:"af-kanban-card-schedule"});A(c,"check-circle-2","af-meta-icon"),c.appendText(` ${this.formatStarted(s.started)} \xB7 ${this.formatDuration(s.durationSeconds)}`),n.onclick=()=>this.openSlideover(s)}renderKanbanFailedCard(e,s){let n=s.status==="cancelled",a=e.createDiv({cls:`af-kanban-card ${n?"af-kanban-card-cancelled":"af-kanban-card-failed"}`});a.createDiv({cls:"af-kanban-card-title",text:s.task});let r=a.createDiv({cls:"af-kanban-card-agent"}),o=r.createSpan({cls:"af-kanban-card-agent-icon"});(0,b.setIcon)(o,"bot"),r.createSpan({cls:"af-kanban-card-agent-name",text:s.agent});let c=n?`Stopped after ${s.durationSeconds}s`:s.status==="timeout"?`Timeout after ${s.durationSeconds}s`:Vt(s.output,60),l=a.createDiv({cls:"af-kanban-card-error"});A(l,n?"square":"alert-triangle","af-meta-icon"),l.appendText(` ${c}`);let d=a.createDiv({cls:"af-kanban-card-footer"}),h=d.createSpan({cls:"af-kanban-card-schedule"});if(A(h,n?"square":"x-circle","af-meta-icon"),h.appendText(` ${this.formatStarted(s.started)}`),!n){let u=d.createEl("button",{cls:"af-btn-sm"});A(u,"refresh-cw","af-btn-icon"),u.appendText(" Retry"),u.onclick=p=>{p.stopPropagation(),this.plugin.runAgentPrompt(s.agent)}}a.onclick=()=>this.openSlideover(s)}renderRunsPage(e){let s=e.createDiv({cls:"af-runs-page"}),n=this.plugin.runtime.getRecentRuns(),a=s.createDiv({cls:"af-runs-toolbar"});a.createDiv({cls:"af-page-title",text:"Run History"}),a.createDiv({cls:"af-toolbar-spacer"});let r=s.createDiv({cls:"af-runs-table"});if(n.length===0){this.renderEmptyState(r,"scroll-text","No runs yet","Run an agent to see history here");return}let o=r.createEl("table"),l=o.createEl("thead").createEl("tr");for(let h of["Status","Agent","Task","Started","Duration","Tokens","Model"])l.createEl("th",{text:h});let d=o.createEl("tbody");for(let h of n.slice(0,50))this.renderRunRow(d,h)}renderRunRow(e,s){let n=e.createEl("tr"),r=n.createEl("td").createSpan({cls:`af-status-badge ${this.statusToBadgeClass(s.status)}`}),o=r.createSpan();(0,b.setIcon)(o,this.statusToIconName(s.status)),r.appendText(` ${this.statusToBadgeText(s.status)}`);let c=n.createEl("td",{cls:"af-agent-link"});c.setText(s.agent),c.onclick=l=>{l.stopPropagation(),this.navigate("agent-detail",s.agent)},n.createEl("td",{text:s.task}),n.createEl("td",{cls:"af-mono",text:this.formatStarted(s.started)}),n.createEl("td",{cls:"af-mono",text:this.formatDuration(s.durationSeconds)}),n.createEl("td",{cls:"af-mono",text:s.tokensUsed?s.tokensUsed.toLocaleString():"\u2014"}),n.createEl("td",{cls:"af-mono",text:s.model}),n.style.cursor="pointer",n.onclick=()=>this.openSlideover(s)}renderSkillsPage(e){let s=e.createDiv({cls:"af-skills-page"}),n=this.plugin.runtime.getSnapshot(),a=s.createDiv({cls:"af-agents-toolbar"});a.createDiv({cls:"af-page-title",text:"Skills Library"}),a.createDiv({cls:"af-toolbar-spacer"});let r=a.createEl("button",{cls:"af-btn-sm primary"});A(r,"plus","af-btn-icon"),r.appendText(" New Skill"),r.onclick=()=>void this.plugin.createSkillTemplate();let o=s.createDiv({cls:"af-skills-grid"});if(n.skills.length===0){this.renderEmptyState(o,"puzzle","No skills yet","Create skills to give agents specialized abilities");return}for(let c of n.skills)this.renderSkillCard(o,c,n.agents)}renderSkillCard(e,s,n){let a=e.createDiv({cls:"af-skill-card"}),r=a.createDiv({cls:"af-skill-card-header"}),o=r.createDiv({cls:"af-skill-card-icon"});(0,b.setIcon)(o,this.getSkillIcon(s.name));let c=r.createEl("button",{cls:"af-btn-sm af-btn-xs"});A(c,"edit","af-btn-icon"),c.onclick=d=>{d.stopPropagation(),this.navigate("edit-skill",s.name)},a.createDiv({cls:"af-skill-card-name",text:s.name}),a.createDiv({cls:"af-skill-card-desc",text:s.description??"No description"});let l=n.filter(d=>d.skills.includes(s.name));if(l.length>0){let d=a.createDiv({cls:"af-skill-card-agents"});for(let h of l)d.createSpan({cls:"af-skill-card-agent-tag",text:h.name})}}renderChannelsPage(e){let s=e.createDiv({cls:"af-agents-page"}),n=this.plugin.runtime.getSnapshot(),a=s.createDiv({cls:"af-agents-toolbar"});a.createDiv({cls:"af-page-title",text:"Channels"}),a.createDiv({cls:"af-toolbar-spacer"});let r=a.createEl("button",{cls:"af-btn-sm primary"});A(r,"plus","af-btn-icon"),r.appendText(" New Channel"),r.onclick=()=>this.navigate("create-channel");let o=s.createDiv({cls:"af-agents-grid"});if(n.channels.length===0){this.renderEmptyState(o,"radio","No channels configured","Connect an agent to Slack or another chat platform");return}for(let c of n.channels)this.renderChannelCard(o,c,n.validationIssues)}async renderWikiKeepersPage(e){let s=e.createDiv({cls:"af-agents-page"}),n=this.plugin.runtime.getSnapshot(),a=s.createDiv({cls:"af-agents-toolbar"});a.createDiv({cls:"af-page-title",text:"Wiki Keepers"}),a.createDiv({cls:"af-toolbar-spacer"});let r=n.agents.filter(c=>c.wikiKeeper!==void 0);if(r.length===0){this.renderEmptyState(s,"library","No Wiki Keepers yet","Open Settings \u2192 Agent Fleet \u2192 Wiki Keepers \u2192 + Add to create one.");return}let o=s.createDiv({cls:"af-wk-list"});o.style.display="flex",o.style.flexDirection="column",o.style.gap="16px";for(let c of r)await this.renderWikiKeeperCard(o,c)}async renderWikiKeeperCard(e,s){let n=s.wikiKeeper,a=e.createDiv({cls:"af-card"});a.style.padding="16px",a.style.border="1px solid var(--background-modifier-border)",a.style.borderRadius="8px";let r=a.createDiv();r.style.display="flex",r.style.alignItems="center",r.style.gap="12px",r.style.marginBottom="12px";let o=r.createDiv();o.style.flex="1",o.createEl("strong",{text:s.name});let c=n.scopeRoot||"(whole vault)";o.createEl("div",{text:`Scope: ${c} \xB7 topics: ${n.topicsRoot}/ \xB7 log: ${n.logPath}`,cls:"af-form-hint"});let l=r.createEl("button",{cls:"af-btn-sm"});l.appendText("Open log"),l.onclick=()=>{let y=n.scopeRoot?`${n.scopeRoot}/${n.logPath}`:n.logPath,w=this.plugin.app.vault.getAbstractFileByPath(y);w instanceof b.TFile?this.plugin.app.workspace.getLeaf().openFile(w):new b.Notice(`Log file not found: ${y}`)};let d=n.scopeRoot?`${n.scopeRoot}/${n.logPath}`:n.logPath,h=this.plugin.app.vault.getAbstractFileByPath(d),u=null;if(h instanceof b.TFile)try{let y=await this.plugin.app.vault.cachedRead(h);u=vo(y)}catch{u=null}if(!u){a.createDiv({cls:"af-form-hint"}).setText("No lint report yet. Run wiki-lint manually or wait for the weekly task to fire.");return}let p=a.createDiv();if(p.style.display="flex",p.style.alignItems="baseline",p.style.gap="12px",p.style.marginBottom="8px",p.createEl("strong",{text:`Lint ${u.date}`}),p.createSpan({cls:"af-form-hint",text:`${u.summary.length} summary lines \xB7 ${u.autoApplied.length} auto-applied \xB7 ${u.needsReview.length} needs review`}),u.summary.length>0){let y=a.createEl("details");y.createEl("summary",{text:"Summary"});let w=y.createEl("ul");w.style.marginTop="4px";for(let k of u.summary)w.createEl("li",{text:k})}if(u.autoApplied.length>0){let y=a.createEl("details");y.createEl("summary",{text:`Auto-applied (${u.autoApplied.length})`});let w=y.createEl("ul");w.style.marginTop="4px";for(let k of u.autoApplied)w.createEl("li",{text:k})}if(u.refreshChained.length>0){let y=a.createEl("details");y.createEl("summary",{text:`Refresh chained (${u.refreshChained.length})`});let w=y.createEl("ul");w.style.marginTop="4px";for(let k of u.refreshChained)w.createEl("li",{text:k})}let m=a.createDiv();if(m.style.marginTop="12px",m.createEl("strong",{text:`Needs review (${u.needsReview.length})`}),u.needsReview.length===0){m.createDiv({cls:"af-form-hint",text:"All clear. Nothing requires manual review from this lint pass."});return}let f=m.createDiv();f.style.display="flex",f.style.flexDirection="column",f.style.gap="6px",f.style.marginTop="8px";for(let y of u.needsReview){let w=f.createDiv();w.style.display="flex",w.style.alignItems="flex-start",w.style.gap="8px",w.style.padding="8px 10px",w.style.background="var(--background-secondary)",w.style.borderRadius="4px",w.style.fontSize="13px";let k=w.createDiv();k.style.flex="1",k.setText(y);let g=w.createEl("button",{cls:"af-btn-sm",text:"Dismiss"});g.title="Hide this item from the dashboard until the next lint pass (does not modify log.md).",g.onclick=()=>{w.remove()}}}renderChannelCard(e,s,n){let a=this.plugin.channelManager?.getChannelStatus(s.name)??"disabled",r=ko(a),o=s.enabled&&a!=="disabled"?"af-agent-card":"af-agent-card disabled",c=e.createDiv({cls:o});c.style.cursor="default";let l=c.createDiv({cls:"af-agent-card-header"}),d=l.createDiv({cls:`af-agent-card-avatar ${r}`});(0,b.setIcon)(d,"radio");let h=l.createDiv({cls:"af-agent-card-titleblock"});h.createDiv({cls:"af-agent-card-name",text:s.name}),h.createDiv({cls:"af-agent-card-desc",text:`Default: ${s.defaultAgent}`});let u=l.createSpan({cls:`af-pill ${Cd(a)}`});if(u.createSpan({cls:"af-dot"}),u.appendText(` ${a}`),s.allowedAgents.length>0){let T=c.createDiv({cls:"af-agent-card-skills"});for(let _ of s.allowedAgents){let D=T.createSpan({cls:"af-skill-tag",text:_});_===s.defaultAgent&&(D.style.fontWeight="700")}}let p=c.createDiv({cls:"af-agent-card-stats"}),m=this.plugin.channelManager?.getSessionCount(s.name)??0,f=this.plugin.channelManager?.getMetrics(s.name),y=s.allowedAgents.length>0?String(s.allowedAgents.length):"all";this.renderAgentStat(p,y,"Agents"),this.renderAgentStat(p,String(m),"Sessions"),this.renderAgentStat(p,String(f?.messagesReceived??0),"In"),this.renderAgentStat(p,String(f?.messagesSent??0),"Out");let w=c.createDiv({cls:"af-agent-card-footer"}),k=[s.type];s.enabled||k.push("disabled"),s.allowedUsers.length>0?k.push(`${s.allowedUsers.length} user(s)`):k.push("allowlist empty"),w.createSpan({cls:"af-agent-card-meta",text:k.join(" \xB7 ")});let v=w.createDiv({cls:"af-agent-card-actions"}).createEl("button",{cls:"af-btn-sm"});A(v,"edit","af-btn-icon"),v.appendText(" Edit"),v.onclick=T=>{T.stopPropagation(),this.navigate("edit-channel",s.name)};let S=n.filter(T=>T.path===s.filePath);if(S.length>0){let T=c.createDiv({cls:"af-channel-issues"});for(let _ of S)T.createDiv({cls:"af-channel-issue-row",text:_.message})}}renderCreateChannelPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),n=this.plugin.runtime.getSnapshot(),a=this.plugin.channelCredentials.list(),r=s.createDiv({cls:"af-detail-header"}),o=r.createDiv({cls:"af-detail-header-left"}),c=o.createDiv({cls:"af-agent-card-avatar idle"});(0,b.setIcon)(c,"plus");let l=o.createDiv();l.createDiv({cls:"af-detail-header-name",text:"Create New Channel"}),l.createDiv({cls:"af-detail-header-desc",text:"Connect an external chat transport to an agent"}),r.createDiv({cls:"af-detail-header-actions"});let d={name:"",type:"slack",defaultAgent:n.agents[0]?.name??"",allowedAgents:[],credentialRef:a[0]?.ref??"",allowedUsers:"",perUserSessions:!0,channelContext:"",enabled:!0,tags:"",body:"",transportJson:""},h=s.createDiv({cls:"af-create-form"}),u=h.createDiv({cls:"af-create-section"}),p=u.createDiv({cls:"af-create-section-header"}),m=p.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(m,"radio"),p.createSpan({text:"Channel Details"}),this.createFormField(u,"Name","my-slack","Unique identifier for this channel",j=>{d.name=j});let f=u.createDiv({cls:"af-form-row"});f.createDiv({cls:"af-form-label",text:"Type"});let y=f.createEl("select",{cls:"af-form-select"});y.createEl("option",{text:"slack",attr:{value:"slack"}}),y.createEl("option",{text:"telegram",attr:{value:"telegram"}}),y.addEventListener("change",()=>{d.type=y.value});let w=u.createDiv({cls:"af-form-row"}),k=w.createDiv({cls:"af-form-label"});k.setText("Credential"),this.addTooltip(k,"Configured in Settings \u2192 Agent Fleet \u2192 Channel Credentials");let g=w.createEl("select",{cls:"af-form-select"});a.length===0&&g.createEl("option",{text:"(no credentials configured)",attr:{value:""}});for(let j of a)g.createEl("option",{text:`${j.ref} (${j.entry.type})`,attr:{value:j.ref}});g.addEventListener("change",()=>{d.credentialRef=g.value});let v=u.createDiv({cls:"af-form-row af-form-row-toggle"});v.createDiv({cls:"af-form-label",text:"Enabled"});let S=v.createDiv({cls:"af-agent-card-toggle on"});S.onclick=()=>{let j=S.hasClass("on");S.toggleClass("on",!j),d.enabled=!j};let T=h.createDiv({cls:"af-create-section"}),_=T.createDiv({cls:"af-create-section-header"}),D=_.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(D,"bot"),_.createSpan({text:"Agent Routing"});let E=T.createDiv({cls:"af-form-row"}),C=E.createDiv({cls:"af-form-label"});C.setText("Default agent"),this.addTooltip(C,"Used when no @agent-name prefix is given in a message");let L=E.createEl("select",{cls:"af-form-select"});for(let j of n.agents)L.createEl("option",{text:j.name,attr:{value:j.name}});L.addEventListener("change",()=>{d.defaultAgent=L.value});let R=T.createDiv({cls:"af-form-row"}),B=R.createDiv({cls:"af-form-label"});B.setText("Allowed agents"),this.addTooltip(B,"Agents reachable via @prefix. Leave unchecked to allow all agents.");let F=R.createDiv({cls:"af-form-checkboxes"});for(let j of n.agents){let we=F.createEl("label",{cls:"af-form-checkbox-label"}),xe=we.createEl("input",{attr:{type:"checkbox",value:j.name}});we.appendText(` ${j.name}`),xe.addEventListener("change",()=>{xe.checked?d.allowedAgents.includes(j.name)||d.allowedAgents.push(j.name):d.allowedAgents=d.allowedAgents.filter(ve=>ve!==j.name)})}let Y=T.createDiv({cls:"af-form-row af-form-row-toggle"}),U=Y.createDiv({cls:"af-form-label"});U.setText("Per-user sessions"),this.addTooltip(U,"Each external user gets their own isolated Claude session");let W=Y.createDiv({cls:"af-agent-card-toggle on"});W.onclick=()=>{let j=W.hasClass("on");W.toggleClass("on",!j),d.perUserSessions=!j};let ue=h.createDiv({cls:"af-create-section"}),ge=ue.createDiv({cls:"af-create-section-header"}),pe=ge.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(pe,"shield-check"),ge.createSpan({text:"Access Control"});let Z=ue.createDiv({cls:"af-form-label"});Z.setText("Allowed users"),this.addTooltip(Z,"Slack user IDs (U...), one per line. Only listed users can reach the bot.");let K=ue.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`U0AQW6P37N1
|
|
12007
|
+
U0BXYZ12345`,rows:"4"}});K.addEventListener("input",()=>{d.allowedUsers=K.value});let H=h.createDiv({cls:"af-create-section"}),ne=H.createDiv({cls:"af-create-section-header"}),ae=ne.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(ae,"message-square");let ce=ne.createSpan({text:"Channel Context"});this.addTooltip(ce,"Extra instructions appended to the agent's system prompt when reached through this channel");let re=H.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"You are being contacted via Slack. Keep replies concise...",rows:"6"}});re.addEventListener("input",()=>{d.channelContext=re.value}),this.createFormField(u,"Tags","ops, internal","Comma-separated metadata",j=>{d.tags=j});let Ee=h.createDiv({cls:"af-create-section"}),Ae=Ee.createDiv({cls:"af-create-section-header"}),Ne=Ae.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(Ne,"settings");let Ue=Ae.createSpan({text:"Advanced"});this.addTooltip(Ue,"Markdown body (shown in the channel detail page) and transport-specific overrides");let Pe=Ee.createDiv({cls:"af-form-label"});Pe.setText("Body (markdown)"),this.addTooltip(Pe,"Free-form notes for this channel. Shown in the channel detail page; not sent to the agent.");let Te=Ee.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Notes, runbook snippets, escalation contacts\u2026",rows:"4"}});Te.addEventListener("input",()=>{d.body=Te.value});let Re=Ee.createDiv({cls:"af-form-label"});Re.setText("Transport config (JSON)"),this.addTooltip(Re,"Optional JSON object for transport-specific overrides (e.g. Slack socket_mode, telegram webhook settings). Leave blank for defaults.");let Be=Ee.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`{
|
|
11839
12008
|
"socket_mode": true
|
|
11840
|
-
}`,rows:"4"}});Be.addEventListener("input",()=>{d.transportJson=Be.value});let
|
|
11841
|
-
`),perUserSessions:
|
|
11842
|
-
U0BXYZ12345`,rows:"4"}});
|
|
11843
|
-
`),
|
|
12009
|
+
}`,rows:"4"}});Be.addEventListener("input",()=>{d.transportJson=Be.value});let $e=s.createDiv({cls:"af-create-footer"}),q=$e.createEl("button",{cls:"af-btn-sm",text:"Cancel"});q.onclick=()=>this.navigate("channels");let te=$e.createEl("button",{cls:"af-btn-sm primary af-create-submit"});A(te,"plus","af-btn-icon"),te.appendText(" Create Channel"),te.onclick=async()=>{let j=d.name.trim();if(!j){new b.Notice("Channel name is required.");return}if(!d.credentialRef){new b.Notice("Select a credential.");return}let we;if(d.transportJson.trim())try{let J=JSON.parse(d.transportJson);if(J&&typeof J=="object"&&!Array.isArray(J))we=J;else{new b.Notice("Transport config must be a JSON object.");return}}catch(J){new b.Notice(`Transport JSON is invalid: ${J instanceof Error?J.message:String(J)}`);return}let xe=J=>J.split(/[\n,]+/).map(P=>P.trim()).filter(Boolean),ve=J=>J.split(",").map(P=>P.trim()).filter(Boolean),De={name:se(j),type:d.type,default_agent:d.defaultAgent,allowed_agents:d.allowedAgents.length>0?d.allowedAgents:void 0,enabled:d.enabled,credential_ref:d.credentialRef,allowed_users:xe(d.allowedUsers),per_user_sessions:d.perUserSessions,channel_context:d.channelContext.trim()||void 0,tags:ve(d.tags).length>0?ve(d.tags):void 0,transport:we};try{let J=se(j),P=await this.plugin.repository.getAvailablePath(this.plugin.repository.getSubfolder("channels"),J);await this.plugin.app.vault.create(P,z(De,d.body.trim())),new b.Notice(`Channel "${J}" created.`),await this.plugin.refreshFromVault(),this.navigate("edit-channel",J)}catch(J){let P=J instanceof Error?J.message:String(J);new b.Notice(`Failed to create channel: ${P}`)}}}renderEditChannelPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),n=this.detailContext;if(!n){this.renderEmptyState(s,"radio","No channel selected","");return}let a=this.plugin.runtime.getSnapshot().channels.find(P=>P.name===n);if(!a){this.renderEmptyState(s,"radio","Channel not found",`Channel "${n}" was not found`);return}let r=this.plugin.runtime.getSnapshot(),o=this.plugin.channelCredentials.list(),c=this.plugin.channelManager?.getChannelStatus(a.name)??"disabled",l=s.createDiv({cls:"af-detail-header"}),d=l.createDiv({cls:"af-detail-header-left"}),h=d.createDiv({cls:`af-agent-card-avatar ${ko(c)}`});(0,b.setIcon)(h,"radio");let u=d.createDiv();u.createDiv({cls:"af-detail-header-name",text:`Edit Channel: ${a.name}`}),u.createDiv({cls:"af-detail-header-desc",text:`Status: ${c}`}),l.createDiv({cls:"af-detail-header-actions"});let p={type:a.type,defaultAgent:a.defaultAgent,allowedAgents:[...a.allowedAgents],credentialRef:a.credentialRef,allowedUsers:a.allowedUsers.join(`
|
|
12010
|
+
`),perUserSessions:a.perUserSessions,channelContext:a.channelContext,enabled:a.enabled,tags:a.tags.join(", "),body:a.body,transportJson:Object.keys(a.transport).length>0?JSON.stringify(a.transport,null,2):""},m=s.createDiv({cls:"af-create-form"}),f=m.createDiv({cls:"af-create-section"}),y=f.createDiv({cls:"af-create-section-header"}),w=y.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(w,"radio"),y.createSpan({text:"Channel Details"});let k=f.createDiv({cls:"af-form-row"});k.createDiv({cls:"af-form-label",text:"Name"});let g=k.createEl("input",{cls:"af-form-input",attr:{type:"text",value:a.name,disabled:"true"}});g.style.opacity="0.6";let v=f.createDiv({cls:"af-form-row"});v.createDiv({cls:"af-form-label",text:"Type"});let S=v.createEl("select",{cls:"af-form-select"});for(let P of["slack","telegram"]){let X=S.createEl("option",{text:P,attr:{value:P}});P===a.type&&(X.selected=!0)}S.addEventListener("change",()=>{p.type=S.value});let T=f.createDiv({cls:"af-form-row"}),_=T.createDiv({cls:"af-form-label"});_.setText("Credential"),this.addTooltip(_,"Configured in Settings \u2192 Agent Fleet \u2192 Channel Credentials");let D=T.createEl("select",{cls:"af-form-select"});o.length===0&&D.createEl("option",{text:"(no credentials configured)",attr:{value:""}});for(let P of o){let X=D.createEl("option",{text:`${P.ref} (${P.entry.type})`,attr:{value:P.ref}});P.ref===a.credentialRef&&(X.selected=!0)}D.addEventListener("change",()=>{p.credentialRef=D.value});let E=f.createDiv({cls:"af-form-row af-form-row-toggle"});E.createDiv({cls:"af-form-label",text:"Enabled"});let C=E.createDiv({cls:`af-agent-card-toggle${a.enabled?" on":""}`});C.onclick=()=>{let P=C.hasClass("on");C.toggleClass("on",!P),p.enabled=!P};let L=m.createDiv({cls:"af-create-section"}),R=L.createDiv({cls:"af-create-section-header"}),B=R.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(B,"bot"),R.createSpan({text:"Agent Routing"});let F=L.createDiv({cls:"af-form-row"}),Y=F.createDiv({cls:"af-form-label"});Y.setText("Default agent"),this.addTooltip(Y,"Used when no @agent-name prefix is given in a message");let U=F.createEl("select",{cls:"af-form-select"});for(let P of r.agents){let X=U.createEl("option",{text:P.name,attr:{value:P.name}});P.name===a.defaultAgent&&(X.selected=!0)}U.addEventListener("change",()=>{p.defaultAgent=U.value});let W=L.createDiv({cls:"af-form-row"}),ue=W.createDiv({cls:"af-form-label"});ue.setText("Allowed agents"),this.addTooltip(ue,"Agents reachable via @prefix. Leave unchecked to allow all agents.");let ge=W.createDiv({cls:"af-form-checkboxes"});for(let P of r.agents){let X=ge.createEl("label",{cls:"af-form-checkbox-label"}),de=X.createEl("input",{attr:{type:"checkbox",value:P.name}});a.allowedAgents.includes(P.name)&&(de.checked=!0),X.appendText(` ${P.name}`),de.addEventListener("change",()=>{de.checked?p.allowedAgents.includes(P.name)||p.allowedAgents.push(P.name):p.allowedAgents=p.allowedAgents.filter(me=>me!==P.name)})}let pe=L.createDiv({cls:"af-form-row af-form-row-toggle"}),Z=pe.createDiv({cls:"af-form-label"});Z.setText("Per-user sessions"),this.addTooltip(Z,"Each external user gets their own isolated Claude session");let K=pe.createDiv({cls:`af-agent-card-toggle${a.perUserSessions?" on":""}`});K.onclick=()=>{let P=K.hasClass("on");K.toggleClass("on",!P),p.perUserSessions=!P};let H=m.createDiv({cls:"af-create-section"}),ne=H.createDiv({cls:"af-create-section-header"}),ae=ne.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(ae,"shield-check"),ne.createSpan({text:"Access Control"});let ce=H.createDiv({cls:"af-form-label"});ce.setText("Allowed users"),this.addTooltip(ce,"Slack user IDs (U...), one per line. Only listed users can reach the bot.");let re=H.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`U0AQW6P37N1
|
|
12011
|
+
U0BXYZ12345`,rows:"4"}});re.value=a.allowedUsers.join(`
|
|
12012
|
+
`),re.addEventListener("input",()=>{p.allowedUsers=re.value});let Ee=m.createDiv({cls:"af-create-section"}),Ae=Ee.createDiv({cls:"af-create-section-header"}),Ne=Ae.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(Ne,"message-square");let Ue=Ae.createSpan({text:"Channel Context"});this.addTooltip(Ue,"Extra instructions appended to the agent's system prompt when reached through this channel");let Pe=Ee.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"You are being contacted via Slack. Keep replies concise...",rows:"6"}});Pe.value=a.channelContext,Pe.addEventListener("input",()=>{p.channelContext=Pe.value}),this.createFormField(f,"Tags","ops, internal","Comma-separated metadata",P=>{p.tags=P},a.tags.join(", "));let Te=m.createDiv({cls:"af-create-section"}),Re=Te.createDiv({cls:"af-create-section-header"}),Be=Re.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(Be,"settings");let $e=Re.createSpan({text:"Advanced"});this.addTooltip($e,"Markdown body (shown in the channel detail page) and transport-specific overrides"),Te.createDiv({cls:"af-form-label"}).setText("Body (markdown)");let te=Te.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Notes, runbook snippets, escalation contacts\u2026",rows:"4"}});te.value=a.body,te.addEventListener("input",()=>{p.body=te.value});let j=Te.createDiv({cls:"af-form-label"});j.setText("Transport config (JSON)"),this.addTooltip(j,"Optional JSON object for transport-specific overrides (e.g. Slack socket_mode). Leave blank for defaults.");let we=Te.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`{
|
|
11844
12013
|
"socket_mode": true
|
|
11845
|
-
}`,rows:"4"}});
|
|
12014
|
+
}`,rows:"4"}});we.value=p.transportJson,we.addEventListener("input",()=>{p.transportJson=we.value});let xe=s.createDiv({cls:"af-create-footer"}),ve=xe.createEl("button",{cls:"af-btn-sm danger"});A(ve,"trash-2","af-btn-icon"),ve.appendText(" Delete"),ve.onclick=async()=>{await this.plugin.repository.deleteChannel(a.name),new b.Notice(`Channel "${a.name}" deleted.`),await new Promise(P=>setTimeout(P,200)),await this.plugin.refreshFromVault(),this.navigate("channels")},xe.createDiv({cls:"af-toolbar-spacer"});let De=xe.createEl("button",{cls:"af-btn-sm",text:"Cancel"});De.onclick=()=>this.navigate("channels");let J=xe.createEl("button",{cls:"af-btn-sm primary af-create-submit"});A(J,"check","af-btn-icon"),J.appendText(" Save Changes"),J.onclick=async()=>{let P=me=>me.split(/[\n,]+/).map(tt=>tt.trim()).filter(Boolean),X=me=>me.split(",").map(tt=>tt.trim()).filter(Boolean),de;if(p.transportJson.trim())try{let me=JSON.parse(p.transportJson);if(me&&typeof me=="object"&&!Array.isArray(me))de=me;else{new b.Notice("Transport config must be a JSON object.");return}}catch(me){new b.Notice(`Transport JSON is invalid: ${me instanceof Error?me.message:String(me)}`);return}else de={};try{await this.plugin.repository.updateChannel(a.name,{type:p.type,default_agent:p.defaultAgent,allowed_agents:p.allowedAgents.length>0?p.allowedAgents:[],enabled:p.enabled,credential_ref:p.credentialRef,allowed_users:P(p.allowedUsers),per_user_sessions:p.perUserSessions,channel_context:p.channelContext.trim(),tags:X(p.tags),body:p.body.trim(),transport:de}),new b.Notice(`Channel "${a.name}" updated.`),await this.plugin.refreshFromVault(),this.navigate("channels")}catch(me){let tt=me instanceof Error?me.message:String(me);new b.Notice(`Failed to update channel: ${tt}`)}}}renderApprovalsPage(e){let s=e.createDiv({cls:"af-approvals-page"}),n=this.plugin.runtime.getRecentRuns(),a=s.createDiv({cls:"af-agents-toolbar"});a.createDiv({cls:"af-page-title",text:"Approvals"}),a.createDiv({cls:"af-toolbar-spacer"});let r=n.filter(c=>(c.approvals??[]).some(l=>l.status==="pending"));if(r.length>0){let c=s.createDiv({cls:"af-section-card"}),d=c.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});A(d,"alert-triangle"),d.appendText(` Pending (${r.length})`);let h=c.createDiv({cls:"af-approvals-list"});for(let u of r)this.renderApprovalItem(h,u,!0)}else{let c=s.createDiv({cls:"af-section-card"});this.renderEmptyState(c,"shield-check","No pending approvals","All clear!")}let o=n.filter(c=>(c.approvals??[]).some(l=>l.status!=="pending"));if(o.length>0){let c=s.createDiv({cls:"af-section-card"}),d=c.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});A(d,"check-circle-2"),d.appendText(" History");let h=c.createDiv({cls:"af-approvals-list"});for(let u of o.slice(0,20))this.renderApprovalItem(h,u,!1)}}renderApprovalItem(e,s,n){for(let a of s.approvals??[]){if(n&&a.status!=="pending"||!n&&a.status==="pending")continue;let r=e.createDiv({cls:"af-approval-item"}),o=r.createDiv({cls:"af-approval-item-icon"});a.status==="pending"?((0,b.setIcon)(o,"shield-check"),o.addClass("pending")):a.status==="approved"?((0,b.setIcon)(o,"check-circle-2"),o.addClass("approved")):((0,b.setIcon)(o,"x-circle"),o.addClass("rejected"));let c=r.createDiv({cls:"af-approval-item-body"});if(c.createDiv({cls:"af-approval-item-title",text:`${s.agent} \u2192 ${a.tool}`}),c.createDiv({cls:"af-approval-item-meta",text:`Task: ${s.task} \xB7 ${a.command??"no command"} \xB7 ${this.formatStarted(s.started)}`}),a.reason&&c.createDiv({cls:"af-approval-item-reason",text:`Reason: ${a.reason}`}),n&&a.status==="pending"){let l=r.createDiv({cls:"af-approval-item-actions"}),d=l.createEl("button",{cls:"af-btn-approve"});A(d,"check-circle-2","af-btn-icon"),d.appendText(" Approve"),d.onclick=()=>void this.plugin.runtime.resolveApproval(s,a.tool,"approved").then(()=>this.render());let h=l.createEl("button",{cls:"af-btn-reject"});A(h,"x-circle","af-btn-icon"),h.appendText(" Reject"),h.onclick=()=>void this.plugin.runtime.resolveApproval(s,a.tool,"rejected").then(()=>this.render())}}}renderTaskDetailPage(e){let s=e.createDiv({cls:"af-task-detail-page"}),n=this.detailContext;if(!n){this.renderEmptyState(s,"circle-dot","No task selected","");return}let a=this.plugin.runtime.getSnapshot().tasks.find(R=>R.taskId===n);if(!a){this.renderEmptyState(s,"circle-dot","Task not found",`Task "${n}" was not found`);return}let r=this.plugin.runtime.getSnapshot(),o=this.plugin.runtime.getRecentRuns().filter(R=>R.task===n),c=r.agents.find(R=>R.name===a.agent),l=s.createDiv({cls:"af-detail-header"}),d=l.createDiv({cls:"af-detail-header-left"}),h=d.createDiv({cls:"af-agent-card-avatar idle"});(0,b.setIcon)(h,"circle-dot");let u=d.createDiv();u.createDiv({cls:"af-detail-header-name",text:a.taskId}),u.createDiv({cls:"af-detail-header-desc",text:`Agent: ${a.agent}`});let p=l.createDiv({cls:"af-detail-header-actions"}),m=p.createEl("button",{cls:"af-btn-sm"});A(m,"edit","af-btn-icon"),m.appendText(" Edit"),m.onclick=()=>this.navigate("edit-task",a.taskId);let f=p.createEl("button",{cls:"af-btn-sm primary"});A(f,"play","af-btn-icon"),f.appendText(" Run Now"),f.onclick=()=>void this.plugin.runtime.runTaskNow(a);let y=s.createDiv({cls:"af-section-card"}),k=y.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});A(k,"file-text"),k.appendText(" Details");let g=y.createDiv({cls:"af-config-form"});this.renderConfigRow(g,"Agent",a.agent),this.renderConfigRow(g,"Priority",a.priority.charAt(0).toUpperCase()+a.priority.slice(1)),this.renderConfigRow(g,"Status",a.enabled?"Enabled":"Disabled");let v=a.schedule?this.humanizeCron(a.schedule):a.runAt??"Manual (run on demand)";this.renderConfigRow(g,"Schedule",v),a.schedule&&this.renderConfigRow(g,"Catch up if missed",a.catchUp?"Yes":"No"),this.renderConfigRow(g,"Created",a.created),this.renderConfigRow(g,"Runs",String(a.runCount)),a.lastRun&&this.renderConfigRow(g,"Last Run",this.formatStarted(a.lastRun));let S=s.createDiv({cls:"af-section-card"}),_=S.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});A(_,"message-square"),_.appendText(" Instructions"),S.createDiv({cls:"af-output-block",text:a.body||"(empty)"});let D=s.createDiv({cls:"af-section-card"}),C=D.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});A(C,"scroll-text"),C.appendText(" Recent Runs");let L=D.createDiv({cls:"af-timeline"});if(o.length===0)this.renderEmptyState(L,"scroll-text","No runs yet","");else for(let R of o.slice(0,10))this.renderTimelineItem(L,R)}handleSearch(e,s){if(s.querySelector(".af-search-results")?.remove(),e.length<2)return;let n=e.toLowerCase(),a=this.plugin.runtime.getSnapshot(),r=this.plugin.runtime.getRecentRuns(),o=[];for(let l of a.agents)(l.name.toLowerCase().includes(n)||(l.description?.toLowerCase().includes(n)??!1))&&o.push({label:`Agent: ${l.name}`,icon:"bot",action:()=>this.navigate("agent-detail",l.name)});for(let l of a.tasks)(l.taskId.toLowerCase().includes(n)||l.body.toLowerCase().includes(n))&&o.push({label:`Task: ${l.taskId}`,icon:"circle-dot",action:()=>this.navigate("task-detail",l.taskId)});for(let l of a.skills)l.name.toLowerCase().includes(n)&&o.push({label:`Skill: ${l.name}`,icon:"puzzle",action:()=>this.navigate("edit-skill",l.name)});for(let l of r.slice(0,20))l.output.toLowerCase().includes(n)&&o.push({label:`Run: ${l.agent} / ${l.task}`,icon:"scroll-text",action:()=>this.openSlideover(l)});if(o.length===0)return;let c=s.createDiv({cls:"af-search-results"});for(let l of o.slice(0,10)){let d=c.createDiv({cls:"af-search-result-item"});A(d,l.icon,"af-search-result-icon"),d.createSpan({text:l.label}),d.onclick=()=>{c.remove(),l.action()}}}openChatSlideover(e){this.plugin.openChatView(e.name)}openSlideover(e){this.contentEl.querySelector(".af-slideover-overlay")?.remove();let s=this.contentEl.createDiv({cls:"af-slideover-overlay"}),n=s.createDiv({cls:"af-slideover"}),a=n.createDiv({cls:"af-slideover-header"});a.createDiv({cls:"af-slideover-title",text:"Run Details"});let r=a.createEl("button",{cls:"clickable-icon"});(0,b.setIcon)(r,"cross"),r.onclick=()=>s.remove();let o=n.createDiv({cls:"af-slideover-body"}),c=o.createDiv({cls:"af-slideover-section"});c.createDiv({cls:"af-slideover-section-title",text:"METADATA"}),this.renderDetailRow(c,"Run ID",e.runId.slice(0,8)),this.renderDetailRow(c,"Agent",e.agent),this.renderDetailRow(c,"Task",e.task);let l=c.createDiv({cls:"af-detail-row"});l.createSpan({cls:"af-detail-label",text:"Status"});let h=l.createSpan({cls:"af-detail-value"}).createSpan({cls:`af-status-badge ${this.statusToBadgeClass(e.status)}`}),u=h.createSpan();(0,b.setIcon)(u,this.statusToIconName(e.status)),h.appendText(` ${this.statusToBadgeText(e.status)}`),this.renderDetailRow(c,"Started",e.started),this.renderDetailRow(c,"Duration",this.formatDuration(e.durationSeconds)),this.renderDetailRow(c,"Tokens",e.tokensUsed?e.tokensUsed.toLocaleString():"\u2014");{let v={task:"from task override",agent:"from agent",settings:"from settings default","cli-default":"CLI default"},S=e.modelSource?` (${v[e.modelSource]??e.modelSource})`:"",T=e.concreteModel&&e.concreteModel!==e.model?` \u2192 ${e.concreteModel}`:"";this.renderDetailRow(c,"Model",`${e.model}${T}${S}`)}let p=5e4,m=v=>v.length>p?v.slice(0,p)+`
|
|
11846
12015
|
|
|
11847
12016
|
---
|
|
11848
|
-
*Truncated (${(
|
|
11849
|
-
`)})}let b=o.createDiv({cls:"af-slideover-actions"});if(e.filePath){let y=b.createEl("button",{cls:"af-btn-sm"});D(y,"external-link","af-btn-icon"),y.appendText(" Open Run Note"),y.onclick=()=>void this.plugin.openPath(e.filePath)}let v=b.createEl("button",{cls:"af-btn-sm primary"});D(v,"refresh-cw","af-btn-icon"),v.appendText(" Re-run Task"),v.onclick=()=>void this.plugin.runAgentPrompt(e.agent),s.onclick=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 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,_=d.createEl("option",{text:`${T} ${x}`,attr:{value:String(y)}});y===a.hour&&(_.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 p=u.createDiv({cls:"af-schedule-day-buttons"}),f=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],m=new Set(a.days);for(let y=0;y<7;y++){let x=p.createEl("button",{cls:`af-schedule-day-btn${m.has(y)?" active":""}`,text:f[y]});x.onclick=()=>{m.has(y)?m.delete(y):m.add(y),x.toggleClass("active",m.has(y)),v()}}let g=e.createDiv({cls:"af-form-row af-schedule-dom-row"});g.createDiv({cls:"af-form-label",text:"Day of month"});let k=g.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),T=y==="weekly",_=y==="monthly";l.style.display=x?"":"none",u.style.display=T?"":"none",g.style.display=_?"":"none"},v=()=>{let y=i.value,x=d.value,T=h.value,_="";switch(y){case"every_5m":_="*/5 * * * *";break;case"every_15m":_="*/15 * * * *";break;case"every_30m":_="*/30 * * * *";break;case"every_hour":_="0 * * * *";break;case"every_2h":_="0 */2 * * *";break;case"daily":_=`${T} ${x} * * *`;break;case"weekdays":_=`${T} ${x} * * 1-5`;break;case"weekly":{let R=Array.from(m).sort().join(",")||"1";_=`${T} ${x} * * ${R}`;break}case"monthly":_=`${T} ${x} ${k.value} * *`;break}s.schedule=_,s.type="recurring"};i.addEventListener("change",()=>{b(),v()}),d.addEventListener("change",v),h.addEventListener("change",v),k.addEventListener("change",v),b()}renderHeartbeatSchedule(e,s){let a=this.parseCronComponents(s.heartbeatSchedule),n=e.createDiv({cls:"af-form-row"});n.createDiv({cls:"af-form-label",text:"Frequency"});let i=n.createEl("select",{cls:"af-form-select"}),o=[["every_5m","Every 5 minutes"],["every_15m","Every 15 minutes"],["every_30m","Every 30 minutes"],["every_hour","Every hour"],["every_2h","Every 2 hours"],["every_4h","Every 4 hours"],["every_6h","Every 6 hours"],["every_12h","Every 12 hours"],["daily","Once a day"]],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[g,k]of o){let b=i.createEl("option",{text:k,attr:{value:g}});g===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 g=0;g<24;g++){let k=g>=12?"PM":"AM",b=g===0?12:g>12?g-12:g,v=u.createEl("option",{text:`${b} ${k}`,attr:{value:String(g)}});g===a.hour&&(v.selected=!0)}h.createSpan({cls:"af-schedule-colon",text:":"});let p=h.createEl("select",{cls:"af-form-select af-form-select-sm"});for(let g=0;g<60;g+=5){let k=p.createEl("option",{text:String(g).padStart(2,"0"),attr:{value:String(g)}});g===a.minute&&(k.selected=!0)}let f=()=>{d.style.display=i.value==="daily"?"":"none"},m=()=>{let g=i.value,k=u.value,b=p.value;switch(g){case"every_5m":s.heartbeatSchedule="*/5 * * * *";break;case"every_15m":s.heartbeatSchedule="*/15 * * * *";break;case"every_30m":s.heartbeatSchedule="*/30 * * * *";break;case"every_hour":s.heartbeatSchedule="0 * * * *";break;case"every_2h":s.heartbeatSchedule="0 */2 * * *";break;case"every_4h":s.heartbeatSchedule="0 */4 * * *";break;case"every_6h":s.heartbeatSchedule="0 */6 * * *";break;case"every_12h":s.heartbeatSchedule="0 */12 * * *";break;case"daily":s.heartbeatSchedule=`${b} ${k} * * *`;break}};i.addEventListener("change",()=>{f(),m()}),u.addEventListener("change",m),p.addEventListener("change",m),f()}parseCronComponents(e){let s={freq:"daily",hour:9,minute:0,days:[1],dayOfMonth:1};if(!e?.trim())return s;let a={"*/5 * * * *":"every_5m","*/15 * * * *":"every_15m","*/30 * * * *":"every_30m","0 * * * *":"every_hour","0 */2 * * *":"every_2h"};if(a[e])return{...s,freq:a[e]};let n=e.trim().split(/\s+/);if(n.length!==5)return s;let[i,o,l,,c]=n,d=parseInt(o??"9",10),h=parseInt(i??"0",10);if(l==="*"&&c==="*")return{...s,freq:"daily",hour:d,minute:h};if(l==="*"&&c==="1-5")return{...s,freq:"weekdays",hour:d,minute:h};if(l==="*"&&c!=="*"){let u=(c??"1").split(",").map(p=>parseInt(p,10));return{...s,freq:"weekly",hour:d,minute:h,days:u}}return c==="*"&&l!=="*"?{...s,freq:"monthly",hour:d,minute:h,dayOfMonth:parseInt(l??"1",10)}:{...s,hour:d,minute:h}}humanizeCron(e){let s={"*/5 * * * *":"Every 5 minutes","*/10 * * * *":"Every 10 minutes","*/15 * * * *":"Every 15 minutes","*/30 * * * *":"Every 30 minutes","0 * * * *":"Every hour","0 */2 * * *":"Every 2 hours"};if(s[e])return s[e];let a=e.toLowerCase().trim();if(a.startsWith("every ")||a.startsWith("daily ")||a==="daily")return e;if(a.startsWith("hourly"))return"Every hour";if(a.startsWith("weekdays")||a.startsWith("weekly")||a.startsWith("monthly"))return e;let n=e.trim().split(/\s+/);if(n.length!==5)return e;let[i,o,l,,c]=n,d=(p,f)=>{let m=parseInt(p??"0",10),g=parseInt(f??"0",10),k=m>=12?"PM":"AM",b=m===0?12:m>12?m-12:m;return g===0?`${b} ${k}`:`${b}:${String(g).padStart(2,"0")} ${k}`},h=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],u=p=>p==="1-5"?"weekdays":p==="0,6"?"weekends":p.split(",").map(m=>parseInt(m,10)).map(m=>h[m]??m).join(", ");return o==="*"&&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:"",autoCompactThreshold:85,wikiReferences:[]},d={none:{label:"None",prompt:""},coding:{label:"Coding Agent",prompt:`You are a coding agent. Review code, write tests, fix bugs, and implement features.
|
|
12017
|
+
*Truncated (${(v.length/1024).toFixed(0)} KB total). Open the run note for full content.*`:v,f=!!(e.finalResult&&e.finalResult.trim()),y=e.output?.trim()??"",w=f&&y.length>0&&y!==e.finalResult.trim();if(f){let v=o.createDiv({cls:"af-slideover-section"});v.createDiv({cls:"af-slideover-section-title",text:"OUTPUT"});let S=v.createDiv({cls:"af-output-block af-compact-md"});if(b.MarkdownRenderer.render(this.app,m(e.finalResult),S,"",this.plugin),w){let T=v.createEl("details",{cls:"af-run-transcript"}),_=T.createEl("summary");(0,b.setIcon)(_.createSpan({cls:"af-run-transcript-icon"}),"file-text"),_.createSpan({text:"Show full transcript"}),_.createSpan({cls:"af-run-transcript-meta"}).setText(`${(y.length/1024).toFixed(1)} KB`);let E=T.createDiv({cls:"af-output-block af-compact-md af-run-transcript-body"});b.MarkdownRenderer.render(this.app,m(y),E,"",this.plugin)}}else if(y){let v=o.createDiv({cls:"af-slideover-section"});v.createDiv({cls:"af-slideover-section-title",text:"OUTPUT"});let S=v.createDiv({cls:"af-output-block af-compact-md"});b.MarkdownRenderer.render(this.app,m(y),S,"",this.plugin)}if(e.toolsUsed.length>0){let v=o.createDiv({cls:"af-slideover-section"});v.createDiv({cls:"af-slideover-section-title",text:"TOOLS USED"}),v.createDiv({cls:"af-output-block",text:e.toolsUsed.join(`
|
|
12018
|
+
`)})}let k=o.createDiv({cls:"af-slideover-actions"});if(e.filePath){let v=k.createEl("button",{cls:"af-btn-sm"});A(v,"external-link","af-btn-icon"),v.appendText(" Open Run Note"),v.onclick=()=>void this.plugin.openPath(e.filePath)}let g=k.createEl("button",{cls:"af-btn-sm primary"});A(g,"refresh-cw","af-btn-icon"),g.appendText(" Re-run Task"),g.onclick=()=>void this.plugin.runAgentPrompt(e.agent),s.onclick=v=>{v.target===s&&s.remove()}}renderDetailRow(e,s,n){let a=e.createDiv({cls:"af-detail-row"});a.createSpan({cls:"af-detail-label",text:s}),a.createSpan({cls:"af-detail-value af-mono",text:n})}renderEmptyState(e,s,n,a){let r=e.createDiv({cls:"af-empty-state"}),o=r.createDiv({cls:"af-empty-icon"});(0,b.setIcon)(o,s),r.createDiv({cls:"af-empty-label",text:n}),a&&r.createDiv({cls:"af-empty-sublabel",text:a})}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),n=e%60;return n>0?`${s}m ${n}s`:`${s}m`}formatStarted(e){try{let s=new Date(e),n=new Date;if(s.toDateString()===n.toDateString())return s.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"});let a=new Date(n);return a.setDate(a.getDate()-1),s.toDateString()===a.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),n=new Date,a=s.getTime()-n.getTime();if(a<0)return"overdue";let r=Math.round(a/6e4);if(r<60)return`${r}m`;let o=Math.round(r/60);return o<24?`${o}h`:s.toLocaleDateString([],{month:"short",day:"numeric"})}catch{return e}}getNextTaskLabel(e){let n=e.map(r=>r.nextRun).filter(Boolean).sort()[0];return n?`${e.find(r=>r.nextRun===n)?.agent??"unknown"} in ${this.formatNextRun(n)}`:"none"}getInitials(e){return e.split("-").map(s=>s[0]?.toUpperCase()??"").slice(0,2).join("")}renderAgentAvatar(e,s){let n=s.avatar?.trim();if(!n){(0,b.setIcon)(e,"bot");return}/^[a-z][a-z0-9-]*$/.test(n)?(0,b.setIcon)(e,n):e.setText(n)}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 n=this.parseCronComponents(s.schedule),a=e.createDiv({cls:"af-form-row"});a.createDiv({cls:"af-form-label",text:"Frequency"});let r=a.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[v,S]of o){let T=r.createEl("option",{text:S,attr:{value:v}});v===n.freq&&(T.selected=!0)}let c=e.createDiv({cls:"af-form-row af-schedule-time-row"});c.createDiv({cls:"af-form-label",text:"Time"});let l=c.createDiv({cls:"af-schedule-time-selects"}),d=l.createEl("select",{cls:"af-form-select af-form-select-sm"});for(let v=0;v<24;v++){let S=v>=12?"PM":"AM",T=v===0?12:v>12?v-12:v,_=d.createEl("option",{text:`${T} ${S}`,attr:{value:String(v)}});v===n.hour&&(_.selected=!0)}l.createSpan({cls:"af-schedule-colon",text:":"});let h=l.createEl("select",{cls:"af-form-select af-form-select-sm"});for(let v=0;v<60;v+=5){let S=h.createEl("option",{text:String(v).padStart(2,"0"),attr:{value:String(v)}});v===n.minute&&(S.selected=!0)}let u=e.createDiv({cls:"af-form-row af-schedule-day-row"});u.createDiv({cls:"af-form-label",text:"Day"});let p=u.createDiv({cls:"af-schedule-day-buttons"}),m=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],f=new Set(n.days);for(let v=0;v<7;v++){let S=p.createEl("button",{cls:`af-schedule-day-btn${f.has(v)?" active":""}`,text:m[v]});S.onclick=()=>{f.has(v)?f.delete(v):f.add(v),S.toggleClass("active",f.has(v)),g()}}let y=e.createDiv({cls:"af-form-row af-schedule-dom-row"});y.createDiv({cls:"af-form-label",text:"Day of month"});let w=y.createEl("select",{cls:"af-form-select af-form-select-sm"});for(let v=1;v<=28;v++){let S=w.createEl("option",{text:String(v),attr:{value:String(v)}});v===n.dayOfMonth&&(S.selected=!0)}let k=()=>{let v=r.value,S=["daily","weekdays","weekly","monthly"].includes(v),T=v==="weekly",_=v==="monthly";c.style.display=S?"":"none",u.style.display=T?"":"none",y.style.display=_?"":"none"},g=()=>{let v=r.value,S=d.value,T=h.value,_="";switch(v){case"every_5m":_="*/5 * * * *";break;case"every_15m":_="*/15 * * * *";break;case"every_30m":_="*/30 * * * *";break;case"every_hour":_="0 * * * *";break;case"every_2h":_="0 */2 * * *";break;case"daily":_=`${T} ${S} * * *`;break;case"weekdays":_=`${T} ${S} * * 1-5`;break;case"weekly":{let D=Array.from(f).sort().join(",")||"1";_=`${T} ${S} * * ${D}`;break}case"monthly":_=`${T} ${S} ${w.value} * *`;break}s.schedule=_,s.type="recurring"};r.addEventListener("change",()=>{k(),g()}),d.addEventListener("change",g),h.addEventListener("change",g),w.addEventListener("change",g),k()}renderHeartbeatSchedule(e,s){let n=this.parseCronComponents(s.heartbeatSchedule),a=e.createDiv({cls:"af-form-row"});a.createDiv({cls:"af-form-label",text:"Frequency"});let r=a.createEl("select",{cls:"af-form-select"}),o=[["every_5m","Every 5 minutes"],["every_15m","Every 15 minutes"],["every_30m","Every 30 minutes"],["every_hour","Every hour"],["every_2h","Every 2 hours"],["every_4h","Every 4 hours"],["every_6h","Every 6 hours"],["every_12h","Every 12 hours"],["daily","Once a day"]],c="every_hour",l={"*/5 * * * *":"every_5m","*/15 * * * *":"every_15m","*/30 * * * *":"every_30m","0 * * * *":"every_hour","0 */2 * * *":"every_2h","0 */4 * * *":"every_4h","0 */6 * * *":"every_6h","0 */12 * * *":"every_12h"};l[s.heartbeatSchedule]?c=l[s.heartbeatSchedule]:(n.freq==="daily"||n.freq==="weekdays")&&(c="daily");for(let[y,w]of o){let k=r.createEl("option",{text:w,attr:{value:y}});y===c&&(k.selected=!0)}let d=e.createDiv({cls:"af-form-row af-schedule-time-row"});d.createDiv({cls:"af-form-label",text:"Time"});let h=d.createDiv({cls:"af-schedule-time-selects"}),u=h.createEl("select",{cls:"af-form-select af-form-select-sm"});for(let y=0;y<24;y++){let w=y>=12?"PM":"AM",k=y===0?12:y>12?y-12:y,g=u.createEl("option",{text:`${k} ${w}`,attr:{value:String(y)}});y===n.hour&&(g.selected=!0)}h.createSpan({cls:"af-schedule-colon",text:":"});let p=h.createEl("select",{cls:"af-form-select af-form-select-sm"});for(let y=0;y<60;y+=5){let w=p.createEl("option",{text:String(y).padStart(2,"0"),attr:{value:String(y)}});y===n.minute&&(w.selected=!0)}let m=()=>{d.style.display=r.value==="daily"?"":"none"},f=()=>{let y=r.value,w=u.value,k=p.value;switch(y){case"every_5m":s.heartbeatSchedule="*/5 * * * *";break;case"every_15m":s.heartbeatSchedule="*/15 * * * *";break;case"every_30m":s.heartbeatSchedule="*/30 * * * *";break;case"every_hour":s.heartbeatSchedule="0 * * * *";break;case"every_2h":s.heartbeatSchedule="0 */2 * * *";break;case"every_4h":s.heartbeatSchedule="0 */4 * * *";break;case"every_6h":s.heartbeatSchedule="0 */6 * * *";break;case"every_12h":s.heartbeatSchedule="0 */12 * * *";break;case"daily":s.heartbeatSchedule=`${k} ${w} * * *`;break}};r.addEventListener("change",()=>{m(),f()}),u.addEventListener("change",f),p.addEventListener("change",f),m()}parseCronComponents(e){let s={freq:"daily",hour:9,minute:0,days:[1],dayOfMonth:1};if(!e?.trim())return s;let n={"*/5 * * * *":"every_5m","*/15 * * * *":"every_15m","*/30 * * * *":"every_30m","0 * * * *":"every_hour","0 */2 * * *":"every_2h"};if(n[e])return{...s,freq:n[e]};let a=e.trim().split(/\s+/);if(a.length!==5)return s;let[r,o,c,,l]=a,d=parseInt(o??"9",10),h=parseInt(r??"0",10);if(c==="*"&&l==="*")return{...s,freq:"daily",hour:d,minute:h};if(c==="*"&&l==="1-5")return{...s,freq:"weekdays",hour:d,minute:h};if(c==="*"&&l!=="*"){let u=(l??"1").split(",").map(p=>parseInt(p,10));return{...s,freq:"weekly",hour:d,minute:h,days:u}}return l==="*"&&c!=="*"?{...s,freq:"monthly",hour:d,minute:h,dayOfMonth:parseInt(c??"1",10)}:{...s,hour:d,minute:h}}humanizeCron(e){let s={"*/5 * * * *":"Every 5 minutes","*/10 * * * *":"Every 10 minutes","*/15 * * * *":"Every 15 minutes","*/30 * * * *":"Every 30 minutes","0 * * * *":"Every hour","0 */2 * * *":"Every 2 hours"};if(s[e])return s[e];let n=e.toLowerCase().trim();if(n.startsWith("every ")||n.startsWith("daily ")||n==="daily")return e;if(n.startsWith("hourly"))return"Every hour";if(n.startsWith("weekdays")||n.startsWith("weekly")||n.startsWith("monthly"))return e;let a=e.trim().split(/\s+/);if(a.length!==5)return e;let[r,o,c,,l]=a,d=(p,m)=>{let f=parseInt(p??"0",10),y=parseInt(m??"0",10),w=f>=12?"PM":"AM",k=f===0?12:f>12?f-12:f;return y===0?`${k} ${w}`:`${k}:${String(y).padStart(2,"0")} ${w}`},h=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],u=p=>p==="1-5"?"weekdays":p==="0,6"?"weekends":p.split(",").map(f=>parseInt(f,10)).map(f=>h[f]??f).join(", ");return o==="*"&&c==="*"&&l==="*"?r==="*"?"Every minute":`Every hour at :${String(r).padStart(2,"0")}`:c==="*"&&l==="*"&&o!=="*"?`Daily at ${d(o??"0",r??"0")}`:c==="*"&&l==="1-5"&&o!=="*"?`Weekdays at ${d(o??"0",r??"0")}`:c==="*"&&l!=="*"&&o!=="*"?`${u(l??"1")} at ${d(o??"0",r??"0")}`:l==="*"&&c!=="*"&&o!=="*"?`Monthly on the ${c} at ${d(o??"0",r??"0")}`:e}getTagClass(e){return e==="monitoring"?"monitoring":e==="devops"?"devops":e==="sample"?"sample":"default"}renderCreateAgentPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),n=s.createDiv({cls:"af-detail-header"}),a=n.createDiv({cls:"af-detail-header-left"}),r=a.createDiv({cls:"af-agent-card-avatar idle"});(0,b.setIcon)(r,"plus");let o=a.createDiv();o.createDiv({cls:"af-detail-header-name",text:"Create New Agent"}),o.createDiv({cls:"af-detail-header-desc",text:"Configure a new agent for your fleet"});let c=n.createDiv({cls:"af-detail-header-actions"}),l={name:"",description:"",avatar:"",tags:"",systemPrompt:"",model:"default",adapter:"claude-code",cwd:"",timeout:300,permissionMode:"bypassPermissions",effort:"",selectedSkills:new Set,selectedMcpServers:new Set,skillsBody:"",contextBody:"",approvalRequired:"",memory:!0,enabled:!0,allowedCommands:"",blockedCommands:"",heartbeatEnabled:!1,heartbeatSchedule:"0 */6 * * *",heartbeatBody:"",heartbeatNotify:!0,heartbeatChannel:"",autoCompactThreshold:85,wikiReferences:[]},d={none:{label:"None",prompt:""},coding:{label:"Coding Agent",prompt:`You are a coding agent. Review code, write tests, fix bugs, and implement features.
|
|
11850
12019
|
Follow existing code conventions. Write clean, well-tested code.
|
|
11851
12020
|
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.
|
|
11852
12021
|
Be concise and factual. Highlight anomalies clearly.
|
|
@@ -11854,13 +12023,13 @@ Include timestamps and relevant context in all reports.`},briefing:{label:"Brief
|
|
|
11854
12023
|
Prioritize recent and important changes. Keep summaries concise.
|
|
11855
12024
|
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.
|
|
11856
12025
|
Focus on correctness, security, and maintainability.
|
|
11857
|
-
Be specific \u2014 reference file names and line numbers.`}},h=s.createDiv({cls:"af-create-form"}),u=h.createDiv({cls:"af-create-section"}),p=u.createDiv({cls:"af-create-section-header"}),f=p.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(f,"user"),p.createSpan({text:"Identity"}),this.createFormField(u,"Name","deploy-watcher","Unique identifier (will be slugified)",U=>{c.name=U}),this.createFormField(u,"Description","Monitors deployments and reports status","",U=>{c.description=U});let m=u.createDiv({cls:"af-form-row"});m.createDiv({cls:"af-form-label",text:"Avatar"});let g=m.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"text",placeholder:"\u{1F6E1}\uFE0F"}});g.addEventListener("input",()=>{c.avatar=g.value}),this.createFormField(u,"Tags","devops, monitoring","Comma-separated",U=>{c.tags=U});let k=u.createDiv({cls:"af-form-row af-form-row-toggle"});k.createDiv({cls:"af-form-label",text:"Enabled"});let b=k.createDiv({cls:"af-agent-card-toggle on"});b.onclick=()=>{let U=b.hasClass("on");b.toggleClass("on",!U),c.enabled=!U};let v=h.createDiv({cls:"af-create-section"}),y=v.createDiv({cls:"af-create-section-header"}),x=y.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(x,"message-square"),y.createSpan({text:"System Prompt"});let T=v.createDiv({cls:"af-form-row"});T.createDiv({cls:"af-form-label",text:"Template"});let _=T.createEl("select",{cls:"af-form-select"});for(let[U,{label:Y}]of Object.entries(d))_.createEl("option",{text:Y,attr:{value:U}});let R=v.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"You are a deployment monitoring agent...",rows:"10"}});R.addEventListener("input",()=>{c.systemPrompt=R.value}),_.addEventListener("change",()=>{let U=d[_.value];U&&_.value!=="none"&&(c.systemPrompt=U.prompt,R.value=U.prompt)});let A=h.createDiv({cls:"af-create-section"}),S=A.createDiv({cls:"af-create-section-header"}),L=S.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(L,"settings"),S.createSpan({text:"Runtime Config"});let E=A.createDiv({cls:"af-create-config-grid"}),N=E.createDiv({cls:"af-form-row"});N.createDiv({cls:"af-form-label",text:"Adapter"});let I=N.createEl("select",{cls:"af-form-select"}),q=[["claude-code","Claude Code",!1],["codex","Codex (coming soon)",!0],["process","Process (coming soon)",!0],["http","HTTP (coming soon)",!0]];for(let[U,Y,Ue]of q){let ke=I.createEl("option",{text:Y,attr:{value:U,...Ue?{disabled:"true"}:{}}});U==="claude-code"&&(ke.selected=!0)}let F=E.createDiv({cls:"af-form-row"}),$=F.createDiv({cls:"af-form-label",text:"Model"});this.addTooltip($,`Aliases (opus/sonnet/haiku/opusplan) work on any backend. Choose Custom\u2026 for a pinned ID or Bedrock/Vertex/Foundry. Blank = use Settings default (${this.plugin.settings.defaultModel||"Claude CLI default"}).`);let W=F.createDiv({cls:"af-form-field-wrap"});_t(W,{value:c.model,onChange:U=>{c.model=U}}),I.addEventListener("change",()=>{c.adapter=I.value});let ie=E.createDiv({cls:"af-form-row"});ie.createDiv({cls:"af-form-label",text:"Working Dir"});let ue=ie.createEl("input",{cls:"af-form-input",attr:{type:"text",placeholder:"Leave empty for vault root"}});ue.addEventListener("input",()=>{c.cwd=ue.value});let ee=E.createDiv({cls:"af-form-row"});ee.createDiv({cls:"af-form-label",text:"Timeout (sec)"});let V=ee.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"number",value:"300"}});V.addEventListener("input",()=>{let U=parseInt(V.value,10);!isNaN(U)&&U>0&&(c.timeout=U)});let z=E.createDiv({cls:"af-form-row"});z.createDiv({cls:"af-form-label",text:"Permission Mode"});let Q=z.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[U,Y]of ce)Q.createEl("option",{text:Y,attr:{value:U}});Q.addEventListener("change",()=>{c.permissionMode=Q.value});let ne=E.createDiv({cls:"af-form-hint",text:"Skip all permission checks"});Q.addEventListener("change",()=>{let U=ce.find(([Y])=>Y===Q.value)?.[2]??"";ne.textContent=U});let de=E.createDiv({cls:"af-form-row"});de.createDiv({cls:"af-form-label",text:"Effort Level"});let Le=de.createEl("select",{cls:"af-form-select"});for(let[U,Y]of[["","Default"],["low","Low"],["medium","Medium"],["high","High"],["max","Max"]])Le.createEl("option",{text:Y,attr:{value:U}});Le.addEventListener("change",()=>{c.effort=Le.value}),E.createDiv({cls:"af-form-hint",text:"Controls reasoning depth \u2014 low is fast, max is most thorough"});let Ae=E.createDiv({cls:"af-form-row"}),Ne=Ae.createDiv({cls:"af-form-label",text:"Auto-compact at"});this.addTooltip(Ne,"Percent of context window at which the chat auto-invokes /compact before the next message. 85% is a good default. Set 0 to disable.");let je=Ae.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"number",min:"0",max:"100",value:String(c.autoCompactThreshold)}});je.addEventListener("input",()=>{let U=parseInt(je.value,10);!isNaN(U)&&U>=0&&U<=100&&(c.autoCompactThreshold=U)}),E.createDiv({cls:"af-form-hint",text:"0 disables auto-compact"});{let U=this.plugin.runtime.getSnapshot().agents.filter(Y=>Y.wikiKeeper!==void 0);if(U.length>0){let Y=E.createDiv({cls:"af-form-row af-form-row-toggle"}),Ue=Y.createDiv({cls:"af-form-label",text:"Wiki access"});this.addTooltip(Ue,"Lets this agent read + cite from the selected Wiki Keeper scopes (requires the wiki-query skill).");let ke=Y.createDiv({cls:"af-form-field-wrap"});for(let ge of U){let Fe=ke.createEl("label",{cls:"af-form-checkbox-row"}),pe=Fe.createEl("input",{attr:{type:"checkbox"}});Fe.createSpan({text:` ${ge.name}`,cls:"af-form-checkbox-label"}),pe.addEventListener("change",()=>{pe.checked?c.wikiReferences.includes(ge.name)||c.wikiReferences.push(ge.name):c.wikiReferences=c.wikiReferences.filter(Ge=>Ge!==ge.name)})}}}{let U=h.createDiv({cls:"af-create-section"}),Y=U.createDiv({cls:"af-create-section-header"}),Ue=Y.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(Ue,"heart-pulse");let ke=Y.createSpan({text:"Heartbeat"});this.addTooltip(ke,"Autonomous periodic run \u2014 what the agent does when no one is asking");let ge=U.createDiv({cls:"af-form-row af-form-row-toggle"});ge.createDiv({cls:"af-form-label",text:"Enabled"});let Fe=ge.createDiv({cls:"af-agent-card-toggle"}),pe=U.createDiv();pe.style.display="none",Fe.onclick=()=>{let et=Fe.hasClass("on");Fe.toggleClass("on",!et),c.heartbeatEnabled=!et,pe.style.display=et?"none":""},this.renderHeartbeatSchedule(pe,c);let Ge=pe.createDiv({cls:"af-form-row af-form-row-toggle"}),O=Ge.createDiv({cls:"af-form-label"});O.setText("Notify"),this.addTooltip(O,"Show an Obsidian notice when the heartbeat completes");let j=Ge.createDiv({cls:"af-agent-card-toggle on"});j.onclick=()=>{let et=j.hasClass("on");j.toggleClass("on",!et),c.heartbeatNotify=!et};let se=this.plugin.runtime.getSnapshot(),_e=pe.createDiv({cls:"af-form-row"}),Pe=_e.createDiv({cls:"af-form-label"});Pe.setText("Post to channel"),this.addTooltip(Pe,"Heartbeat results are posted to this Slack channel when the run completes");let Oe=_e.createEl("select",{cls:"af-form-select"});Oe.createEl("option",{text:"(none)",attr:{value:""}});for(let et of se.channels)Oe.createEl("option",{text:et.name,attr:{value:et.name}});Oe.addEventListener("change",()=>{c.heartbeatChannel=Oe.value});let Re=pe.createDiv({cls:"af-form-label"});Re.style.width="auto",Re.style.marginTop="12px",Re.setText("Instruction"),this.addTooltip(Re,'What the agent does on each heartbeat. Also used by the "Run Now" button.');let rt=pe.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Check status, scan for issues, report findings...",rows:"8"}});rt.addEventListener("input",()=>{c.heartbeatBody=rt.value})}let Ee=h.createDiv({cls:"af-create-section"}),Te=Ee.createDiv({cls:"af-create-section-header"}),De=Te.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(De,"puzzle"),Te.createSpan({text:"Skills"});let Be=this.plugin.runtime.getSnapshot();if(Be.skills.length>0){Ee.createDiv({cls:"af-form-sublabel",text:"Shared Skills"});let U=Ee.createDiv({cls:"af-create-skills-grid"});for(let Y of Be.skills){let Ue=U.createDiv({cls:"af-create-skill-item"}),ke=Ue.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});ke.addEventListener("change",()=>{ke.checked?c.selectedSkills.add(Y.name):c.selectedSkills.delete(Y.name)});let ge=Ue.createDiv({cls:"af-create-skill-label"});ge.createSpan({cls:"af-create-skill-name",text:Y.name}),Y.description&&ge.createSpan({cls:"af-create-skill-desc",text:` \u2014 ${Y.description}`})}}let He=Ee.createDiv({cls:"af-form-sublabel"});He.setText("Agent-specific skills"),this.addTooltip(He,"Custom skills/instructions only for this agent, not shared with others");let G=Ee.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Custom skills/instructions for this agent...",rows:"4"}});G.addEventListener("input",()=>{c.skillsBody=G.value});{let U=h.createDiv({cls:"af-create-section"}),Y=U.createDiv({cls:"af-create-section-header"}),Ue=Y.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(Ue,"plug");let ke=Y.createSpan({text:"MCP Servers"});this.addTooltip(ke,"Grant agent access to MCP servers");let ge=this.plugin.mcpManager.getCachedServers();if(ge===null){let Fe=U.createDiv({cls:"af-form-hint"});Fe.appendText("MCP servers not loaded. ");let pe=Fe.createEl("a",{cls:"af-link",text:"Go to MCP Servers tab to load them."});pe.onclick=Ge=>{Ge.preventDefault(),this.navigate("mcp")}}else if(ge.length===0)U.createDiv({cls:"af-form-hint",text:"No MCP servers found. Configure them with 'claude mcp add'."});else{let Fe=U.createDiv({cls:"af-create-skills-grid"});for(let pe of ge){let Ge=Fe.createDiv({cls:"af-mcp-agent-item"}),O=Ge.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});O.addEventListener("change",()=>{O.checked?c.selectedMcpServers.add(pe.name):c.selectedMcpServers.delete(pe.name)});let se=Ge.createDiv({cls:"af-mcp-agent-label"}).createDiv({cls:"af-mcp-agent-name-row"}),_e=se.createSpan({cls:`af-mcp-status-dot ${pe.enabled?pe.status:"disabled"}`});_e.title=pe.enabled?pe.status:"disabled",se.createSpan({cls:"af-create-skill-name",text:pe.name});let Pe=pe.toolDetails.length||pe.tools.length;Pe>0?se.createSpan({cls:"af-mcp-agent-tool-count",text:`${Pe} tools`}):pe.enabled?pe.status==="needs-auth"&&se.createSpan({cls:"af-mcp-agent-tool-count af-muted",text:"needs auth"}):se.createSpan({cls:"af-mcp-agent-tool-count af-muted",text:"disabled"})}}}let te=h.createDiv({cls:"af-create-section"}),H=te.createDiv({cls:"af-create-section-header"}),be=H.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(be,"file-text");let we=H.createSpan({text:"Context"});this.addTooltip(we,"Project-specific context included in every run");let fe=te.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Background info, repo structure, conventions...",rows:"4"}});fe.addEventListener("input",()=>{c.contextBody=fe.value});let Me=h.createDiv({cls:"af-create-section"}),J=Me.createDiv({cls:"af-create-section-header"}),P=J.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(P,"shield-check"),J.createSpan({text:"Permissions"}),this.createFormField(Me,"Approval required","git_push, file_delete","Comma-separated tool names",U=>{c.approvalRequired=U});let Z=Me.createDiv({cls:"af-form-row"});Z.createDiv({cls:"af-form-label",text:"Allowed Commands"});let re=Z.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:`Bash(curl *)
|
|
12026
|
+
Be specific \u2014 reference file names and line numbers.`}},h=s.createDiv({cls:"af-create-form"}),u=h.createDiv({cls:"af-create-section"}),p=u.createDiv({cls:"af-create-section-header"}),m=p.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(m,"user"),p.createSpan({text:"Identity"}),this.createFormField(u,"Name","deploy-watcher","Unique identifier (will be slugified)",N=>{l.name=N}),this.createFormField(u,"Description","Monitors deployments and reports status","",N=>{l.description=N});let f=u.createDiv({cls:"af-form-row"});f.createDiv({cls:"af-form-label",text:"Avatar"});let y=f.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"text",placeholder:"\u{1F6E1}\uFE0F"}});y.addEventListener("input",()=>{l.avatar=y.value}),this.createFormField(u,"Tags","devops, monitoring","Comma-separated",N=>{l.tags=N});let w=u.createDiv({cls:"af-form-row af-form-row-toggle"});w.createDiv({cls:"af-form-label",text:"Enabled"});let k=w.createDiv({cls:"af-agent-card-toggle on"});k.onclick=()=>{let N=k.hasClass("on");k.toggleClass("on",!N),l.enabled=!N};let g=h.createDiv({cls:"af-create-section"}),v=g.createDiv({cls:"af-create-section-header"}),S=v.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(S,"message-square"),v.createSpan({text:"System Prompt"});let T=g.createDiv({cls:"af-form-row"});T.createDiv({cls:"af-form-label",text:"Template"});let _=T.createEl("select",{cls:"af-form-select"});for(let[N,{label:V}]of Object.entries(d))_.createEl("option",{text:V,attr:{value:N}});let D=g.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"You are a deployment monitoring agent...",rows:"10"}});D.addEventListener("input",()=>{l.systemPrompt=D.value}),_.addEventListener("change",()=>{let N=d[_.value];N&&_.value!=="none"&&(l.systemPrompt=N.prompt,D.value=N.prompt)});let E=h.createDiv({cls:"af-create-section"}),C=E.createDiv({cls:"af-create-section-header"}),L=C.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(L,"settings"),C.createSpan({text:"Runtime Config"});let R=E.createDiv({cls:"af-create-config-grid"}),B=R.createDiv({cls:"af-form-row"});B.createDiv({cls:"af-form-label",text:"Adapter"});let F=B.createEl("select",{cls:"af-form-select"});for(let[N,V,Ie]of wo){let be=F.createEl("option",{text:V,attr:{value:N,...Ie?{disabled:"true"}:{}}});N==="claude-code"&&(be.selected=!0)}let Y=R.createDiv({cls:"af-form-row"}),U=Y.createDiv({cls:"af-form-label",text:"Model"});this.addTooltip(U,`Aliases (opus/sonnet/haiku/opusplan) work on any backend. Choose Custom\u2026 for a pinned ID or Bedrock/Vertex/Foundry. Blank = use Settings default (${this.plugin.settings.defaultModel||"CLI default"}).`);let W=Y.createDiv({cls:"af-form-field-wrap"}),ue=()=>{Ot(W,{value:l.model,adapter:l.adapter,onChange:N=>{l.model=N}})};ue();let ge=()=>{};F.addEventListener("change",()=>{l.adapter=F.value,(Os(l.adapter)?Zs:en).some(V=>V.value===l.model.trim())&&(l.model=""),ue(),l.permissionMode=$n(l.permissionMode,l.adapter),ge()});let pe=R.createDiv({cls:"af-form-row"});pe.createDiv({cls:"af-form-label",text:"Working Dir"});let Z=pe.createEl("input",{cls:"af-form-input",attr:{type:"text",placeholder:"Leave empty for vault root"}});Z.addEventListener("input",()=>{l.cwd=Z.value});let K=R.createDiv({cls:"af-form-row"});K.createDiv({cls:"af-form-label",text:"Timeout (sec)"});let H=K.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"number",value:"300"}});H.addEventListener("input",()=>{let N=parseInt(H.value,10);!isNaN(N)&&N>0&&(l.timeout=N)});let ne=R.createDiv({cls:"af-form-row"});ne.createDiv({cls:"af-form-label",text:"Permission Mode"});let ae=ne.createEl("select",{cls:"af-form-select"}),ce=R.createDiv({cls:"af-form-hint",text:""});ge=()=>{l.permissionMode=$n(l.permissionMode,l.adapter);let N=Un(l.adapter);ae.empty();for(let[V,Ie]of N){let be=ae.createEl("option",{text:Ie,attr:{value:V}});V===l.permissionMode&&(be.selected=!0)}ce.textContent=N.find(([V])=>V===ae.value)?.[2]??""},ge(),ae.addEventListener("change",()=>{l.permissionMode=ae.value,ce.textContent=Un(l.adapter).find(([N])=>N===ae.value)?.[2]??""});let re=R.createDiv({cls:"af-form-row"});re.createDiv({cls:"af-form-label",text:"Effort Level"});let Ee=re.createEl("select",{cls:"af-form-select"});for(let[N,V]of[["","Default"],["low","Low"],["medium","Medium"],["high","High"],["max","Max"]])Ee.createEl("option",{text:V,attr:{value:N}});Ee.addEventListener("change",()=>{l.effort=Ee.value}),R.createDiv({cls:"af-form-hint",text:"Controls reasoning depth \u2014 low is fast, max is most thorough"});let Ae=R.createDiv({cls:"af-form-row"}),Ne=Ae.createDiv({cls:"af-form-label",text:"Auto-compact at"});this.addTooltip(Ne,"Percent of context window at which the chat auto-invokes /compact before the next message. 85% is a good default. Set 0 to disable.");let Ue=Ae.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"number",min:"0",max:"100",value:String(l.autoCompactThreshold)}});Ue.addEventListener("input",()=>{let N=parseInt(Ue.value,10);!isNaN(N)&&N>=0&&N<=100&&(l.autoCompactThreshold=N)}),R.createDiv({cls:"af-form-hint",text:"0 disables auto-compact"});{let N=this.plugin.runtime.getSnapshot().agents.filter(V=>V.wikiKeeper!==void 0);if(N.length>0){let V=R.createDiv({cls:"af-form-row af-form-row-toggle"}),Ie=V.createDiv({cls:"af-form-label",text:"Wiki access"});this.addTooltip(Ie,"Lets this agent read + cite from the selected Wiki Keeper scopes (requires the wiki-query skill).");let be=V.createDiv({cls:"af-form-field-wrap"});for(let Se of N){let Fe=be.createEl("label",{cls:"af-form-checkbox-row"}),fe=Fe.createEl("input",{attr:{type:"checkbox"}});Fe.createSpan({text:` ${Se.name}`,cls:"af-form-checkbox-label"}),fe.addEventListener("change",()=>{fe.checked?l.wikiReferences.includes(Se.name)||l.wikiReferences.push(Se.name):l.wikiReferences=l.wikiReferences.filter(Ge=>Ge!==Se.name)})}}}{let N=h.createDiv({cls:"af-create-section"}),V=N.createDiv({cls:"af-create-section-header"}),Ie=V.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(Ie,"heart-pulse");let be=V.createSpan({text:"Heartbeat"});this.addTooltip(be,"Autonomous periodic run \u2014 what the agent does when no one is asking");let Se=N.createDiv({cls:"af-form-row af-form-row-toggle"});Se.createDiv({cls:"af-form-label",text:"Enabled"});let Fe=Se.createDiv({cls:"af-agent-card-toggle"}),fe=N.createDiv();fe.style.display="none",Fe.onclick=()=>{let ee=Fe.hasClass("on");Fe.toggleClass("on",!ee),l.heartbeatEnabled=!ee,fe.style.display=ee?"none":""},this.renderHeartbeatSchedule(fe,l);let Ge=fe.createDiv({cls:"af-form-row af-form-row-toggle"}),St=Ge.createDiv({cls:"af-form-label"});St.setText("Notify"),this.addTooltip(St,"Show an Obsidian notice when the heartbeat completes");let Ct=Ge.createDiv({cls:"af-agent-card-toggle on"});Ct.onclick=()=>{let ee=Ct.hasClass("on");Ct.toggleClass("on",!ee),l.heartbeatNotify=!ee};let nt=this.plugin.runtime.getSnapshot(),Tt=fe.createDiv({cls:"af-form-row"}),It=Tt.createDiv({cls:"af-form-label"});It.setText("Post to channel"),this.addTooltip(It,"Heartbeat results are posted to this Slack channel when the run completes");let _t=Tt.createEl("select",{cls:"af-form-select"});_t.createEl("option",{text:"(none)",attr:{value:""}});for(let ee of nt.channels)_t.createEl("option",{text:ee.name,attr:{value:ee.name}});_t.addEventListener("change",()=>{l.heartbeatChannel=_t.value});let M=fe.createDiv({cls:"af-form-label"});M.style.width="auto",M.style.marginTop="12px",M.setText("Instruction"),this.addTooltip(M,'What the agent does on each heartbeat. Also used by the "Run Now" button.');let $=fe.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Check status, scan for issues, report findings...",rows:"8"}});$.addEventListener("input",()=>{l.heartbeatBody=$.value})}let Pe=h.createDiv({cls:"af-create-section"}),Te=Pe.createDiv({cls:"af-create-section-header"}),Re=Te.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(Re,"puzzle"),Te.createSpan({text:"Skills"});let Be=this.plugin.runtime.getSnapshot();if(Be.skills.length>0){Pe.createDiv({cls:"af-form-sublabel",text:"Shared Skills"});let N=Pe.createDiv({cls:"af-create-skills-grid"});for(let V of Be.skills){let Ie=N.createDiv({cls:"af-create-skill-item"}),be=Ie.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});be.addEventListener("change",()=>{be.checked?l.selectedSkills.add(V.name):l.selectedSkills.delete(V.name)});let Se=Ie.createDiv({cls:"af-create-skill-label"});Se.createSpan({cls:"af-create-skill-name",text:V.name}),V.description&&Se.createSpan({cls:"af-create-skill-desc",text:` \u2014 ${V.description}`})}}let $e=Pe.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 q=Pe.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Custom skills/instructions for this agent...",rows:"4"}});q.addEventListener("input",()=>{l.skillsBody=q.value});{let N=h.createDiv({cls:"af-create-section"}),V=N.createDiv({cls:"af-create-section-header"}),Ie=V.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(Ie,"plug");let be=V.createSpan({text:"MCP Servers"});this.addTooltip(be,"Grant agent access to MCP servers"),N.createDiv({cls:"af-form-hint",text:"Codex agents read servers from Codex's own config (`codex mcp add`); selections here are matched to those servers by name."});let Se=this.plugin.mcpManager.getCachedServers();if(Se===null){let Fe=N.createDiv({cls:"af-form-hint"});Fe.appendText("MCP servers not loaded. ");let fe=Fe.createEl("a",{cls:"af-link",text:"Go to MCP Servers tab to load them."});fe.onclick=Ge=>{Ge.preventDefault(),this.navigate("mcp")}}else if(Se.length===0)N.createDiv({cls:"af-form-hint",text:"No MCP servers found. Configure them with 'claude mcp add'."});else{let Fe=N.createDiv({cls:"af-create-skills-grid"});for(let fe of Se){let Ge=Fe.createDiv({cls:"af-mcp-agent-item"}),St=Ge.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});St.addEventListener("change",()=>{St.checked?l.selectedMcpServers.add(fe.name):l.selectedMcpServers.delete(fe.name)});let nt=Ge.createDiv({cls:"af-mcp-agent-label"}).createDiv({cls:"af-mcp-agent-name-row"}),Tt=nt.createSpan({cls:`af-mcp-status-dot ${fe.enabled?fe.status:"disabled"}`});Tt.title=fe.enabled?fe.status:"disabled",nt.createSpan({cls:"af-create-skill-name",text:fe.name});let It=fe.toolDetails.length||fe.tools.length;It>0?nt.createSpan({cls:"af-mcp-agent-tool-count",text:`${It} tools`}):fe.enabled?fe.status==="needs-auth"&&nt.createSpan({cls:"af-mcp-agent-tool-count af-muted",text:"needs auth"}):nt.createSpan({cls:"af-mcp-agent-tool-count af-muted",text:"disabled"})}}}let te=h.createDiv({cls:"af-create-section"}),j=te.createDiv({cls:"af-create-section-header"}),we=j.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(we,"file-text");let xe=j.createSpan({text:"Context"});this.addTooltip(xe,"Project-specific context included in every run");let ve=te.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Background info, repo structure, conventions...",rows:"4"}});ve.addEventListener("input",()=>{l.contextBody=ve.value});let De=h.createDiv({cls:"af-create-section"}),J=De.createDiv({cls:"af-create-section-header"}),P=J.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(P,"shield-check"),J.createSpan({text:"Permissions"}),this.createFormField(De,"Approval required","git_push, file_delete","Comma-separated tool names",N=>{l.approvalRequired=N});let X=De.createDiv({cls:"af-form-row"});X.createDiv({cls:"af-form-label",text:"Allowed Commands"});let de=X.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:`Bash(curl *)
|
|
11858
12027
|
Bash(python3 *)
|
|
11859
12028
|
Read
|
|
11860
12029
|
Edit
|
|
11861
|
-
Write`,rows:"4"}});
|
|
12030
|
+
Write`,rows:"4"}});de.addEventListener("input",()=>{l.allowedCommands=de.value});let me=De.createDiv({cls:"af-form-row"});me.createDiv({cls:"af-form-label",text:"Blocked Commands"});let tt=me.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:`Bash(git push *)
|
|
11862
12031
|
Bash(rm -rf *)
|
|
11863
|
-
Bash(sudo *)`,rows:"4"}});
|
|
12032
|
+
Bash(sudo *)`,rows:"4"}});tt.addEventListener("input",()=>{l.blockedCommands=tt.value}),De.createDiv({cls:"af-form-hint",text:"On Codex agents these become execpolicy command rules \u2014 only Bash(cmd args *) prefixes are enforced; tool-name rules (Read/Write) and mid-pattern wildcards are ignored, and file/network access is governed by Permission Mode (the sandbox)."});let st=De.createDiv({cls:"af-form-row"});st.createDiv({cls:"af-form-label",text:"Memory enabled"});let Gt=st.createDiv({cls:"af-agent-card-toggle on"});Gt.onclick=()=>{let N=Gt.hasClass("on");Gt.toggleClass("on",!N),l.memory=!N};let Bs=s.createDiv({cls:"af-create-footer"}),Us=Bs.createEl("button",{cls:"af-btn-sm",text:"Cancel"});Us.onclick=()=>this.navigate("agents");let Dt=Bs.createEl("button",{cls:"af-btn-sm primary af-create-submit"});A(Dt,"plus","af-btn-icon"),Dt.appendText(" Create Agent"),Dt.onclick=async()=>{let N=l.name.trim();if(!N){new b.Notice("Agent name is required.");return}let V=se(N);if(this.plugin.repository.getAgentByName(V)){new b.Notice(`Agent "${V}" already exists.`);return}let Ie=be=>be.split(",").map(Se=>Se.trim()).filter(Boolean);try{let be=Se=>oe(Se).map(Fe=>Fe.trim()).filter(Boolean);await this.plugin.repository.createAgentFolder({name:V,description:l.description.trim(),avatar:l.avatar.trim(),tags:Ie(l.tags),systemPrompt:l.systemPrompt.trim(),model:l.model.trim()||"default",adapter:l.adapter,cwd:l.cwd.trim(),timeout:l.timeout,permissionMode:l.permissionMode,effort:l.effort||void 0,approvalRequired:Ie(l.approvalRequired),memory:l.memory,memoryMaxEntries:100,skills:Array.from(l.selectedSkills),mcpServers:Array.from(l.selectedMcpServers),skillsBody:l.skillsBody.trim(),contextBody:l.contextBody.trim(),enabled:l.enabled,permissionRules:{allow:be(l.allowedCommands),deny:be(l.blockedCommands)},autoCompactThreshold:l.autoCompactThreshold,wikiReferences:l.wikiReferences}),l.heartbeatEnabled&&l.heartbeatBody.trim()&&await this.plugin.repository.updateHeartbeat(V,{enabled:l.heartbeatEnabled,schedule:l.heartbeatSchedule.trim(),notify:l.heartbeatNotify,channel:l.heartbeatChannel,body:l.heartbeatBody.trim()}),new b.Notice(`Agent "${V}" created.`),await this.plugin.refreshFromVault(),this.navigate("agent-detail",V)}catch(be){let Se=be instanceof Error?be.message:String(be);new b.Notice(`Failed to create agent: ${Se}`)}}}renderCreateSkillPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),n=s.createDiv({cls:"af-detail-header"}),a=n.createDiv({cls:"af-detail-header-left"}),r=a.createDiv({cls:"af-agent-card-avatar idle"});(0,b.setIcon)(r,"plus");let o=a.createDiv();o.createDiv({cls:"af-detail-header-name",text:"Create New Skill"}),o.createDiv({cls:"af-detail-header-desc",text:"Define a reusable skill for your agents"});let c=n.createDiv({cls:"af-detail-header-actions"}),l={name:"",description:"",tags:"",body:"",toolsBody:"",referencesBody:"",examplesBody:""},d={none:{label:"None",prompt:""},cli:{label:"CLI Tool Wrapper",prompt:`You are using the {{tool}} CLI. All operations go through the wrapper script.
|
|
11864
12033
|
|
|
11865
12034
|
Requirements:
|
|
11866
12035
|
- Ensure required environment variables are set
|
|
@@ -11897,47 +12066,49 @@ Key behaviors:
|
|
|
11897
12066
|
- Use tables and charts where appropriate
|
|
11898
12067
|
- Always state the time range and filters applied
|
|
11899
12068
|
- Flag anomalies and outliers explicitly
|
|
11900
|
-
- End with actionable insights, not just observations`}},h=s.createDiv({cls:"af-create-form"}),u=h.createDiv({cls:"af-create-section"}),p=u.createDiv({cls:"af-create-section-header"}),
|
|
12069
|
+
- End with actionable insights, not just observations`}},h=s.createDiv({cls:"af-create-form"}),u=h.createDiv({cls:"af-create-section"}),p=u.createDiv({cls:"af-create-section-header"}),m=p.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(m,"puzzle"),p.createSpan({text:"Identity"}),this.createFormField(u,"Name","todoist","Unique identifier (will be slugified)",H=>{l.name=H}),this.createFormField(u,"Description","Manage tasks and projects via CLI","",H=>{l.description=H}),this.createFormField(u,"Tags","productivity, tasks","Comma-separated",H=>{l.tags=H});let f=h.createDiv({cls:"af-create-section"}),y=f.createDiv({cls:"af-create-section-header"}),w=y.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(w,"file-text"),y.createSpan({text:"Core Instructions"});let k=f.createDiv({cls:"af-form-row"});k.createDiv({cls:"af-form-label",text:"Template"});let g=k.createEl("select",{cls:"af-form-select"});for(let[H,{label:ne}]of Object.entries(d))g.createEl("option",{text:ne,attr:{value:H}});let v=f.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Skill instructions \u2014 what does this skill do and how should agents use it?",rows:"10"}});v.addEventListener("input",()=>{l.body=v.value}),g.addEventListener("change",()=>{let H=d[g.value];H&&g.value!=="none"&&(l.body=H.prompt,v.value=H.prompt)});let S=h.createDiv({cls:"af-create-section"}),T=S.createDiv({cls:"af-create-section-header"}),_=T.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(_,"wrench");let D=T.createSpan({text:"Tools"});this.addTooltip(D,"CLI commands, API endpoints, and tool definitions available to agents using this skill");let E=S.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`## Commands
|
|
11901
12070
|
|
|
11902
12071
|
### list
|
|
11903
12072
|
Usage: tool list [--filter <query>]
|
|
11904
|
-
...`,rows:"8"}});
|
|
12073
|
+
...`,rows:"8"}});E.addEventListener("input",()=>{l.toolsBody=E.value});let C=h.createDiv({cls:"af-create-section"}),L=C.createDiv({cls:"af-create-section-header"}),R=L.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(R,"book-open");let B=L.createSpan({text:"References"});this.addTooltip(B,"Background docs, conventions, cheat sheets");let F=C.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"API docs, filter syntax, conventions...",rows:"6"}});F.addEventListener("input",()=>{l.referencesBody=F.value});let Y=h.createDiv({cls:"af-create-section"}),U=Y.createDiv({cls:"af-create-section-header"}),W=U.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(W,"message-circle");let ue=U.createSpan({text:"Examples"});this.addTooltip(ue,"Example prompts and ideal outputs showing how to use this skill");let ge=Y.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`## Example: List all tasks
|
|
11905
12074
|
|
|
11906
12075
|
User: Show me my tasks for today
|
|
11907
12076
|
|
|
11908
|
-
Agent: ...`,rows:"6"}});
|
|
11909
|
-
`),blockedCommands:
|
|
11910
|
-
`),heartbeatEnabled:n.heartbeatEnabled,heartbeatSchedule:n.heartbeatSchedule,heartbeatBody:n.heartbeatBody,heartbeatNotify:n.heartbeatNotify,heartbeatChannel:n.heartbeatChannel,autoCompactThreshold:n.autoCompactThreshold??85,wikiReferences:(n.wikiReferences??[]).map(O=>O.agent)},u=s.createDiv({cls:"af-create-form"}),p=u.createDiv({cls:"af-create-section"}),f=p.createDiv({cls:"af-create-section-header"}),m=f.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(m,"user"),f.createSpan({text:"Identity"});let g=p.createDiv({cls:"af-form-row"});g.createDiv({cls:"af-form-label",text:"Name"});let k=g.createEl("input",{cls:"af-form-input",attr:{type:"text",value:n.name,disabled:"true"}});k.style.opacity="0.6",this.createFormField(p,"Description","Monitors deployments and reports status","",O=>{h.description=O},n.description??"");let b=p.createDiv({cls:"af-form-row"});b.createDiv({cls:"af-form-label",text:"Avatar"});let v=b.createEl("button",{cls:"af-avatar-picker-btn"}),y=v.createDiv({cls:"af-avatar-picker-preview"});this.renderAgentAvatar(y,{...n,avatar:h.avatar??n.avatar}),v.createSpan({cls:"af-avatar-picker-label",text:h.avatar||n.avatar||"Pick icon\u2026"}),v.addEventListener("click",()=>{new na(this.app,h.avatar??n.avatar,O=>{h.avatar=O,y.empty(),(0,w.setIcon)(y,O),v.querySelector(".af-avatar-picker-label")?.setText(O)}).open()}),this.createFormField(p,"Tags","devops, monitoring","Comma-separated",O=>{h.tags=O},n.tags.join(", "));let x=p.createDiv({cls:"af-form-row"});x.createDiv({cls:"af-form-label",text:"Enabled"});let T=x.createDiv({cls:`af-agent-card-toggle${n.enabled?" on":""}`});T.onclick=()=>{let O=T.hasClass("on");T.toggleClass("on",!O),h.enabled=!O};let _=u.createDiv({cls:"af-create-section"}),R=_.createDiv({cls:"af-create-section-header"}),A=R.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(A,"message-square"),R.createSpan({text:"System Prompt"});let S=_.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"System prompt...",rows:"10"}});S.value=n.body,S.addEventListener("input",()=>{h.systemPrompt=S.value});let L=u.createDiv({cls:"af-create-section"}),E=L.createDiv({cls:"af-create-section-header"}),N=E.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(N,"settings"),E.createSpan({text:"Runtime Config"});let I=L.createDiv({cls:"af-create-config-grid"}),q=I.createDiv({cls:"af-form-row"});q.createDiv({cls:"af-form-label",text:"Adapter"});let F=q.createEl("select",{cls:"af-form-select"}),$=[["claude-code","Claude Code",!1],["codex","Codex (coming soon)",!0],["process","Process (coming soon)",!0],["http","HTTP (coming soon)",!0]];for(let[O,j,se]of $){let _e=F.createEl("option",{text:j,attr:{value:O,...se?{disabled:"true"}:{}}});O===n.adapter&&(_e.selected=!0)}let W=I.createDiv({cls:"af-form-row"}),ie=W.createDiv({cls:"af-form-label",text:"Model"});this.addTooltip(ie,`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 ue=W.createDiv({cls:"af-form-field-wrap"});_t(ue,{value:h.model,onChange:O=>{h.model=O}}),F.addEventListener("change",()=>{h.adapter=F.value});let ee=I.createDiv({cls:"af-form-row"});ee.createDiv({cls:"af-form-label",text:"Working Dir"});let V=ee.createEl("input",{cls:"af-form-input",attr:{type:"text",placeholder:"Leave empty for vault root",value:n.cwd??""}});V.addEventListener("input",()=>{h.cwd=V.value});let z=I.createDiv({cls:"af-form-row"});z.createDiv({cls:"af-form-label",text:"Timeout (sec)"});let Q=z.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"number",value:String(n.timeout)}});Q.addEventListener("input",()=>{let O=parseInt(Q.value,10);!isNaN(O)&&O>0&&(h.timeout=O)});let ce=I.createDiv({cls:"af-form-row"});ce.createDiv({cls:"af-form-label",text:"Permission Mode"});let ne=ce.createEl("select",{cls:"af-form-select"}),de=[["bypassPermissions","Bypass Permissions","Auto-approve everything except deny list"],["dontAsk","Don\u2019t Ask","Auto-approve all tool calls"],["acceptEdits","Accept Edits","Auto-approve file edits, block bash unless allowed"],["plan","Plan","Read-only mode, no writes or commands"],["default","Default","Ask for each tool call"]];for(let[O,j]of de){let se=ne.createEl("option",{text:j,attr:{value:O}});O===n.permissionMode&&(se.selected=!0)}ne.addEventListener("change",()=>{h.permissionMode=ne.value});let Le=I.createDiv({cls:"af-form-hint",text:de.find(([O])=>O===n.permissionMode)?.[2]??""});ne.addEventListener("change",()=>{let O=de.find(([j])=>j===ne.value)?.[2]??"";Le.textContent=O});let Ae=I.createDiv({cls:"af-form-row"});Ae.createDiv({cls:"af-form-label",text:"Effort Level"});let Ne=Ae.createEl("select",{cls:"af-form-select"});for(let[O,j]of[["","Default"],["low","Low"],["medium","Medium"],["high","High"],["max","Max"]]){let se=Ne.createEl("option",{text:j,attr:{value:O}});O===(n.effort??"")&&(se.selected=!0)}Ne.addEventListener("change",()=>{h.effort=Ne.value}),I.createDiv({cls:"af-form-hint",text:"Controls reasoning depth \u2014 low is fast, max is most thorough"});let je=I.createDiv({cls:"af-form-row"}),Ee=je.createDiv({cls:"af-form-label",text:"Auto-compact at"});this.addTooltip(Ee,"Percent of context window at which the chat auto-invokes /compact before the next message. 85% is a good default. Set 0 to disable.");let Te=je.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"number",min:"0",max:"100",value:String(h.autoCompactThreshold)}});Te.addEventListener("input",()=>{let O=parseInt(Te.value,10);!isNaN(O)&&O>=0&&O<=100&&(h.autoCompactThreshold=O)}),I.createDiv({cls:"af-form-hint",text:"0 disables auto-compact"});{let O=this.plugin.runtime.getSnapshot().agents.filter(j=>j.wikiKeeper!==void 0);if(O.length>0){let j=I.createDiv({cls:"af-form-row af-form-row-toggle"}),se=j.createDiv({cls:"af-form-label",text:"Wiki access"});this.addTooltip(se,"Lets this agent read + cite from the selected Wiki Keeper scopes (requires the wiki-query skill).");let _e=j.createDiv({cls:"af-form-field-wrap"});for(let Pe of O){let Oe=_e.createEl("label",{cls:"af-form-checkbox-row"}),Re=Oe.createEl("input",{attr:{type:"checkbox"}});h.wikiReferences.includes(Pe.name)&&(Re.checked=!0),Oe.createSpan({text:` ${Pe.name}`,cls:"af-form-checkbox-label"}),Re.addEventListener("change",()=>{Re.checked?h.wikiReferences.includes(Pe.name)||h.wikiReferences.push(Pe.name):h.wikiReferences=h.wikiReferences.filter(rt=>rt!==Pe.name)})}}}if(n.isFolder){let O=u.createDiv({cls:"af-create-section"}),j=O.createDiv({cls:"af-create-section-header"}),se=j.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(se,"heart-pulse");let _e=j.createSpan({text:"Heartbeat"});this.addTooltip(_e,"Autonomous periodic run \u2014 what the agent does when no one is asking");let Pe=O.createDiv({cls:"af-form-row af-form-row-toggle"});Pe.createDiv({cls:"af-form-label",text:"Enabled"});let Oe=Pe.createDiv({cls:`af-agent-card-toggle${h.heartbeatEnabled?" on":""}`}),Re=O.createDiv();Re.style.display=h.heartbeatEnabled?"":"none",Oe.onclick=()=>{let ot=Oe.hasClass("on");Oe.toggleClass("on",!ot),h.heartbeatEnabled=!ot,Re.style.display=ot?"none":""},this.renderHeartbeatSchedule(Re,h);let rt=Re.createDiv({cls:"af-form-row af-form-row-toggle"}),et=rt.createDiv({cls:"af-form-label"});et.setText("Notify"),this.addTooltip(et,"Show an Obsidian notice when the heartbeat completes");let oa=rt.createDiv({cls:`af-agent-card-toggle${h.heartbeatNotify?" on":""}`});oa.onclick=()=>{let ot=oa.hasClass("on");oa.toggleClass("on",!ot),h.heartbeatNotify=!ot};let Ki=this.plugin.runtime.getSnapshot(),Ka=Re.createDiv({cls:"af-form-row"}),Ja=Ka.createDiv({cls:"af-form-label"});Ja.setText("Post to channel"),this.addTooltip(Ja,"Heartbeat results are posted to this Slack channel when the run completes");let ys=Ka.createEl("select",{cls:"af-form-select"});ys.createEl("option",{text:"(none)",attr:{value:""}});for(let ot of Ki.channels){let Ji=ys.createEl("option",{text:ot.name,attr:{value:ot.name}});ot.name===h.heartbeatChannel&&(Ji.selected=!0)}ys.addEventListener("change",()=>{h.heartbeatChannel=ys.value});let vs=Re.createDiv({cls:"af-form-label"});vs.style.width="auto",vs.style.marginTop="12px",vs.setText("Instruction"),this.addTooltip(vs,'What the agent does on each heartbeat. Also used by the "Run Now" button.');let la=Re.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Check status, scan for issues, report findings...",rows:"8"}});la.value=h.heartbeatBody,la.addEventListener("input",()=>{h.heartbeatBody=la.value})}let De=u.createDiv({cls:"af-create-section"}),Be=De.createDiv({cls:"af-create-section-header"}),He=Be.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(He,"puzzle"),Be.createSpan({text:"Skills"});let G=this.plugin.runtime.getSnapshot();if(G.skills.length>0){De.createDiv({cls:"af-form-sublabel",text:"Shared Skills"});let O=De.createDiv({cls:"af-create-skills-grid"});for(let j of G.skills){let se=O.createDiv({cls:"af-create-skill-item"}),_e=se.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});_e.checked=h.selectedSkills.has(j.name),_e.addEventListener("change",()=>{_e.checked?h.selectedSkills.add(j.name):h.selectedSkills.delete(j.name)});let Pe=se.createDiv({cls:"af-create-skill-label"});Pe.createSpan({cls:"af-create-skill-name",text:j.name}),j.description&&Pe.createSpan({cls:"af-create-skill-desc",text:` \u2014 ${j.description}`})}}let te=De.createDiv({cls:"af-form-sublabel"});te.setText("Agent-specific skills"),this.addTooltip(te,"Custom skills/instructions only for this agent, not shared with others");let H=De.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Custom skills/instructions for this agent...",rows:"4"}});H.value=n.skillsBody,H.addEventListener("input",()=>{h.skillsBody=H.value});let be=u.createDiv({cls:"af-create-section"}),we=be.createDiv({cls:"af-create-section-header"}),fe=we.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(fe,"plug");let Me=we.createSpan({text:"MCP Servers"});this.addTooltip(Me,"Grant agent access to MCP servers");let J=this.plugin.mcpManager.getCachedServers();if(J===null){let O=be.createDiv({cls:"af-form-hint"});O.appendText("MCP servers not loaded. ");let j=O.createEl("a",{cls:"af-link",text:"Go to MCP Servers tab to load them."});j.onclick=se=>{se.preventDefault(),this.navigate("mcp")}}else if(J.length===0)be.createDiv({cls:"af-form-hint",text:"No MCP servers found. Configure them with 'claude mcp add'."});else{let O=be.createDiv({cls:"af-create-skills-grid"});for(let j of J){let se=O.createDiv({cls:"af-mcp-agent-item"}),_e=se.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});_e.checked=h.selectedMcpServers.has(j.name),_e.addEventListener("change",()=>{_e.checked?h.selectedMcpServers.add(j.name):h.selectedMcpServers.delete(j.name)});let Oe=se.createDiv({cls:"af-mcp-agent-label"}).createDiv({cls:"af-mcp-agent-name-row"}),Re=Oe.createSpan({cls:`af-mcp-status-dot ${j.enabled?j.status:"disabled"}`});Re.title=j.enabled?j.status:"disabled",Oe.createSpan({cls:"af-create-skill-name",text:j.name});let rt=j.toolDetails.length||j.tools.length;rt>0?Oe.createSpan({cls:"af-mcp-agent-tool-count",text:`${rt} tools`}):j.enabled?j.status==="needs-auth"&&Oe.createSpan({cls:"af-mcp-agent-tool-count af-muted",text:"needs auth"}):Oe.createSpan({cls:"af-mcp-agent-tool-count af-muted",text:"disabled"})}}let P=u.createDiv({cls:"af-create-section"}),Z=P.createDiv({cls:"af-create-section-header"}),re=Z.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(re,"file-text");let he=Z.createSpan({text:"Context"});this.addTooltip(he,"Project-specific context included in every run");let Ze=P.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Background info, repo structure, conventions...",rows:"4"}});Ze.value=n.contextBody,Ze.addEventListener("input",()=>{h.contextBody=Ze.value});let vt=u.createDiv({cls:"af-create-section"}),Mt=vt.createDiv({cls:"af-create-section-header"}),fs=Mt.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(fs,"shield-check"),Mt.createSpan({text:"Permissions"}),this.createFormField(vt,"Approval required","git_push, file_delete","Comma-separated tool names",O=>{h.approvalRequired=O},n.approvalRequired.join(", "));let gs=vt.createDiv({cls:"af-form-row"});gs.createDiv({cls:"af-form-label",text:"Allowed Commands"});let xt=gs.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:`Bash(curl *)
|
|
12077
|
+
Agent: ...`,rows:"6"}});ge.addEventListener("input",()=>{l.examplesBody=ge.value});let pe=s.createDiv({cls:"af-create-footer"}),Z=pe.createEl("button",{cls:"af-btn-sm",text:"Cancel"});Z.onclick=()=>this.navigate("skills");let K=pe.createEl("button",{cls:"af-btn-sm primary af-create-submit"});A(K,"plus","af-btn-icon"),K.appendText(" Create Skill"),K.onclick=async()=>{let H=l.name.trim();if(!H){new b.Notice("Skill name is required.");return}let ne=se(H);if(this.plugin.repository.getSkillByName(ne)){new b.Notice(`Skill "${ne}" already exists.`);return}let ae=ce=>ce.split(",").map(re=>re.trim()).filter(Boolean);try{await this.plugin.repository.createSkillFolder({name:ne,description:l.description.trim(),tags:ae(l.tags),body:l.body.trim(),toolsBody:l.toolsBody.trim(),referencesBody:l.referencesBody.trim(),examplesBody:l.examplesBody.trim()}),new b.Notice(`Skill "${ne}" created.`),await this.plugin.refreshFromVault(),this.navigate("skills")}catch(ce){let re=ce instanceof Error?ce.message:String(ce);new b.Notice(`Failed to create skill: ${re}`)}}}renderEditAgentPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),n=this.detailContext;if(!n){this.renderEmptyState(s,"bot","No agent selected","");return}let a=this.plugin.runtime.getSnapshot().agents.find(M=>M.name===n);if(!a){this.renderEmptyState(s,"bot","Agent not found",`Agent "${n}" was not found`);return}let r=s.createDiv({cls:"af-detail-header"}),o=r.createDiv({cls:"af-detail-header-left"}),c=o.createDiv({cls:"af-agent-card-avatar idle"});(0,b.setIcon)(c,"edit");let l=o.createDiv();l.createDiv({cls:"af-detail-header-name",text:`Edit Agent: ${a.name}`}),l.createDiv({cls:"af-detail-header-desc",text:"Modify agent configuration"});let d=r.createDiv({cls:"af-detail-header-actions"}),h={name:a.name,description:a.description??"",avatar:a.avatar,tags:a.tags.join(", "),systemPrompt:a.body,model:a.model,adapter:a.adapter,cwd:a.cwd??"",timeout:a.timeout,permissionMode:a.permissionMode,effort:a.effort??"",selectedSkills:new Set(a.skills),selectedMcpServers:new Set(a.mcpServers??[]),skillsBody:a.skillsBody,contextBody:a.contextBody,approvalRequired:a.approvalRequired.join(", "),memory:a.memory,memoryTokenBudget:a.memoryTokenBudget,reflectionEnabled:a.reflection.enabled,reflectionProposeSkills:a.reflection.proposeSkills,enabled:a.enabled,allowedCommands:a.permissionRules.allow.join(`
|
|
12078
|
+
`),blockedCommands:a.permissionRules.deny.join(`
|
|
12079
|
+
`),heartbeatEnabled:a.heartbeatEnabled,heartbeatSchedule:a.heartbeatSchedule,heartbeatBody:a.heartbeatBody,heartbeatNotify:a.heartbeatNotify,heartbeatChannel:a.heartbeatChannel,autoCompactThreshold:a.autoCompactThreshold??85,wikiReferences:(a.wikiReferences??[]).map(M=>M.agent)},u=s.createDiv({cls:"af-create-form"}),p=u.createDiv({cls:"af-create-section"}),m=p.createDiv({cls:"af-create-section-header"}),f=m.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(f,"user"),m.createSpan({text:"Identity"});let y=p.createDiv({cls:"af-form-row"});y.createDiv({cls:"af-form-label",text:"Name"});let w=y.createEl("input",{cls:"af-form-input",attr:{type:"text",value:a.name,disabled:"true"}});w.style.opacity="0.6",this.createFormField(p,"Description","Monitors deployments and reports status","",M=>{h.description=M},a.description??"");let k=p.createDiv({cls:"af-form-row"});k.createDiv({cls:"af-form-label",text:"Avatar"});let g=k.createEl("button",{cls:"af-avatar-picker-btn"}),v=g.createDiv({cls:"af-avatar-picker-preview"});this.renderAgentAvatar(v,{...a,avatar:h.avatar??a.avatar}),g.createSpan({cls:"af-avatar-picker-label",text:h.avatar||a.avatar||"Pick icon\u2026"}),g.addEventListener("click",()=>{new Nn(this.app,h.avatar??a.avatar,M=>{h.avatar=M,v.empty(),(0,b.setIcon)(v,M),g.querySelector(".af-avatar-picker-label")?.setText(M)}).open()}),this.createFormField(p,"Tags","devops, monitoring","Comma-separated",M=>{h.tags=M},a.tags.join(", "));let S=p.createDiv({cls:"af-form-row"});S.createDiv({cls:"af-form-label",text:"Enabled"});let T=S.createDiv({cls:`af-agent-card-toggle${a.enabled?" on":""}`});T.onclick=()=>{let M=T.hasClass("on");T.toggleClass("on",!M),h.enabled=!M};let _=u.createDiv({cls:"af-create-section"}),D=_.createDiv({cls:"af-create-section-header"}),E=D.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(E,"message-square"),D.createSpan({text:"System Prompt"});let C=_.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"System prompt...",rows:"10"}});C.value=a.body,C.addEventListener("input",()=>{h.systemPrompt=C.value});let L=u.createDiv({cls:"af-create-section"}),R=L.createDiv({cls:"af-create-section-header"}),B=R.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(B,"settings"),R.createSpan({text:"Runtime Config"});let F=L.createDiv({cls:"af-create-config-grid"}),Y=F.createDiv({cls:"af-form-row"});Y.createDiv({cls:"af-form-label",text:"Adapter"});let U=Y.createEl("select",{cls:"af-form-select"});for(let[M,$,ee]of wo){let Oe=U.createEl("option",{text:$,attr:{value:M,...ee?{disabled:"true"}:{}}});(M===a.adapter||Os(a.adapter)&&M==="codex")&&(Oe.selected=!0)}let W=F.createDiv({cls:"af-form-row"}),ue=W.createDiv({cls:"af-form-label",text:"Model"});this.addTooltip(ue,`Aliases (opus/sonnet/haiku/opusplan) work on any backend. Choose Custom\u2026 for a pinned ID or Bedrock/Vertex/Foundry. Blank = use Settings default (${this.plugin.settings.defaultModel||"CLI default"}).`);let ge=W.createDiv({cls:"af-form-field-wrap"}),pe=()=>{Ot(ge,{value:h.model,adapter:h.adapter,onChange:M=>{h.model=M}})};pe();let Z=()=>{};U.addEventListener("change",()=>{h.adapter=U.value,(Os(h.adapter)?Zs:en).some($=>$.value===h.model.trim())&&(h.model=""),pe(),h.permissionMode=$n(h.permissionMode,h.adapter),Z()});let K=F.createDiv({cls:"af-form-row"});K.createDiv({cls:"af-form-label",text:"Working Dir"});let H=K.createEl("input",{cls:"af-form-input",attr:{type:"text",placeholder:"Leave empty for vault root",value:a.cwd??""}});H.addEventListener("input",()=>{h.cwd=H.value});let ne=F.createDiv({cls:"af-form-row"});ne.createDiv({cls:"af-form-label",text:"Timeout (sec)"});let ae=ne.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"number",value:String(a.timeout)}});ae.addEventListener("input",()=>{let M=parseInt(ae.value,10);!isNaN(M)&&M>0&&(h.timeout=M)});let ce=F.createDiv({cls:"af-form-row"});ce.createDiv({cls:"af-form-label",text:"Permission Mode"});let re=ce.createEl("select",{cls:"af-form-select"}),Ee=F.createDiv({cls:"af-form-hint",text:""});Z=()=>{h.permissionMode=$n(h.permissionMode,h.adapter);let M=Un(h.adapter);re.empty();for(let[$,ee]of M){let Oe=re.createEl("option",{text:ee,attr:{value:$}});$===h.permissionMode&&(Oe.selected=!0)}Ee.textContent=M.find(([$])=>$===re.value)?.[2]??""},Z(),re.addEventListener("change",()=>{h.permissionMode=re.value,Ee.textContent=Un(h.adapter).find(([M])=>M===re.value)?.[2]??""});let Ae=F.createDiv({cls:"af-form-row"});Ae.createDiv({cls:"af-form-label",text:"Effort Level"});let Ne=Ae.createEl("select",{cls:"af-form-select"});for(let[M,$]of[["","Default"],["low","Low"],["medium","Medium"],["high","High"],["max","Max"]]){let ee=Ne.createEl("option",{text:$,attr:{value:M}});M===(a.effort??"")&&(ee.selected=!0)}Ne.addEventListener("change",()=>{h.effort=Ne.value}),F.createDiv({cls:"af-form-hint",text:"Controls reasoning depth \u2014 low is fast, max is most thorough"});let Ue=F.createDiv({cls:"af-form-row"}),Pe=Ue.createDiv({cls:"af-form-label",text:"Auto-compact at"});this.addTooltip(Pe,"Percent of context window at which the chat auto-invokes /compact before the next message. 85% is a good default. Set 0 to disable.");let Te=Ue.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"number",min:"0",max:"100",value:String(h.autoCompactThreshold)}});Te.addEventListener("input",()=>{let M=parseInt(Te.value,10);!isNaN(M)&&M>=0&&M<=100&&(h.autoCompactThreshold=M)}),F.createDiv({cls:"af-form-hint",text:"0 disables auto-compact"});{let M=this.plugin.runtime.getSnapshot().agents.filter($=>$.wikiKeeper!==void 0);if(M.length>0){let $=F.createDiv({cls:"af-form-row af-form-row-toggle"}),ee=$.createDiv({cls:"af-form-label",text:"Wiki access"});this.addTooltip(ee,"Lets this agent read + cite from the selected Wiki Keeper scopes (requires the wiki-query skill).");let Oe=$.createDiv({cls:"af-form-field-wrap"});for(let at of M){let it=Oe.createEl("label",{cls:"af-form-checkbox-row"}),Ve=it.createEl("input",{attr:{type:"checkbox"}});h.wikiReferences.includes(at.name)&&(Ve.checked=!0),it.createSpan({text:` ${at.name}`,cls:"af-form-checkbox-label"}),Ve.addEventListener("change",()=>{Ve.checked?h.wikiReferences.includes(at.name)||h.wikiReferences.push(at.name):h.wikiReferences=h.wikiReferences.filter(Mt=>Mt!==at.name)})}}}if(a.isFolder){let M=u.createDiv({cls:"af-create-section"}),$=M.createDiv({cls:"af-create-section-header"}),ee=$.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(ee,"heart-pulse");let Oe=$.createSpan({text:"Heartbeat"});this.addTooltip(Oe,"Autonomous periodic run \u2014 what the agent does when no one is asking");let at=M.createDiv({cls:"af-form-row af-form-row-toggle"});at.createDiv({cls:"af-form-label",text:"Enabled"});let it=at.createDiv({cls:`af-agent-card-toggle${h.heartbeatEnabled?" on":""}`}),Ve=M.createDiv();Ve.style.display=h.heartbeatEnabled?"":"none",it.onclick=()=>{let ut=it.hasClass("on");it.toggleClass("on",!ut),h.heartbeatEnabled=!ut,Ve.style.display=ut?"none":""},this.renderHeartbeatSchedule(Ve,h);let Mt=Ve.createDiv({cls:"af-form-row af-form-row-toggle"}),ja=Mt.createDiv({cls:"af-form-label"});ja.setText("Notify"),this.addTooltip(ja,"Show an Obsidian notice when the heartbeat completes");let Hn=Mt.createDiv({cls:`af-agent-card-toggle${h.heartbeatNotify?" on":""}`});Hn.onclick=()=>{let ut=Hn.hasClass("on");Hn.toggleClass("on",!ut),h.heartbeatNotify=!ut};let Eo=this.plugin.runtime.getSnapshot(),Wa=Ve.createDiv({cls:"af-form-row"}),Ha=Wa.createDiv({cls:"af-form-label"});Ha.setText("Post to channel"),this.addTooltip(Ha,"Heartbeat results are posted to this Slack channel when the run completes");let $s=Wa.createEl("select",{cls:"af-form-select"});$s.createEl("option",{text:"(none)",attr:{value:""}});for(let ut of Eo.channels){let Ao=$s.createEl("option",{text:ut.name,attr:{value:ut.name}});ut.name===h.heartbeatChannel&&(Ao.selected=!0)}$s.addEventListener("change",()=>{h.heartbeatChannel=$s.value});let js=Ve.createDiv({cls:"af-form-label"});js.style.width="auto",js.style.marginTop="12px",js.setText("Instruction"),this.addTooltip(js,'What the agent does on each heartbeat. Also used by the "Run Now" button.');let qn=Ve.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Check status, scan for issues, report findings...",rows:"8"}});qn.value=h.heartbeatBody,qn.addEventListener("input",()=>{h.heartbeatBody=qn.value})}let Re=u.createDiv({cls:"af-create-section"}),Be=Re.createDiv({cls:"af-create-section-header"}),$e=Be.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)($e,"puzzle"),Be.createSpan({text:"Skills"});let q=this.plugin.runtime.getSnapshot();if(q.skills.length>0){Re.createDiv({cls:"af-form-sublabel",text:"Shared Skills"});let M=Re.createDiv({cls:"af-create-skills-grid"});for(let $ of q.skills){let ee=M.createDiv({cls:"af-create-skill-item"}),Oe=ee.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});Oe.checked=h.selectedSkills.has($.name),Oe.addEventListener("change",()=>{Oe.checked?h.selectedSkills.add($.name):h.selectedSkills.delete($.name)});let at=ee.createDiv({cls:"af-create-skill-label"});at.createSpan({cls:"af-create-skill-name",text:$.name}),$.description&&at.createSpan({cls:"af-create-skill-desc",text:` \u2014 ${$.description}`})}}let te=Re.createDiv({cls:"af-form-sublabel"});te.setText("Agent-specific skills"),this.addTooltip(te,"Custom skills/instructions only for this agent, not shared with others");let j=Re.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Custom skills/instructions for this agent...",rows:"4"}});j.value=a.skillsBody,j.addEventListener("input",()=>{h.skillsBody=j.value});let we=u.createDiv({cls:"af-create-section"}),xe=we.createDiv({cls:"af-create-section-header"}),ve=xe.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(ve,"plug");let De=xe.createSpan({text:"MCP Servers"});this.addTooltip(De,"Grant agent access to MCP servers"),we.createDiv({cls:"af-form-hint",text:"Codex agents read servers from Codex's own config (`codex mcp add`); selections here are matched to those servers by name."});let J=this.plugin.mcpManager.getCachedServers();if(J===null){let M=we.createDiv({cls:"af-form-hint"});M.appendText("MCP servers not loaded. ");let $=M.createEl("a",{cls:"af-link",text:"Go to MCP Servers tab to load them."});$.onclick=ee=>{ee.preventDefault(),this.navigate("mcp")}}else if(J.length===0)we.createDiv({cls:"af-form-hint",text:"No MCP servers found. Configure them with 'claude mcp add'."});else{let M=we.createDiv({cls:"af-create-skills-grid"});for(let $ of J){let ee=M.createDiv({cls:"af-mcp-agent-item"}),Oe=ee.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});Oe.checked=h.selectedMcpServers.has($.name),Oe.addEventListener("change",()=>{Oe.checked?h.selectedMcpServers.add($.name):h.selectedMcpServers.delete($.name)});let it=ee.createDiv({cls:"af-mcp-agent-label"}).createDiv({cls:"af-mcp-agent-name-row"}),Ve=it.createSpan({cls:`af-mcp-status-dot ${$.enabled?$.status:"disabled"}`});Ve.title=$.enabled?$.status:"disabled",it.createSpan({cls:"af-create-skill-name",text:$.name});let Mt=$.toolDetails.length||$.tools.length;Mt>0?it.createSpan({cls:"af-mcp-agent-tool-count",text:`${Mt} tools`}):$.enabled?$.status==="needs-auth"&&it.createSpan({cls:"af-mcp-agent-tool-count af-muted",text:"needs auth"}):it.createSpan({cls:"af-mcp-agent-tool-count af-muted",text:"disabled"})}}let P=u.createDiv({cls:"af-create-section"}),X=P.createDiv({cls:"af-create-section-header"}),de=X.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(de,"file-text");let me=X.createSpan({text:"Context"});this.addTooltip(me,"Project-specific context included in every run");let tt=P.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Background info, repo structure, conventions...",rows:"4"}});tt.value=a.contextBody,tt.addEventListener("input",()=>{h.contextBody=tt.value});let st=u.createDiv({cls:"af-create-section"}),Gt=st.createDiv({cls:"af-create-section-header"}),Bs=Gt.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(Bs,"shield-check"),Gt.createSpan({text:"Permissions"}),this.createFormField(st,"Approval required","git_push, file_delete","Comma-separated tool names",M=>{h.approvalRequired=M},a.approvalRequired.join(", "));let Us=st.createDiv({cls:"af-form-row"});Us.createDiv({cls:"af-form-label",text:"Allowed Commands"});let Dt=Us.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:`Bash(curl *)
|
|
11911
12080
|
Bash(python3 *)
|
|
11912
12081
|
Read
|
|
11913
12082
|
Edit
|
|
11914
|
-
Write`,rows:"4"}});
|
|
11915
|
-
`),
|
|
12083
|
+
Write`,rows:"4"}});Dt.value=a.permissionRules.allow.join(`
|
|
12084
|
+
`),Dt.addEventListener("input",()=>{h.allowedCommands=Dt.value});let N=st.createDiv({cls:"af-form-row"});N.createDiv({cls:"af-form-label",text:"Blocked Commands"});let V=N.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:`Bash(git push *)
|
|
11916
12085
|
Bash(rm -rf *)
|
|
11917
|
-
Bash(sudo *)`,rows:"4"}});
|
|
11918
|
-
`),Y.addEventListener("input",()=>{h.blockedCommands=Y.value});let Ue=vt.createDiv({cls:"af-form-row"});Ue.createDiv({cls:"af-form-label",text:"Memory enabled"});let ke=Ue.createDiv({cls:`af-agent-card-toggle${n.memory?" on":""}`});ke.onclick=()=>{let O=ke.hasClass("on");ke.toggleClass("on",!O),h.memory=!O};let ge=s.createDiv({cls:"af-create-footer"}),Fe=ge.createEl("button",{cls:"af-btn-sm danger"});D(Fe,"trash-2","af-btn-icon"),Fe.appendText(" Delete"),Fe.onclick=()=>void this.plugin.deleteAgent(n.name),ge.createDiv({cls:"af-toolbar-spacer"});let pe=ge.createEl("button",{cls:"af-btn-sm",text:"Cancel"});pe.onclick=()=>this.navigate("agent-detail",n.name);let Ge=ge.createEl("button",{cls:"af-btn-sm primary af-create-submit"});D(Ge,"check","af-btn-icon"),Ge.appendText(" Save Changes"),Ge.onclick=async()=>{let O=j=>j.split(",").map(se=>se.trim()).filter(Boolean);try{let j=se=>me(se).map(_e=>_e.trim()).filter(Boolean);await this.plugin.repository.updateAgent(n.name,{description:h.description.trim(),avatar:h.avatar.trim(),tags:O(h.tags),systemPrompt:h.systemPrompt.trim(),model:h.model.trim()||"default",adapter:h.adapter,cwd:h.cwd.trim(),timeout:h.timeout,permissionMode:h.permissionMode,effort:h.effort||void 0,approvalRequired:O(h.approvalRequired),memory:h.memory,skills:Array.from(h.selectedSkills),mcpServers:Array.from(h.selectedMcpServers),skillsBody:h.skillsBody.trim(),contextBody:h.contextBody.trim(),enabled:h.enabled,permissionRules:{allow:j(h.allowedCommands),deny:j(h.blockedCommands)},autoCompactThreshold:h.autoCompactThreshold,wikiReferences:h.wikiReferences}),n.isFolder&&await this.plugin.repository.updateHeartbeat(n.name,{enabled:h.heartbeatEnabled,schedule:h.heartbeatSchedule.trim(),notify:h.heartbeatNotify,channel:h.heartbeatChannel,body:h.heartbeatBody.trim()}),new w.Notice(`Agent "${n.name}" updated.`),await this.plugin.refreshFromVault(),this.navigate("agent-detail",n.name)}catch(j){let se=j instanceof Error?j.message:String(j);new w.Notice(`Failed to update agent: ${se}`)}}}renderCreateTaskPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),a=this.plugin.runtime.getSnapshot(),n=s.createDiv({cls:"af-detail-header"}),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,scheduleMode:"recurring",schedule:"0 9 * * *",runAt:"",type:"immediate",enabled:!0,catchUp:!0,effort:"",model:""},d=s.createDiv({cls:"af-create-form"}),h=d.createDiv({cls:"af-create-section"}),u=h.createDiv({cls:"af-create-section-header"}),p=u.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(p,"file-text"),u.createSpan({text:"Task Details"}),this.createFormField(h,"Title","Daily status report","Used as the task identifier",G=>{c.title=G});let f=h.createDiv({cls:"af-form-row"});f.createDiv({cls:"af-form-label",text:"Agent"});let m=f.createEl("select",{cls:"af-form-select"});for(let G of a.agents)m.createEl("option",{text:G.name,attr:{value:G.name}});m.addEventListener("change",()=>{c.agent=m.value});let g=h.createDiv({cls:"af-form-row"});g.createDiv({cls:"af-form-label",text:"Priority"});let k=g.createEl("select",{cls:"af-form-select"}),b=[["low","Low"],["medium","Medium"],["high","High"],["critical","Critical"]];for(let[G,te]of b){let H=k.createEl("option",{text:te,attr:{value:G}});G==="medium"&&(H.selected=!0)}k.addEventListener("change",()=>{c.priority=k.value}),this.createFormField(h,"Tags","monitoring, devops","Comma-separated",G=>{c.tags=G});let v=d.createDiv({cls:"af-create-section"}),y=v.createDiv({cls:"af-create-section-header"}),x=y.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(x,"message-square"),y.createSpan({text:"Instructions"});let T=v.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Describe what the agent should do...",rows:"10"}});T.addEventListener("input",()=>{c.body=T.value});let _=d.createDiv({cls:"af-create-section"}),R=_.createDiv({cls:"af-create-section-header"}),A=R.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(A,"clock"),R.createSpan({text:"Schedule"});let S=_.createDiv({cls:"af-form-row af-form-row-toggle"});S.createDiv({cls:"af-form-label",text:"Enable schedule"});let L=S.createDiv({cls:"af-agent-card-toggle"}),E=_.createDiv({cls:"af-schedule-body"});E.style.display="none",L.onclick=()=>{let G=L.hasClass("on");L.toggleClass("on",!G),c.scheduleEnabled=!G,E.style.display=G?"none":"",G?c.type="immediate":c.type=c.scheduleMode==="once"?"once":"recurring"};let N=E.createDiv({cls:"af-form-row"});N.createDiv({cls:"af-form-label",text:"Mode"});let I=N.createEl("select",{cls:"af-form-select"});for(let[G,te]of[["recurring","Recurring"],["once","One-time"]])I.createEl("option",{text:te,attr:{value:G}});let q=E.createDiv(),F=E.createDiv();F.style.display="none",this.renderInlineSchedule(q,c);let $=F.createDiv({cls:"af-form-row"});$.createDiv({cls:"af-form-label",text:"Run at"});let W=$.createEl("input",{cls:"af-form-input",attr:{type:"datetime-local",value:this.toDatetimeLocal(new Date(Date.now()+36e5))}});c.runAt=new Date(W.value).toISOString(),W.addEventListener("input",()=>{c.runAt=W.value?new Date(W.value).toISOString():""}),I.addEventListener("change",()=>{c.scheduleMode=I.value,q.style.display=c.scheduleMode==="recurring"?"":"none",F.style.display=c.scheduleMode==="once"?"":"none",c.scheduleEnabled&&(c.type=c.scheduleMode==="once"?"once":"recurring")});let ie=E.createDiv({cls:"af-form-row af-form-row-toggle"});ie.createDiv({cls:"af-form-label",text:"Enabled"});let ue=ie.createDiv({cls:"af-agent-card-toggle on"});ue.onclick=()=>{let G=ue.hasClass("on");ue.toggleClass("on",!G),c.enabled=!G};let ee=E.createDiv({cls:"af-form-row af-form-row-toggle"});ee.createDiv({cls:"af-form-label"}).setText("Catch up if missed");let z=ee.createDiv({cls:`af-agent-card-toggle${c.catchUp?" on":""}`});z.onclick=()=>{let G=z.hasClass("on");z.toggleClass("on",!G),c.catchUp=!G};let Q=d.createDiv({cls:"af-create-section"}),ce=Q.createDiv({cls:"af-create-section-header"}),ne=ce.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(ne,"gauge"),ce.createSpan({text:"Execution"});let de=Q.createDiv({cls:"af-form-row"}),Le=de.createDiv({cls:"af-form-label",text:"Model"}),Ae=de.createDiv({cls:"af-form-field-wrap"}),Ne=G=>{Ae.empty();let te=a.agents.find(H=>H.name===G);_t(Ae,{value:c.model,onChange:H=>{c.model=H},allowInherit:!0,inheritPlaceholder:te?`Inherit from ${te.name}${te.model?` (${te.model})`:""}`:"Inherit from agent"})};Ne(c.agent),m.addEventListener("change",()=>Ne(m.value)),this.addTooltip(Le,"Override the agent\u2019s model for this task only. Useful for routing simple runs to haiku while the agent stays on opus for heavier work.");let je=Q.createDiv({cls:"af-form-row"}),Ee=je.createDiv({cls:"af-form-label",text:"Effort"}),Te=je.createEl("select",{cls:"af-form-select"});for(let[G,te]of[["","Agent Default"],["low","Low"],["medium","Medium"],["high","High"],["max","Max"]]){let H=Te.createEl("option",{text:te,attr:{value:G}});G===c.effort&&(H.selected=!0)}Te.addEventListener("change",()=>{c.effort=Te.value}),this.addTooltip(Ee,"Overrides the agent\u2019s effort level for this task. Higher effort = more thinking tokens spent.");let De=s.createDiv({cls:"af-create-footer"}),Be=De.createEl("button",{cls:"af-btn-sm",text:"Cancel"});Be.onclick=()=>this.navigate("kanban");let He=De.createEl("button",{cls:"af-btn-sm primary af-create-submit"});D(He,"plus","af-btn-icon"),He.appendText(" Create Task"),He.onclick=async()=>{let G=c.title.trim();if(!G){new w.Notice("Task title is required.");return}let te=xe(G),H=fe=>fe.split(",").map(Me=>Me.trim()).filter(Boolean),be=c.scheduleEnabled?c.scheduleMode==="once"?"once":"recurring":"immediate",we={task_id:te,agent:c.agent,type:be,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:H(c.tags)};if(be==="recurring")we.schedule=c.schedule.trim()||"0 9 * * *";else if(be==="once"){if(!c.runAt){new w.Notice("Pick a date/time for the one-time run.");return}we.run_at=c.runAt}try{let fe=await this.plugin.repository.getAvailablePath(this.plugin.repository.getSubfolder("tasks"),te);await this.plugin.app.vault.create(fe,K(we,c.body.trim()||"Describe the task here.")),new w.Notice(`Task "${te}" created.`),await this.plugin.refreshFromVault(),this.navigate("task-detail",te)}catch(fe){let Me=fe instanceof Error?fe.message:String(fe);new w.Notice(`Failed to create task: ${Me}`)}}}toLocalISO(e){let s=a=>String(a).padStart(2,"0");return`${e.getFullYear()}-${s(e.getMonth()+1)}-${s(e.getDate())}T${s(e.getHours())}:${s(e.getMinutes())}:${s(e.getSeconds())}`}toDatetimeLocal(e){let s=a=>String(a).padStart(2,"0");return`${e.getFullYear()}-${s(e.getMonth()+1)}-${s(e.getDate())}T${s(e.getHours())}:${s(e.getMinutes())}`}renderEditTaskPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),a=this.detailContext;if(!a){this.renderEmptyState(s,"circle-dot","No task selected","");return}let n=this.plugin.runtime.getSnapshot().tasks.find(P=>P.taskId===a);if(!n){this.renderEmptyState(s,"circle-dot","Task not found",`Task "${a}" was not found`);return}let i=this.plugin.runtime.getSnapshot(),o=s.createDiv({cls:"af-detail-header"}),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 * * *",runAt:n.runAt??"",scheduleEnabled:h,scheduleMode:n.type==="once"?"once":"recurring",enabled:n.enabled,catchUp:n.catchUp,effort:n.effort??"",model:n.model??"",tags:n.tags.join(", "),body:n.body},p=s.createDiv({cls:"af-create-form"}),f=p.createDiv({cls:"af-create-section"}),m=f.createDiv({cls:"af-create-section-header"}),g=m.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(g,"file-text"),m.createSpan({text:"Task Details"});let k=f.createDiv({cls:"af-form-row"});k.createDiv({cls:"af-form-label",text:"Title"});let b=k.createEl("input",{cls:"af-form-input",attr:{type:"text",value:n.taskId,disabled:"true"}});b.style.opacity="0.6";let v=f.createDiv({cls:"af-form-row"});v.createDiv({cls:"af-form-label",text:"Agent"});let y=v.createEl("select",{cls:"af-form-select"});for(let P of i.agents){let Z=y.createEl("option",{text:P.name,attr:{value:P.name}});P.name===n.agent&&(Z.selected=!0)}y.addEventListener("change",()=>{u.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"}),_=[["low","Low"],["medium","Medium"],["high","High"],["critical","Critical"]];for(let[P,Z]of _){let re=T.createEl("option",{text:Z,attr:{value:P}});P===n.priority&&(re.selected=!0)}T.addEventListener("change",()=>{u.priority=T.value}),this.createFormField(f,"Tags","monitoring, critical","Comma-separated",P=>{u.tags=P},n.tags.join(", "));let R=p.createDiv({cls:"af-create-section"}),A=R.createDiv({cls:"af-create-section-header"}),S=A.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(S,"message-square"),A.createSpan({text:"Instructions"});let L=R.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Describe what the agent should do...",rows:"10"}});L.value=n.body,L.addEventListener("input",()=>{u.body=L.value});let E=p.createDiv({cls:"af-create-section"}),N=E.createDiv({cls:"af-create-section-header"}),I=N.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(I,"clock"),N.createSpan({text:"Schedule"});let q=E.createDiv({cls:"af-form-row af-form-row-toggle"});q.createDiv({cls:"af-form-label",text:"Enable schedule"});let F=q.createDiv({cls:`af-agent-card-toggle${h?" on":""}`}),$=E.createDiv({cls:"af-schedule-body"});$.style.display=h?"":"none",F.onclick=()=>{let P=F.hasClass("on");F.toggleClass("on",!P),u.scheduleEnabled=!P,$.style.display=P?"none":"",P?u.type="immediate":u.type=u.scheduleMode==="once"?"once":"recurring"};let W=$.createDiv({cls:"af-form-row"});W.createDiv({cls:"af-form-label",text:"Mode"});let ie=W.createEl("select",{cls:"af-form-select"});for(let[P,Z]of[["recurring","Recurring"],["once","One-time"]]){let re=ie.createEl("option",{text:Z,attr:{value:P}});P===u.scheduleMode&&(re.selected=!0)}let ue=$.createDiv(),ee=$.createDiv();ue.style.display=u.scheduleMode==="recurring"?"":"none",ee.style.display=u.scheduleMode==="once"?"":"none",this.renderInlineSchedule(ue,u);let V=ee.createDiv({cls:"af-form-row"});V.createDiv({cls:"af-form-label",text:"Run at"});let z=u.runAt?this.toDatetimeLocal(new Date(u.runAt)):this.toDatetimeLocal(new Date(Date.now()+36e5)),Q=V.createEl("input",{cls:"af-form-input",attr:{type:"datetime-local",value:z}});u.runAt||(u.runAt=new Date(Q.value).toISOString()),Q.addEventListener("input",()=>{u.runAt=Q.value?new Date(Q.value).toISOString():""}),ie.addEventListener("change",()=>{u.scheduleMode=ie.value,ue.style.display=u.scheduleMode==="recurring"?"":"none",ee.style.display=u.scheduleMode==="once"?"":"none",u.scheduleEnabled&&(u.type=u.scheduleMode==="once"?"once":"recurring")});let ce=$.createDiv({cls:"af-form-row af-form-row-toggle"});ce.createDiv({cls:"af-form-label",text:"Enabled"});let ne=ce.createDiv({cls:`af-agent-card-toggle${n.enabled?" on":""}`});ne.onclick=()=>{let P=ne.hasClass("on");ne.toggleClass("on",!P),u.enabled=!P};let de=$.createDiv({cls:"af-form-row af-form-row-toggle"});de.createDiv({cls:"af-form-label"}).setText("Catch up if missed");let Ae=de.createDiv({cls:`af-agent-card-toggle${u.catchUp?" on":""}`});Ae.onclick=()=>{let P=Ae.hasClass("on");Ae.toggleClass("on",!P),u.catchUp=!P};let Ne=p.createDiv({cls:"af-create-section"}),je=Ne.createDiv({cls:"af-create-section-header"}),Ee=je.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(Ee,"gauge"),je.createSpan({text:"Execution"});let Te=Ne.createDiv({cls:"af-form-row"}),De=Te.createDiv({cls:"af-form-label",text:"Effort"}),Be=Te.createEl("select",{cls:"af-form-select"}),He=[["","Agent Default"],["low","Low"],["medium","Medium"],["high","High"],["max","Max"]];for(let[P,Z]of He){let re=Be.createEl("option",{text:Z,attr:{value:P}});P===u.effort&&(re.selected=!0)}Be.addEventListener("change",()=>{u.effort=Be.value}),this.addTooltip(De,"Overrides the agent\u2019s effort level for this task. Higher effort = more thinking tokens spent.");let G=Ne.createDiv({cls:"af-form-row"}),te=G.createDiv({cls:"af-form-label",text:"Model"}),H=G.createDiv({cls:"af-form-field-wrap"}),be=P=>{H.empty();let Z=i.agents.find(re=>re.name===P);_t(H,{value:u.model,onChange:re=>{u.model=re},allowInherit:!0,inheritPlaceholder:Z?`Inherit from ${Z.name}${Z.model?` (${Z.model})`:""}`:"Inherit from agent"})};be(u.agent),y.addEventListener("change",()=>be(y.value)),this.addTooltip(te,"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 we=s.createDiv({cls:"af-create-footer"}),fe=we.createEl("button",{cls:"af-btn-sm danger"});D(fe,"trash-2","af-btn-icon"),fe.appendText(" Delete"),fe.onclick=async()=>{await this.plugin.repository.deleteTask(n.taskId),new w.Notice(`Task "${n.taskId}" deleted.`),await new Promise(P=>setTimeout(P,200)),await this.plugin.refreshFromVault(),this.navigate("kanban")},we.createDiv({cls:"af-toolbar-spacer"});let Me=we.createEl("button",{cls:"af-btn-sm",text:"Cancel"});Me.onclick=()=>this.navigate("task-detail",n.taskId);let J=we.createEl("button",{cls:"af-btn-sm primary af-create-submit"});D(J,"check","af-btn-icon"),J.appendText(" Save Changes"),J.onclick=async()=>{let P=re=>re.split(",").map(he=>he.trim()).filter(Boolean),Z=u.scheduleEnabled?u.scheduleMode==="once"?"once":"recurring":"immediate";if(Z==="once"&&!u.runAt){new w.Notice("Pick a date/time for the one-time run.");return}try{await this.plugin.repository.updateTask(n.taskId,{agent:u.agent,type:Z,priority:u.priority,schedule:Z==="recurring"?u.schedule.trim():"",runAt:Z==="once"?u.runAt:"",enabled:u.enabled,catch_up:u.catchUp,effort:u.effort||void 0,model:u.model||"",tags:P(u.tags),body:u.body.trim()}),new w.Notice(`Task "${n.taskId}" updated.`),await this.plugin.refreshFromVault(),this.navigate("task-detail",n.taskId)}catch(re){let he=re instanceof Error?re.message:String(re);new w.Notice(`Failed to update task: ${he}`)}}}renderEditSkillPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),a=this.detailContext;if(!a){this.renderEmptyState(s,"puzzle","No skill selected","");return}let n=this.plugin.runtime.getSnapshot().skills.find(ce=>ce.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"}),p=u.createDiv({cls:"af-create-section"}),f=p.createDiv({cls:"af-create-section-header"}),m=f.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(m,"puzzle"),f.createSpan({text:"Identity"});let g=p.createDiv({cls:"af-form-row"});g.createDiv({cls:"af-form-label",text:"Name"});let k=g.createEl("input",{cls:"af-form-input",attr:{type:"text",value:n.name,disabled:"true"}});k.style.opacity="0.6",this.createFormField(p,"Description","Manage tasks and projects via CLI","",ce=>{h.description=ce},n.description??""),this.createFormField(p,"Tags","productivity, tasks","Comma-separated",ce=>{h.tags=ce},n.tags.join(", "));let b=u.createDiv({cls:"af-create-section"}),v=b.createDiv({cls:"af-create-section-header"}),y=v.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(y,"file-text"),v.createSpan({text:"Core Instructions"});let x=b.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Skill instructions...",rows:"10"}});x.value=n.body,x.addEventListener("input",()=>{h.body=x.value});let T=u.createDiv({cls:"af-create-section"}),_=T.createDiv({cls:"af-create-section-header"}),R=_.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(R,"wrench");let A=_.createSpan({text:"Tools"});this.addTooltip(A,"CLI commands, API endpoints, and tool definitions available to agents using this skill");let S=T.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`## Commands
|
|
12086
|
+
Bash(sudo *)`,rows:"4"}});V.value=a.permissionRules.deny.join(`
|
|
12087
|
+
`),V.addEventListener("input",()=>{h.blockedCommands=V.value}),st.createDiv({cls:"af-form-hint",text:"On Codex agents these become execpolicy command rules \u2014 only Bash(cmd args *) prefixes are enforced; tool-name rules (Read/Write) and mid-pattern wildcards are ignored, and file/network access is governed by Permission Mode (the sandbox)."});let Ie=st.createDiv({cls:"af-form-row"});Ie.createDiv({cls:"af-form-label",text:"Memory enabled"});let be=Ie.createDiv({cls:`af-agent-card-toggle${a.memory?" on":""}`});be.onclick=()=>{let M=be.hasClass("on");be.toggleClass("on",!M),h.memory=!M};let Se=st.createDiv({cls:"af-form-row"});Se.createDiv({cls:"af-form-label",text:"Memory token budget"});let Fe=Se.createEl("input",{cls:"af-create-input",attr:{type:"number",min:"200",step:"100"}});Fe.value=String(h.memoryTokenBudget),Fe.addEventListener("input",()=>{let M=parseInt(Fe.value,10);Number.isFinite(M)&&(h.memoryTokenBudget=M)});let fe=st.createDiv({cls:"af-form-row"});fe.createDiv({cls:"af-form-label",text:"Nightly reflection"});let Ge=fe.createDiv({cls:`af-agent-card-toggle${a.reflection.enabled?" on":""}`});Ge.onclick=()=>{let M=Ge.hasClass("on");Ge.toggleClass("on",!M),h.reflectionEnabled=!M};let St=st.createDiv({cls:"af-form-row"});St.createDiv({cls:"af-form-label",text:"Propose skills from memory"});let Ct=St.createDiv({cls:`af-agent-card-toggle${a.reflection.proposeSkills?" on":""}`});Ct.onclick=()=>{let M=Ct.hasClass("on");Ct.toggleClass("on",!M),h.reflectionProposeSkills=!M};let nt=s.createDiv({cls:"af-create-footer"}),Tt=nt.createEl("button",{cls:"af-btn-sm danger"});A(Tt,"trash-2","af-btn-icon"),Tt.appendText(" Delete"),Tt.onclick=()=>void this.plugin.deleteAgent(a.name),nt.createDiv({cls:"af-toolbar-spacer"});let It=nt.createEl("button",{cls:"af-btn-sm",text:"Cancel"});It.onclick=()=>this.navigate("agent-detail",a.name);let _t=nt.createEl("button",{cls:"af-btn-sm primary af-create-submit"});A(_t,"check","af-btn-icon"),_t.appendText(" Save Changes"),_t.onclick=async()=>{let M=$=>$.split(",").map(ee=>ee.trim()).filter(Boolean);try{let $=ee=>oe(ee).map(Oe=>Oe.trim()).filter(Boolean);await this.plugin.repository.updateAgent(a.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,memoryTokenBudget:h.memoryTokenBudget,reflectionEnabled:h.reflectionEnabled,reflectionProposeSkills:h.reflectionProposeSkills,skills:Array.from(h.selectedSkills),mcpServers:Array.from(h.selectedMcpServers),skillsBody:h.skillsBody.trim(),contextBody:h.contextBody.trim(),enabled:h.enabled,permissionRules:{allow:$(h.allowedCommands),deny:$(h.blockedCommands)},autoCompactThreshold:h.autoCompactThreshold,wikiReferences:h.wikiReferences}),a.isFolder&&await this.plugin.repository.updateHeartbeat(a.name,{enabled:h.heartbeatEnabled,schedule:h.heartbeatSchedule.trim(),notify:h.heartbeatNotify,channel:h.heartbeatChannel,body:h.heartbeatBody.trim()}),new b.Notice(`Agent "${a.name}" updated.`),await this.plugin.refreshFromVault(),this.navigate("agent-detail",a.name)}catch($){let ee=$ instanceof Error?$.message:String($);new b.Notice(`Failed to update agent: ${ee}`)}}}renderCreateTaskPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),n=this.plugin.runtime.getSnapshot(),a=s.createDiv({cls:"af-detail-header"}),r=a.createDiv({cls:"af-detail-header-left"}),o=r.createDiv({cls:"af-agent-card-avatar idle"});(0,b.setIcon)(o,"plus");let c=r.createDiv();c.createDiv({cls:"af-detail-header-name",text:"Create New Task"}),c.createDiv({cls:"af-detail-header-desc",text:"Configure a new task for your fleet"}),a.createDiv({cls:"af-detail-header-actions"});let l={title:"",agent:n.agents[0]?.name??"",priority:"medium",tags:"",body:"",scheduleEnabled:!1,scheduleMode:"recurring",schedule:"0 9 * * *",runAt:"",type:"immediate",enabled:!0,catchUp:!0,effort:"",model:""},d=s.createDiv({cls:"af-create-form"}),h=d.createDiv({cls:"af-create-section"}),u=h.createDiv({cls:"af-create-section-header"}),p=u.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(p,"file-text"),u.createSpan({text:"Task Details"}),this.createFormField(h,"Title","Daily status report","Used as the task identifier",q=>{l.title=q});let m=h.createDiv({cls:"af-form-row"});m.createDiv({cls:"af-form-label",text:"Agent"});let f=m.createEl("select",{cls:"af-form-select"});for(let q of n.agents)f.createEl("option",{text:q.name,attr:{value:q.name}});f.addEventListener("change",()=>{l.agent=f.value});let y=h.createDiv({cls:"af-form-row"});y.createDiv({cls:"af-form-label",text:"Priority"});let w=y.createEl("select",{cls:"af-form-select"}),k=[["low","Low"],["medium","Medium"],["high","High"],["critical","Critical"]];for(let[q,te]of k){let j=w.createEl("option",{text:te,attr:{value:q}});q==="medium"&&(j.selected=!0)}w.addEventListener("change",()=>{l.priority=w.value}),this.createFormField(h,"Tags","monitoring, devops","Comma-separated",q=>{l.tags=q});let g=d.createDiv({cls:"af-create-section"}),v=g.createDiv({cls:"af-create-section-header"}),S=v.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(S,"message-square"),v.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",()=>{l.body=T.value});let _=d.createDiv({cls:"af-create-section"}),D=_.createDiv({cls:"af-create-section-header"}),E=D.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(E,"clock"),D.createSpan({text:"Schedule"});let C=_.createDiv({cls:"af-form-row af-form-row-toggle"});C.createDiv({cls:"af-form-label",text:"Enable schedule"});let L=C.createDiv({cls:"af-agent-card-toggle"}),R=_.createDiv({cls:"af-schedule-body"});R.style.display="none",L.onclick=()=>{let q=L.hasClass("on");L.toggleClass("on",!q),l.scheduleEnabled=!q,R.style.display=q?"none":"",q?l.type="immediate":l.type=l.scheduleMode==="once"?"once":"recurring"};let B=R.createDiv({cls:"af-form-row"});B.createDiv({cls:"af-form-label",text:"Mode"});let F=B.createEl("select",{cls:"af-form-select"});for(let[q,te]of[["recurring","Recurring"],["once","One-time"]])F.createEl("option",{text:te,attr:{value:q}});let Y=R.createDiv(),U=R.createDiv();U.style.display="none",this.renderInlineSchedule(Y,l);let W=U.createDiv({cls:"af-form-row"});W.createDiv({cls:"af-form-label",text:"Run at"});let ue=W.createEl("input",{cls:"af-form-input",attr:{type:"datetime-local",value:this.toDatetimeLocal(new Date(Date.now()+36e5))}});l.runAt=new Date(ue.value).toISOString(),ue.addEventListener("input",()=>{l.runAt=ue.value?new Date(ue.value).toISOString():""}),F.addEventListener("change",()=>{l.scheduleMode=F.value,Y.style.display=l.scheduleMode==="recurring"?"":"none",U.style.display=l.scheduleMode==="once"?"":"none",l.scheduleEnabled&&(l.type=l.scheduleMode==="once"?"once":"recurring")});let ge=R.createDiv({cls:"af-form-row af-form-row-toggle"});ge.createDiv({cls:"af-form-label",text:"Enabled"});let pe=ge.createDiv({cls:"af-agent-card-toggle on"});pe.onclick=()=>{let q=pe.hasClass("on");pe.toggleClass("on",!q),l.enabled=!q};let Z=R.createDiv({cls:"af-form-row af-form-row-toggle"});Z.createDiv({cls:"af-form-label"}).setText("Catch up if missed");let H=Z.createDiv({cls:`af-agent-card-toggle${l.catchUp?" on":""}`});H.onclick=()=>{let q=H.hasClass("on");H.toggleClass("on",!q),l.catchUp=!q};let ne=d.createDiv({cls:"af-create-section"}),ae=ne.createDiv({cls:"af-create-section-header"}),ce=ae.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(ce,"gauge"),ae.createSpan({text:"Execution"});let re=ne.createDiv({cls:"af-form-row"}),Ee=re.createDiv({cls:"af-form-label",text:"Model"}),Ae=re.createDiv({cls:"af-form-field-wrap"}),Ne=q=>{Ae.empty();let te=n.agents.find(j=>j.name===q);Ot(Ae,{value:l.model,adapter:te?.adapter,onChange:j=>{l.model=j},allowInherit:!0,inheritPlaceholder:te?`Inherit from ${te.name}${te.model?` (${te.model})`:""}`:"Inherit from agent"})};Ne(l.agent),f.addEventListener("change",()=>Ne(f.value)),this.addTooltip(Ee,"Override the agent\u2019s model for this task only. Useful for routing simple runs to haiku while the agent stays on opus for heavier work.");let Ue=ne.createDiv({cls:"af-form-row"}),Pe=Ue.createDiv({cls:"af-form-label",text:"Effort"}),Te=Ue.createEl("select",{cls:"af-form-select"});for(let[q,te]of[["","Agent Default"],["low","Low"],["medium","Medium"],["high","High"],["max","Max"]]){let j=Te.createEl("option",{text:te,attr:{value:q}});q===l.effort&&(j.selected=!0)}Te.addEventListener("change",()=>{l.effort=Te.value}),this.addTooltip(Pe,"Overrides the agent\u2019s effort level for this task. Higher effort = more thinking tokens spent.");let Re=s.createDiv({cls:"af-create-footer"}),Be=Re.createEl("button",{cls:"af-btn-sm",text:"Cancel"});Be.onclick=()=>this.navigate("kanban");let $e=Re.createEl("button",{cls:"af-btn-sm primary af-create-submit"});A($e,"plus","af-btn-icon"),$e.appendText(" Create Task"),$e.onclick=async()=>{let q=l.title.trim();if(!q){new b.Notice("Task title is required.");return}let te=se(q),j=ve=>ve.split(",").map(De=>De.trim()).filter(Boolean),we=l.scheduleEnabled?l.scheduleMode==="once"?"once":"recurring":"immediate",xe={task_id:te,agent:l.agent,type:we,priority:l.priority,enabled:l.enabled,created:this.toLocalISO(new Date),run_count:0,catch_up:l.catchUp,effort:l.effort||void 0,model:l.model||void 0,tags:j(l.tags)};if(we==="recurring")xe.schedule=l.schedule.trim()||"0 9 * * *";else if(we==="once"){if(!l.runAt){new b.Notice("Pick a date/time for the one-time run.");return}xe.run_at=l.runAt}try{let ve=await this.plugin.repository.getAvailablePath(this.plugin.repository.getSubfolder("tasks"),te);await this.plugin.app.vault.create(ve,z(xe,l.body.trim()||"Describe the task here.")),new b.Notice(`Task "${te}" created.`),await this.plugin.refreshFromVault(),this.navigate("task-detail",te)}catch(ve){let De=ve instanceof Error?ve.message:String(ve);new b.Notice(`Failed to create task: ${De}`)}}}toLocalISO(e){let s=n=>String(n).padStart(2,"0");return`${e.getFullYear()}-${s(e.getMonth()+1)}-${s(e.getDate())}T${s(e.getHours())}:${s(e.getMinutes())}:${s(e.getSeconds())}`}toDatetimeLocal(e){let s=n=>String(n).padStart(2,"0");return`${e.getFullYear()}-${s(e.getMonth()+1)}-${s(e.getDate())}T${s(e.getHours())}:${s(e.getMinutes())}`}renderEditTaskPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),n=this.detailContext;if(!n){this.renderEmptyState(s,"circle-dot","No task selected","");return}let a=this.plugin.runtime.getSnapshot().tasks.find(P=>P.taskId===n);if(!a){this.renderEmptyState(s,"circle-dot","Task not found",`Task "${n}" was not found`);return}let r=this.plugin.runtime.getSnapshot(),o=s.createDiv({cls:"af-detail-header"}),c=o.createDiv({cls:"af-detail-header-left"}),l=c.createDiv({cls:"af-agent-card-avatar idle"});(0,b.setIcon)(l,"edit");let d=c.createDiv();d.createDiv({cls:"af-detail-header-name",text:`Edit Task: ${a.taskId}`}),d.createDiv({cls:"af-detail-header-desc",text:"Modify task configuration"}),o.createDiv({cls:"af-detail-header-actions"});let h=!!(a.schedule||a.runAt),u={agent:a.agent,type:a.type,priority:a.priority,schedule:a.schedule??"0 9 * * *",runAt:a.runAt??"",scheduleEnabled:h,scheduleMode:a.type==="once"?"once":"recurring",enabled:a.enabled,catchUp:a.catchUp,effort:a.effort??"",model:a.model??"",tags:a.tags.join(", "),body:a.body},p=s.createDiv({cls:"af-create-form"}),m=p.createDiv({cls:"af-create-section"}),f=m.createDiv({cls:"af-create-section-header"}),y=f.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(y,"file-text"),f.createSpan({text:"Task Details"});let w=m.createDiv({cls:"af-form-row"});w.createDiv({cls:"af-form-label",text:"Title"});let k=w.createEl("input",{cls:"af-form-input",attr:{type:"text",value:a.taskId,disabled:"true"}});k.style.opacity="0.6";let g=m.createDiv({cls:"af-form-row"});g.createDiv({cls:"af-form-label",text:"Agent"});let v=g.createEl("select",{cls:"af-form-select"});for(let P of r.agents){let X=v.createEl("option",{text:P.name,attr:{value:P.name}});P.name===a.agent&&(X.selected=!0)}v.addEventListener("change",()=>{u.agent=v.value});let S=m.createDiv({cls:"af-form-row"});S.createDiv({cls:"af-form-label",text:"Priority"});let T=S.createEl("select",{cls:"af-form-select"}),_=[["low","Low"],["medium","Medium"],["high","High"],["critical","Critical"]];for(let[P,X]of _){let de=T.createEl("option",{text:X,attr:{value:P}});P===a.priority&&(de.selected=!0)}T.addEventListener("change",()=>{u.priority=T.value}),this.createFormField(m,"Tags","monitoring, critical","Comma-separated",P=>{u.tags=P},a.tags.join(", "));let D=p.createDiv({cls:"af-create-section"}),E=D.createDiv({cls:"af-create-section-header"}),C=E.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(C,"message-square"),E.createSpan({text:"Instructions"});let L=D.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Describe what the agent should do...",rows:"10"}});L.value=a.body,L.addEventListener("input",()=>{u.body=L.value});let R=p.createDiv({cls:"af-create-section"}),B=R.createDiv({cls:"af-create-section-header"}),F=B.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(F,"clock"),B.createSpan({text:"Schedule"});let Y=R.createDiv({cls:"af-form-row af-form-row-toggle"});Y.createDiv({cls:"af-form-label",text:"Enable schedule"});let U=Y.createDiv({cls:`af-agent-card-toggle${h?" on":""}`}),W=R.createDiv({cls:"af-schedule-body"});W.style.display=h?"":"none",U.onclick=()=>{let P=U.hasClass("on");U.toggleClass("on",!P),u.scheduleEnabled=!P,W.style.display=P?"none":"",P?u.type="immediate":u.type=u.scheduleMode==="once"?"once":"recurring"};let ue=W.createDiv({cls:"af-form-row"});ue.createDiv({cls:"af-form-label",text:"Mode"});let ge=ue.createEl("select",{cls:"af-form-select"});for(let[P,X]of[["recurring","Recurring"],["once","One-time"]]){let de=ge.createEl("option",{text:X,attr:{value:P}});P===u.scheduleMode&&(de.selected=!0)}let pe=W.createDiv(),Z=W.createDiv();pe.style.display=u.scheduleMode==="recurring"?"":"none",Z.style.display=u.scheduleMode==="once"?"":"none",this.renderInlineSchedule(pe,u);let K=Z.createDiv({cls:"af-form-row"});K.createDiv({cls:"af-form-label",text:"Run at"});let H=u.runAt?this.toDatetimeLocal(new Date(u.runAt)):this.toDatetimeLocal(new Date(Date.now()+36e5)),ne=K.createEl("input",{cls:"af-form-input",attr:{type:"datetime-local",value:H}});u.runAt||(u.runAt=new Date(ne.value).toISOString()),ne.addEventListener("input",()=>{u.runAt=ne.value?new Date(ne.value).toISOString():""}),ge.addEventListener("change",()=>{u.scheduleMode=ge.value,pe.style.display=u.scheduleMode==="recurring"?"":"none",Z.style.display=u.scheduleMode==="once"?"":"none",u.scheduleEnabled&&(u.type=u.scheduleMode==="once"?"once":"recurring")});let ae=W.createDiv({cls:"af-form-row af-form-row-toggle"});ae.createDiv({cls:"af-form-label",text:"Enabled"});let ce=ae.createDiv({cls:`af-agent-card-toggle${a.enabled?" on":""}`});ce.onclick=()=>{let P=ce.hasClass("on");ce.toggleClass("on",!P),u.enabled=!P};let re=W.createDiv({cls:"af-form-row af-form-row-toggle"});re.createDiv({cls:"af-form-label"}).setText("Catch up if missed");let Ae=re.createDiv({cls:`af-agent-card-toggle${u.catchUp?" on":""}`});Ae.onclick=()=>{let P=Ae.hasClass("on");Ae.toggleClass("on",!P),u.catchUp=!P};let Ne=p.createDiv({cls:"af-create-section"}),Ue=Ne.createDiv({cls:"af-create-section-header"}),Pe=Ue.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(Pe,"gauge"),Ue.createSpan({text:"Execution"});let Te=Ne.createDiv({cls:"af-form-row"}),Re=Te.createDiv({cls:"af-form-label",text:"Effort"}),Be=Te.createEl("select",{cls:"af-form-select"}),$e=[["","Agent Default"],["low","Low"],["medium","Medium"],["high","High"],["max","Max"]];for(let[P,X]of $e){let de=Be.createEl("option",{text:X,attr:{value:P}});P===u.effort&&(de.selected=!0)}Be.addEventListener("change",()=>{u.effort=Be.value}),this.addTooltip(Re,"Overrides the agent\u2019s effort level for this task. Higher effort = more thinking tokens spent.");let q=Ne.createDiv({cls:"af-form-row"}),te=q.createDiv({cls:"af-form-label",text:"Model"}),j=q.createDiv({cls:"af-form-field-wrap"}),we=P=>{j.empty();let X=r.agents.find(de=>de.name===P);Ot(j,{value:u.model,adapter:X?.adapter,onChange:de=>{u.model=de},allowInherit:!0,inheritPlaceholder:X?`Inherit from ${X.name}${X.model?` (${X.model})`:""}`:"Inherit from agent"})};we(u.agent),v.addEventListener("change",()=>we(v.value)),this.addTooltip(te,"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 xe=s.createDiv({cls:"af-create-footer"}),ve=xe.createEl("button",{cls:"af-btn-sm danger"});A(ve,"trash-2","af-btn-icon"),ve.appendText(" Delete"),ve.onclick=async()=>{await this.plugin.repository.deleteTask(a.taskId),new b.Notice(`Task "${a.taskId}" deleted.`),await new Promise(P=>setTimeout(P,200)),await this.plugin.refreshFromVault(),this.navigate("kanban")},xe.createDiv({cls:"af-toolbar-spacer"});let De=xe.createEl("button",{cls:"af-btn-sm",text:"Cancel"});De.onclick=()=>this.navigate("task-detail",a.taskId);let J=xe.createEl("button",{cls:"af-btn-sm primary af-create-submit"});A(J,"check","af-btn-icon"),J.appendText(" Save Changes"),J.onclick=async()=>{let P=de=>de.split(",").map(me=>me.trim()).filter(Boolean),X=u.scheduleEnabled?u.scheduleMode==="once"?"once":"recurring":"immediate";if(X==="once"&&!u.runAt){new b.Notice("Pick a date/time for the one-time run.");return}try{await this.plugin.repository.updateTask(a.taskId,{agent:u.agent,type:X,priority:u.priority,schedule:X==="recurring"?u.schedule.trim():"",runAt:X==="once"?u.runAt:"",enabled:u.enabled,catch_up:u.catchUp,effort:u.effort||void 0,model:u.model||"",tags:P(u.tags),body:u.body.trim()}),new b.Notice(`Task "${a.taskId}" updated.`),await this.plugin.refreshFromVault(),this.navigate("task-detail",a.taskId)}catch(de){let me=de instanceof Error?de.message:String(de);new b.Notice(`Failed to update task: ${me}`)}}}renderEditSkillPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),n=this.detailContext;if(!n){this.renderEmptyState(s,"puzzle","No skill selected","");return}let a=this.plugin.runtime.getSnapshot().skills.find(ae=>ae.name===n);if(!a){this.renderEmptyState(s,"puzzle","Skill not found",`Skill "${n}" was not found`);return}let r=s.createDiv({cls:"af-detail-header"}),o=r.createDiv({cls:"af-detail-header-left"}),c=o.createDiv({cls:"af-agent-card-avatar idle"});(0,b.setIcon)(c,"edit");let l=o.createDiv();l.createDiv({cls:"af-detail-header-name",text:`Edit Skill: ${a.name}`}),l.createDiv({cls:"af-detail-header-desc",text:"Modify skill definition"});let d=r.createDiv({cls:"af-detail-header-actions"}),h={description:a.description??"",tags:a.tags.join(", "),body:a.body,toolsBody:a.toolsBody,referencesBody:a.referencesBody,examplesBody:a.examplesBody},u=s.createDiv({cls:"af-create-form"}),p=u.createDiv({cls:"af-create-section"}),m=p.createDiv({cls:"af-create-section-header"}),f=m.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(f,"puzzle"),m.createSpan({text:"Identity"});let y=p.createDiv({cls:"af-form-row"});y.createDiv({cls:"af-form-label",text:"Name"});let w=y.createEl("input",{cls:"af-form-input",attr:{type:"text",value:a.name,disabled:"true"}});w.style.opacity="0.6",this.createFormField(p,"Description","Manage tasks and projects via CLI","",ae=>{h.description=ae},a.description??""),this.createFormField(p,"Tags","productivity, tasks","Comma-separated",ae=>{h.tags=ae},a.tags.join(", "));let k=u.createDiv({cls:"af-create-section"}),g=k.createDiv({cls:"af-create-section-header"}),v=g.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(v,"file-text"),g.createSpan({text:"Core Instructions"});let S=k.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Skill instructions...",rows:"10"}});S.value=a.body,S.addEventListener("input",()=>{h.body=S.value});let T=u.createDiv({cls:"af-create-section"}),_=T.createDiv({cls:"af-create-section-header"}),D=_.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(D,"wrench");let E=_.createSpan({text:"Tools"});this.addTooltip(E,"CLI commands, API endpoints, and tool definitions available to agents using this skill");let C=T.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`## Commands
|
|
11919
12088
|
|
|
11920
12089
|
### list
|
|
11921
|
-
...`,rows:"8"}});
|
|
11922
|
-
...`,rows:"6"}});ue.value=n.examplesBody,ue.addEventListener("input",()=>{h.examplesBody=ue.value});let ee=s.createDiv({cls:"af-create-footer"}),V=ee.createEl("button",{cls:"af-btn-sm danger"});D(V,"trash-2","af-btn-icon"),V.appendText(" Delete"),V.onclick=async()=>{await this.plugin.repository.deleteSkill(n.name),new w.Notice(`Skill "${n.name}" deleted.`),await new Promise(ce=>setTimeout(ce,200)),await this.plugin.refreshFromVault(),this.navigate("skills")},ee.createDiv({cls:"af-toolbar-spacer"});let z=ee.createEl("button",{cls:"af-btn-sm",text:"Cancel"});z.onclick=()=>this.navigate("skills");let Q=ee.createEl("button",{cls:"af-btn-sm primary af-create-submit"});D(Q,"check","af-btn-icon"),Q.appendText(" Save Changes"),Q.onclick=async()=>{let ce=ne=>ne.split(",").map(de=>de.trim()).filter(Boolean);try{await this.plugin.repository.updateSkill(n.name,{description:h.description.trim(),tags:ce(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(ne){let de=ne instanceof Error?ne.message:String(ne);new w.Notice(`Failed to update skill: ${de}`)}}}renderMcpPage(e){let s=e.createDiv({cls:"af-agents-page"}),a=s.createDiv({cls:"af-agents-toolbar"});a.createDiv({cls:"af-page-title",text:"MCP Servers"}),a.createDiv({cls:"af-toolbar-spacer"});let n=a.createEl("button",{cls:"af-btn-sm primary"});D(n,"plus","af-btn-icon"),n.appendText(" Add Server"),n.onclick=()=>this.navigate("add-mcp-server");let i=a.createEl("button",{cls:"af-btn-sm"});D(i,"refresh-cw","af-btn-icon"),i.appendText(" Refresh"),i.onclick=()=>{this.plugin.mcpManager.invalidateCache(),this.render()};let o=this.plugin.mcpManager.getCachedServers();if(o===null){let 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"}),f=c.createDiv({cls:"af-mcp-progress-bar"}).createDiv({cls:"af-mcp-progress-fill"});f.style.width="15%";let m=c.createDiv({cls:"af-mcp-progress-detail",text:"Scanning for configured servers\u2026"}),g=this.plugin.mcpManager.onProgress(k=>{switch(k.phase){case"list":u.setText("Scanning servers\u2026"),m.setText(k.message),f.style.width="20%";break;case"details":u.setText(`Checking server ${k.current}/${k.total}\u2026`),m.setText(k.serverName),f.style.width=`${20+k.current/k.total*30}%`;break;case"tools":u.setText("Discovering tools\u2026"),m.setText(k.message),f.style.width="60%",f.addClass("af-mcp-progress-fill-slow");break;case"done":f.style.width="100%",u.setText("Done"),m.setText(`${k.serverCount} server${k.serverCount!==1?"s":""}, ${k.toolCount} tool${k.toolCount!==1?"s":""} discovered`);break}});this.streamingUnsubscribes.push(g),this.plugin.mcpManager.getServers().then(()=>void this.render()).catch(()=>void this.render());return}if(o.length===0){this.renderEmptyState(s,"plug","No MCP servers found","Click 'Add Server' above or use 'claude mcp add' in the terminal");return}let 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=v=>{v.stopPropagation(),this.plugin.mcpManager.toggleServerEnabled(s.name,!s.enabled).then(()=>{this.plugin.mcpManager.invalidateCache(),this.render()})};let h=a.createDiv({cls:`af-mcp-status-badge ${s.enabled?s.status:"disabled"}`}),u=h.createSpan();if(s.enabled?((0,w.setIcon)(u,s.status==="connected"?"check-circle":s.status==="needs-auth"?"alert-circle":"x-circle"),h.createSpan({text:s.status==="connected"?" Connected":s.status==="needs-auth"?" Needs auth":s.status==="error"?" Error":" Disconnected"})):((0,w.setIcon)(u,"pause"),h.createSpan({text:" Disabled"})),s.description){let v=this.truncateDescription(s.description,120);a.createDiv({cls:"af-mcp-description",text:v})}let p=s.url??s.command??"";p&&a.createDiv({cls:"af-mcp-command",text:Ft(p,60)});let f=s.toolDetails.length>0?`${s.toolDetails.length} tools`:s.tools.length>0?`${s.tools.length} tools`:"No tools discovered",m=a.createDiv({cls:"af-mcp-tool-footer"}),g=m.createDiv({cls:"af-mcp-tool-count"}),k=g.createSpan();(0,w.setIcon)(k,"wrench"),g.createSpan({text:` ${f}`});let b=s.toolDetails.length>0?s.toolDetails.map(v=>v.name):s.tools;if(b.length>0){let v=m.createDiv({cls:"af-mcp-tool-chips"}),y=b.slice(0,4);for(let x of y)v.createSpan({cls:"af-mcp-tool-chip",text:x});b.length>4&&v.createSpan({cls:"af-mcp-tool-chip af-mcp-tool-chip-more",text:`+${b.length-4}`})}if(this.authenticatingServers.has(s.name)){let 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 v=a.createDiv({cls:"af-mcp-hint-row"});v.createSpan({text:"Tools available to agents via Claude \u2014 "});let y=v.createEl("a",{cls:"af-link",text:"discover tools"});y.onclick=x=>{x.stopPropagation(),x.preventDefault(),this.authenticateMcpServer(s)}}a.onclick=()=>this.openMcpDetailSlideover(s)}async authenticateMcpServer(e){if(!e.url){new w.Notice("No URL found for this server \u2014 can't authenticate.");return}this.authenticatingServers.add(e.name),this.render(),new w.Notice(`Authenticating ${e.name}\u2026 Complete authorization in your browser.`,1e4);try{let s=e.type==="sse"?"sse":"http";await this.plugin.mcpManager.authenticateServer(e.name,e.url,s),new w.Notice(`${e.name} authenticated successfully!`),await this.plugin.mcpManager.getServers(!0)}catch(s){let a=s instanceof Error?s.message:String(s);new w.Notice(`Authentication failed: ${a}`,8e3)}finally{this.authenticatingServers.delete(e.name),this.render()}}truncateDescription(e,s){let a=me(e)[0]??e,n=a.split(/(?<=[.!?])\s/)[0]??a,i=n.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=f=>{f.target===s&&s.remove()};let o=a.createDiv({cls:"af-slideover-body"});if(e.description){let f=o.createDiv({cls:"af-slideover-section"});f.createDiv({cls:"af-slideover-section-title",text:"DESCRIPTION"}),f.createDiv({cls:"af-mcp-detail-description",text:e.description})}let 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 f of e.toolDetails){let m=c.createDiv({cls:"af-mcp-tool-detail"}),g=m.createDiv({cls:"af-mcp-tool-detail-header"}),k=g.createSpan({cls:"af-mcp-tool-detail-name"}),b=k.createSpan();if((0,w.setIcon)(b,"wrench"),k.createSpan({text:` ${f.name}`}),f.inputSchema){let v=f.inputSchema.required??[];v.length>0&&g.createSpan({cls:"af-mcp-tool-param-count",text:`${v.length} param${v.length!==1?"s":""}`})}if(f.description){let v=me(f.description).filter(T=>T.trim()),y=v.slice(0,2).join(" ").trim();if(v.length>2){let T=m.createEl("details",{cls:"af-mcp-tool-detail-desc"});T.createEl("summary",{text:this.truncateDescription(y,200)}),T.createDiv({cls:"af-mcp-tool-detail-full",text:f.description})}else m.createDiv({cls:"af-mcp-tool-detail-desc",text:y})}if(f.inputSchema){let v=f.inputSchema.properties,y=new Set(f.inputSchema.required??[]);if(v&&Object.keys(v).length>0){let x=m.createDiv({cls:"af-mcp-tool-params"});for(let[T,_]of Object.entries(v)){let R=x.createDiv({cls:"af-mcp-tool-param"});R.createSpan({cls:"af-mcp-tool-param-name",text:T}),_.type&&R.createSpan({cls:"af-mcp-tool-param-type",text:_.type}),y.has(T)&&R.createSpan({cls:"af-mcp-tool-param-required",text:"required"}),_.description&&R.createSpan({cls:"af-mcp-tool-param-desc",text:Ft(_.description,80)})}}}}else if(e.tools.length>0)for(let f of e.tools)c.createDiv({cls:"af-mcp-tool-item",text:f});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 f=h.createEl("button",{cls:"af-btn-sm primary"}),m=f.createSpan();(0,w.setIcon)(m,"key"),f.appendText(" Authenticate"),f.onclick=()=>{s.remove(),this.authenticateMcpServer(e)}}let u=h.createEl("button",{cls:"af-btn-sm danger"}),p=u.createSpan();(0,w.setIcon)(p,"trash-2"),u.appendText(" Remove Server"),u.onclick=async()=>{try{let f=e.scope==="user"?"user":void 0;await this.plugin.mcpManager.removeServer(e.name,f),new w.Notice(`Server "${e.name}" removed.`),s.remove(),this.render()}catch(f){let m=f instanceof Error?f.message:String(f);new w.Notice(`Failed to remove server: ${m}`)}}}renderAddMcpServerPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),a=s.createDiv({cls:"af-detail-header"}),n=a.createDiv({cls:"af-detail-header-left"}),i=n.createDiv({cls:"af-agent-card-avatar idle"});(0,w.setIcon)(i,"plus");let o=n.createDiv();o.createDiv({cls:"af-detail-header-name",text:"Add MCP Server"}),o.createDiv({cls:"af-detail-header-desc",text:"Register a new MCP server for agents to use"}),a.createDiv({cls:"af-detail-header-actions"});let 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",$=>{l.name=$});let p=d.createDiv({cls:"af-form-row"}),f=p.createDiv({cls:"af-form-label"});f.setText("Transport"),this.addTooltip(f,"stdio: local process, http/sse: remote server");let m=p.createEl("select",{cls:"af-form-select"});m.createEl("option",{text:"stdio",attr:{value:"stdio"}}),m.createEl("option",{text:"http",attr:{value:"http"}}),m.createEl("option",{text:"sse",attr:{value:"sse"}});let g=d.createDiv({cls:"af-form-row"}),k=g.createDiv({cls:"af-form-label"});k.setText("Scope"),this.addTooltip(k,"local: this project only, user: available across all projects");let b=g.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 v=c.createDiv({cls:"af-create-section"}),y=v.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(v,"Command","npx @anthropic-ai/mcp-server-memory","The command to run",$=>{l.command=$}),this.createFormField(v,"Arguments","--port 3000","Space-separated arguments (optional)",$=>{l.args=$});let T=v.createDiv({cls:"af-form-label"});T.setText("Environment variables"),this.addTooltip(T,"One KEY=VALUE per line");let _=v.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`API_KEY=sk-...
|
|
11923
|
-
DEBUG=true`,rows:"3"}});_.addEventListener("input",()=>{l.envVars=_.value});let R=c.createDiv({cls:"af-create-section"}),A=R.createDiv({cls:"af-create-section-header"}),S=A.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(S,"globe"),A.createSpan({text:"Remote Server Configuration"}),this.createFormField(R,"URL","https://mcp.example.com/sse","Server endpoint URL",$=>{l.url=$});let L=R.createDiv({cls:"af-form-label"});L.setText("Custom headers"),this.addTooltip(L,"One Header: Value per line (optional)");let E=R.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"X-Custom-Header: value",rows:"3"}});E.addEventListener("input",()=>{l.headers=E.value});let N=()=>{v.style.display=l.transport==="stdio"?"":"none",R.style.display=l.transport!=="stdio"?"":"none"};m.addEventListener("change",()=>{l.transport=m.value,N()}),N();let I=s.createDiv({cls:"af-create-footer"}),q=I.createEl("button",{cls:"af-btn-sm",text:"Cancel"});q.onclick=()=>this.navigate("mcp");let F=I.createEl("button",{cls:"af-btn-sm primary af-create-submit"});D(F,"plus","af-btn-icon"),F.appendText(" Add Server"),F.onclick=async()=>{let $=l.name.trim();if(!$){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 W={};if(l.envVars.trim())for(let ee of me(l.envVars)){let V=ee.trim();if(!V)continue;let z=V.indexOf("=");if(z<=0){new w.Notice(`Invalid env var: ${V}`);return}W[V.slice(0,z)]=V.slice(z+1)}let ie={};if(l.headers.trim())for(let ee of me(l.headers)){let V=ee.trim();if(!V)continue;let z=V.indexOf(":");if(z<=0){new w.Notice(`Invalid header: ${V}`);return}ie[V.slice(0,z).trim()]=V.slice(z+1).trim()}let ue=l.args.trim()?l.args.trim().split(/\s+/):void 0;F.disabled=!0,F.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"?ue: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(ie).length>0?ie:void 0}),new w.Notice(`Server "${$}" added successfully.`),this.navigate("mcp")}catch(ee){let V=ee instanceof Error?ee.message:String(ee);new w.Notice(`Failed to add server: ${V}`),F.disabled=!1,F.setText(""),D(F,"plus","af-btn-icon"),F.appendText(" Add Server")}}}createFormField(e,s,a,n,i,o){let 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 Vi(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 Ya(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 dl(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 hl(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 X=require("obsidian");var Jt=class r extends X.ItemView{constructor(e,s){super(e);this.plugin=s}selectedAgentName=null;sessions=new Map;headerEl;agentSelect;messagesEl;messagesInner;textarea;sendBtn;attachStopBtn;isInStopMode=!1;attachedFiles=[];attachedImages=[];pillsRow;activityEl=null;streamingDot=null;activityUnsub=null;statsEl;statsUnsub=null;statsSourceSession=null;threadExpanded=new Map;getViewType(){return nt}getDisplayText(){return this.selectedAgentName?`Chat: ${this.selectedAgentName}`:"Agent Chat"}getIcon(){return"message-circle"}getState(){return{agentName:this.selectedAgentName??null}}async setState(e,s){await super.setState(e,s),e?.agentName&&typeof e.agentName=="string"&&this.selectAgent(e.agentName)}async onOpen(){this.plugin.subscribeView(this),this.buildShell(),await this.render()}async onClose(){for(let{session:e}of this.sessions.values())e.abort();this.sessions.clear(),this.statsUnsub?.(),this.statsUnsub=null,this.activityUnsub?.(),this.activityUnsub=null,this.plugin.unsubscribeView(this)}selectAgent(e){let s=this.plugin.app.workspace.getLeavesOfType(nt);for(let a of s)if(a.view!==this&&a.view instanceof r&&a.view.selectedAgentName===e){this.plugin.app.workspace.revealLeaf(a);return}this.selectedAgentName=e,this.agentSelect&&(this.agentSelect.value=e),this.leaf.updateHeader(),this.switchToAgent(e)}buildShell(){let e=this.contentEl;e.empty(),e.addClass("af-root");let s=e.createDiv({cls:"af-chat-view-container"});this.headerEl=s.createDiv({cls:"af-chat-view-header"}),this.agentSelect=this.headerEl.createEl("select",{cls:"af-chat-view-agent-select"});let a=this.headerEl.createEl("button",{cls:"af-btn-sm af-chat-view-new-btn"});D(a,"plus","af-btn-icon"),a.appendText(" New Chat"),a.onclick=()=>void this.handleNewChat(),this.agentSelect.onchange=()=>{let l=this.agentSelect.value;if(l){let c=this.plugin.app.workspace.getLeavesOfType(nt);for(let d of c)if(d.view!==this&&d.view instanceof r&&d.view.selectedAgentName===l){this.plugin.app.workspace.revealLeaf(d),this.agentSelect.value=this.selectedAgentName??"";return}this.textarea.disabled=!1,this.textarea.placeholder="Message the agent\u2026 (Ctrl+Enter to send)",this.switchToAgent(l)}},this.messagesEl=s.createDiv({cls:"af-chat-messages"}),this.messagesInner=this.messagesEl.createDiv({cls:"af-chat-messages-inner"});let n=s.createDiv({cls:"af-chat-input-area"});this.pillsRow=n.createDiv({cls:"af-chat-pills-row"}),this.pillsRow.style.display="none";let i=n.createDiv({cls:"af-chat-input-row"});this.attachStopBtn=i.createEl("button",{cls:"af-chat-attach-btn"}),D(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"}),D(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: ${Kt(e.contextTokensUsed)} / ${Kt(e.contextWindow)} tokens (${a}%)
|
|
12090
|
+
...`,rows:"8"}});C.value=a.toolsBody,C.addEventListener("input",()=>{h.toolsBody=C.value});let L=u.createDiv({cls:"af-create-section"}),R=L.createDiv({cls:"af-create-section-header"}),B=R.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(B,"book-open");let F=R.createSpan({text:"References"});this.addTooltip(F,"Background docs, conventions, cheat sheets");let Y=L.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"API docs, filter syntax, conventions...",rows:"6"}});Y.value=a.referencesBody,Y.addEventListener("input",()=>{h.referencesBody=Y.value});let U=u.createDiv({cls:"af-create-section"}),W=U.createDiv({cls:"af-create-section-header"}),ue=W.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(ue,"message-circle");let ge=W.createSpan({text:"Examples"});this.addTooltip(ge,"Example prompts and ideal outputs showing how to use this skill");let pe=U.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`## Example: List all tasks
|
|
12091
|
+
...`,rows:"6"}});pe.value=a.examplesBody,pe.addEventListener("input",()=>{h.examplesBody=pe.value});let Z=s.createDiv({cls:"af-create-footer"}),K=Z.createEl("button",{cls:"af-btn-sm danger"});A(K,"trash-2","af-btn-icon"),K.appendText(" Delete"),K.onclick=async()=>{await this.plugin.repository.deleteSkill(a.name),new b.Notice(`Skill "${a.name}" deleted.`),await new Promise(ae=>setTimeout(ae,200)),await this.plugin.refreshFromVault(),this.navigate("skills")},Z.createDiv({cls:"af-toolbar-spacer"});let H=Z.createEl("button",{cls:"af-btn-sm",text:"Cancel"});H.onclick=()=>this.navigate("skills");let ne=Z.createEl("button",{cls:"af-btn-sm primary af-create-submit"});A(ne,"check","af-btn-icon"),ne.appendText(" Save Changes"),ne.onclick=async()=>{let ae=ce=>ce.split(",").map(re=>re.trim()).filter(Boolean);try{await this.plugin.repository.updateSkill(a.name,{description:h.description.trim(),tags:ae(h.tags),body:h.body.trim(),toolsBody:h.toolsBody.trim(),referencesBody:h.referencesBody.trim(),examplesBody:h.examplesBody.trim()}),new b.Notice(`Skill "${a.name}" updated.`),await this.plugin.refreshFromVault(),this.navigate("skills")}catch(ce){let re=ce instanceof Error?ce.message:String(ce);new b.Notice(`Failed to update skill: ${re}`)}}}renderMcpPage(e){let s=e.createDiv({cls:"af-agents-page"}),n=s.createDiv({cls:"af-agents-toolbar"});n.createDiv({cls:"af-page-title",text:"MCP Servers"}),n.createDiv({cls:"af-toolbar-spacer"});let a=n.createEl("button",{cls:"af-btn-sm primary"});A(a,"plus","af-btn-icon"),a.appendText(" Add Server"),a.onclick=()=>this.navigate("add-mcp-server");let r=n.createEl("button",{cls:"af-btn-sm"});A(r,"refresh-cw","af-btn-icon"),r.appendText(" Refresh"),r.onclick=()=>{this.plugin.mcpManager.invalidateCache(),this.render()};let o=this.plugin.mcpManager.getCachedServers();if(o===null){let l=s.createDiv({cls:"af-mcp-progress"}),d=l.createDiv({cls:"af-mcp-progress-header"}),h=d.createDiv({cls:"af-mcp-spinner"});for(let w=0;w<3;w++)h.createSpan();let u=d.createSpan({cls:"af-mcp-progress-label",text:"Discovering MCP servers\u2026"}),m=l.createDiv({cls:"af-mcp-progress-bar"}).createDiv({cls:"af-mcp-progress-fill"});m.style.width="15%";let f=l.createDiv({cls:"af-mcp-progress-detail",text:"Scanning for configured servers\u2026"}),y=this.plugin.mcpManager.onProgress(w=>{switch(w.phase){case"list":u.setText("Scanning servers\u2026"),f.setText(w.message),m.style.width="20%";break;case"details":u.setText(`Checking server ${w.current}/${w.total}\u2026`),f.setText(w.serverName),m.style.width=`${20+w.current/w.total*30}%`;break;case"tools":u.setText("Discovering tools\u2026"),f.setText(w.message),m.style.width="60%",m.addClass("af-mcp-progress-fill-slow");break;case"done":m.style.width="100%",u.setText("Done"),f.setText(`${w.serverCount} server${w.serverCount!==1?"s":""}, ${w.toolCount} tool${w.toolCount!==1?"s":""} discovered`);break}});this.streamingUnsubscribes.push(y),this.plugin.mcpManager.getServers().then(()=>void this.render()).catch(()=>void this.render());return}if(o.length===0){this.renderEmptyState(s,"plug","No MCP servers found","Click 'Add Server' above or use 'claude mcp add' in the terminal");return}let c=s.createDiv({cls:"af-agents-grid"});for(let l of o)this.renderMcpCard(c,l)}renderMcpCard(e,s){let n=e.createDiv({cls:`af-mcp-card${s.enabled?"":" af-mcp-card-disabled"}`}),a=n.createDiv({cls:"af-agent-card-header"}),r=s.enabled?s.status==="connected"?"idle":s.status==="needs-auth"?"pending":"error":"disabled",o=a.createDiv({cls:`af-agent-card-avatar ${r}`});(0,b.setIcon)(o,"plug");let c=a.createDiv({cls:"af-agent-card-titleblock"});c.createDiv({cls:"af-agent-card-name",text:s.name});let l=c.createDiv({cls:"af-agent-card-desc af-mcp-meta"});l.createSpan({cls:"af-mcp-type-badge",text:s.type}),s.scope!=="unknown"&&l.createSpan({cls:"af-badge",text:s.scope});let d=a.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=n.createDiv({cls:`af-mcp-status-badge ${s.enabled?s.status:"disabled"}`}),u=h.createSpan();if(s.enabled?((0,b.setIcon)(u,s.status==="connected"?"check-circle":s.status==="needs-auth"?"alert-circle":"x-circle"),h.createSpan({text:s.status==="connected"?" Connected":s.status==="needs-auth"?" Needs auth":s.status==="error"?" Error":" Disconnected"})):((0,b.setIcon)(u,"pause"),h.createSpan({text:" Disabled"})),s.description){let g=this.truncateDescription(s.description,120);n.createDiv({cls:"af-mcp-description",text:g})}let p=s.url??s.command??"";p&&n.createDiv({cls:"af-mcp-command",text:Vt(p,60)});let m=s.toolDetails.length>0?`${s.toolDetails.length} tools`:s.tools.length>0?`${s.tools.length} tools`:"No tools discovered",f=n.createDiv({cls:"af-mcp-tool-footer"}),y=f.createDiv({cls:"af-mcp-tool-count"}),w=y.createSpan();(0,b.setIcon)(w,"wrench"),y.createSpan({text:` ${m}`});let k=s.toolDetails.length>0?s.toolDetails.map(g=>g.name):s.tools;if(k.length>0){let g=f.createDiv({cls:"af-mcp-tool-chips"}),v=k.slice(0,4);for(let S of v)g.createSpan({cls:"af-mcp-tool-chip",text:S});k.length>4&&g.createSpan({cls:"af-mcp-tool-chip af-mcp-tool-chip-more",text:`+${k.length-4}`})}if(this.authenticatingServers.has(s.name)){let v=n.createDiv({cls:"af-mcp-auth-row"}).createEl("button",{cls:"af-btn-sm primary",attr:{disabled:"true"}}),S=v.createSpan({cls:"af-spin"});(0,b.setIcon)(S,"loader-2"),v.appendText(" Authenticating\u2026")}else if(s.enabled&&s.status==="needs-auth"){let v=n.createDiv({cls:"af-mcp-auth-row"}).createEl("button",{cls:"af-btn-sm primary"}),S=v.createSpan();(0,b.setIcon)(S,"key"),v.appendText(" Authenticate"),v.onclick=T=>{T.stopPropagation(),this.authenticateMcpServer(s)}}else if(s.enabled&&s.status==="connected"&&s.type!=="stdio"&&s.toolDetails.length===0){let g=n.createDiv({cls:"af-mcp-hint-row"});g.createSpan({text:"Tools available to agents via Claude \u2014 "});let v=g.createEl("a",{cls:"af-link",text:"discover tools"});v.onclick=S=>{S.stopPropagation(),S.preventDefault(),this.authenticateMcpServer(s)}}n.onclick=()=>this.openMcpDetailSlideover(s)}async authenticateMcpServer(e){if(!e.url){new b.Notice("No URL found for this server \u2014 can't authenticate.");return}this.authenticatingServers.add(e.name),this.render(),new b.Notice(`Authenticating ${e.name}\u2026 Complete authorization in your browser.`,1e4);try{let s=e.type==="sse"?"sse":"http";await this.plugin.mcpManager.authenticateServer(e.name,e.url,s),new b.Notice(`${e.name} authenticated successfully!`),await this.plugin.mcpManager.getServers(!0)}catch(s){let n=s instanceof Error?s.message:String(s);new b.Notice(`Authentication failed: ${n}`,8e3)}finally{this.authenticatingServers.delete(e.name),this.render()}}truncateDescription(e,s){let n=oe(e)[0]??e,a=n.split(/(?<=[.!?])\s/)[0]??n,r=a.length<n.length?a:n;return r.length<=s?r:r.slice(0,s-1)+"\u2026"}openMcpDetailSlideover(e){this.contentEl.querySelector(".af-slideover-overlay")?.remove();let s=this.contentEl.createDiv({cls:"af-slideover-overlay"}),n=s.createDiv({cls:"af-slideover"}),a=n.createDiv({cls:"af-slideover-header"});a.createDiv({cls:"af-slideover-title",text:e.name});let r=a.createEl("button",{cls:"clickable-icon"});(0,b.setIcon)(r,"cross"),r.onclick=()=>s.remove(),s.onclick=m=>{m.target===s&&s.remove()};let o=n.createDiv({cls:"af-slideover-body"});if(e.description){let m=o.createDiv({cls:"af-slideover-section"});m.createDiv({cls:"af-slideover-section-title",text:"DESCRIPTION"}),m.createDiv({cls:"af-mcp-detail-description",text:e.description})}let c=o.createDiv({cls:"af-slideover-section"});c.createDiv({cls:"af-slideover-section-title",text:"SERVER INFO"}),this.renderDetailRow(c,"Name",e.name),this.renderDetailRow(c,"Type",e.type),this.renderDetailRow(c,"Status",e.status),this.renderDetailRow(c,"Scope",e.scope),e.url&&this.renderDetailRow(c,"URL",e.url),e.command&&this.renderDetailRow(c,"Command",e.command),e.args&&this.renderDetailRow(c,"Args",e.args);let l=o.createDiv({cls:"af-slideover-section"}),d=e.toolDetails.length||e.tools.length;if(l.createDiv({cls:"af-slideover-section-title",text:`AVAILABLE TOOLS (${d})`}),e.toolDetails.length>0)for(let m of e.toolDetails){let f=l.createDiv({cls:"af-mcp-tool-detail"}),y=f.createDiv({cls:"af-mcp-tool-detail-header"}),w=y.createSpan({cls:"af-mcp-tool-detail-name"}),k=w.createSpan();if((0,b.setIcon)(k,"wrench"),w.createSpan({text:` ${m.name}`}),m.inputSchema){let g=m.inputSchema.required??[];g.length>0&&y.createSpan({cls:"af-mcp-tool-param-count",text:`${g.length} param${g.length!==1?"s":""}`})}if(m.description){let g=oe(m.description).filter(T=>T.trim()),v=g.slice(0,2).join(" ").trim();if(g.length>2){let T=f.createEl("details",{cls:"af-mcp-tool-detail-desc"});T.createEl("summary",{text:this.truncateDescription(v,200)}),T.createDiv({cls:"af-mcp-tool-detail-full",text:m.description})}else f.createDiv({cls:"af-mcp-tool-detail-desc",text:v})}if(m.inputSchema){let g=m.inputSchema.properties,v=new Set(m.inputSchema.required??[]);if(g&&Object.keys(g).length>0){let S=f.createDiv({cls:"af-mcp-tool-params"});for(let[T,_]of Object.entries(g)){let D=S.createDiv({cls:"af-mcp-tool-param"});D.createSpan({cls:"af-mcp-tool-param-name",text:T}),_.type&&D.createSpan({cls:"af-mcp-tool-param-type",text:_.type}),v.has(T)&&D.createSpan({cls:"af-mcp-tool-param-required",text:"required"}),_.description&&D.createSpan({cls:"af-mcp-tool-param-desc",text:Vt(_.description,80)})}}}}else if(e.tools.length>0)for(let m of e.tools)l.createDiv({cls:"af-mcp-tool-item",text:m});else l.createDiv({cls:"af-form-hint",text:e.status==="connected"?"No tools reported by this server.":"Connect to this server to discover available tools."});let h=o.createDiv({cls:"af-slideover-section"});if(h.createDiv({cls:"af-slideover-section-title",text:"ACTIONS"}),e.enabled&&e.status==="needs-auth"&&e.url){let m=h.createEl("button",{cls:"af-btn-sm primary"}),f=m.createSpan();(0,b.setIcon)(f,"key"),m.appendText(" Authenticate"),m.onclick=()=>{s.remove(),this.authenticateMcpServer(e)}}let u=h.createEl("button",{cls:"af-btn-sm danger"}),p=u.createSpan();(0,b.setIcon)(p,"trash-2"),u.appendText(" Remove Server"),u.onclick=async()=>{try{let m=e.scope==="user"?"user":void 0;await this.plugin.mcpManager.removeServer(e.name,m),new b.Notice(`Server "${e.name}" removed.`),s.remove(),this.render()}catch(m){let f=m instanceof Error?m.message:String(m);new b.Notice(`Failed to remove server: ${f}`)}}}renderAddMcpServerPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),n=s.createDiv({cls:"af-detail-header"}),a=n.createDiv({cls:"af-detail-header-left"}),r=a.createDiv({cls:"af-agent-card-avatar idle"});(0,b.setIcon)(r,"plus");let o=a.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"}),n.createDiv({cls:"af-detail-header-actions"});let c={name:"",transport:"stdio",scope:"user",command:"",args:"",envVars:"",url:"",headers:""},l=s.createDiv({cls:"af-create-form"}),d=l.createDiv({cls:"af-create-section"}),h=d.createDiv({cls:"af-create-section-header"}),u=h.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(u,"plug"),h.createSpan({text:"Server Details"}),this.createFormField(d,"Name","my-server","Unique name for this MCP server",W=>{c.name=W});let p=d.createDiv({cls:"af-form-row"}),m=p.createDiv({cls:"af-form-label"});m.setText("Transport"),this.addTooltip(m,"stdio: local process, http/sse: remote server");let f=p.createEl("select",{cls:"af-form-select"});f.createEl("option",{text:"stdio",attr:{value:"stdio"}}),f.createEl("option",{text:"http",attr:{value:"http"}}),f.createEl("option",{text:"sse",attr:{value:"sse"}});let y=d.createDiv({cls:"af-form-row"}),w=y.createDiv({cls:"af-form-label"});w.setText("Scope"),this.addTooltip(w,"local: this project only, user: available across all projects");let k=y.createEl("select",{cls:"af-form-select"});k.createEl("option",{text:"user",attr:{value:"user"}}),k.createEl("option",{text:"local",attr:{value:"local"}}),k.addEventListener("change",()=>{c.scope=k.value});let g=l.createDiv({cls:"af-create-section"}),v=g.createDiv({cls:"af-create-section-header"}),S=v.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(S,"terminal"),v.createSpan({text:"Process Configuration"}),this.createFormField(g,"Command","npx @anthropic-ai/mcp-server-memory","The command to run",W=>{c.command=W}),this.createFormField(g,"Arguments","--port 3000","Space-separated arguments (optional)",W=>{c.args=W});let T=g.createDiv({cls:"af-form-label"});T.setText("Environment variables"),this.addTooltip(T,"One KEY=VALUE per line");let _=g.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`API_KEY=sk-...
|
|
12092
|
+
DEBUG=true`,rows:"3"}});_.addEventListener("input",()=>{c.envVars=_.value});let D=l.createDiv({cls:"af-create-section"}),E=D.createDiv({cls:"af-create-section-header"}),C=E.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(C,"globe"),E.createSpan({text:"Remote Server Configuration"}),this.createFormField(D,"URL","https://mcp.example.com/sse","Server endpoint URL",W=>{c.url=W});let L=D.createDiv({cls:"af-form-label"});L.setText("Custom headers"),this.addTooltip(L,"One Header: Value per line (optional)");let R=D.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"X-Custom-Header: value",rows:"3"}});R.addEventListener("input",()=>{c.headers=R.value});let B=()=>{g.style.display=c.transport==="stdio"?"":"none",D.style.display=c.transport!=="stdio"?"":"none"};f.addEventListener("change",()=>{c.transport=f.value,B()}),B();let F=s.createDiv({cls:"af-create-footer"}),Y=F.createEl("button",{cls:"af-btn-sm",text:"Cancel"});Y.onclick=()=>this.navigate("mcp");let U=F.createEl("button",{cls:"af-btn-sm primary af-create-submit"});A(U,"plus","af-btn-icon"),U.appendText(" Add Server"),U.onclick=async()=>{let W=c.name.trim();if(!W){new b.Notice("Server name is required.");return}if(c.transport==="stdio"){if(!c.command.trim()){new b.Notice("Command is required for stdio servers.");return}}else if(!c.url.trim()){new b.Notice("URL is required for HTTP/SSE servers.");return}let ue={};if(c.envVars.trim())for(let Z of oe(c.envVars)){let K=Z.trim();if(!K)continue;let H=K.indexOf("=");if(H<=0){new b.Notice(`Invalid env var: ${K}`);return}ue[K.slice(0,H)]=K.slice(H+1)}let ge={};if(c.headers.trim())for(let Z of oe(c.headers)){let K=Z.trim();if(!K)continue;let H=K.indexOf(":");if(H<=0){new b.Notice(`Invalid header: ${K}`);return}ge[K.slice(0,H).trim()]=K.slice(H+1).trim()}let pe=c.args.trim()?c.args.trim().split(/\s+/):void 0;U.disabled=!0,U.setText("Adding...");try{await this.plugin.mcpManager.addServer({name:W,transport:c.transport,scope:c.scope,command:c.transport==="stdio"?c.command.trim():void 0,args:c.transport==="stdio"?pe:void 0,envVars:c.transport==="stdio"&&Object.keys(ue).length>0?ue:void 0,url:c.transport!=="stdio"?c.url.trim():void 0,headers:c.transport!=="stdio"&&Object.keys(ge).length>0?ge:void 0}),new b.Notice(`Server "${W}" added successfully.`),this.navigate("mcp")}catch(Z){let K=Z instanceof Error?Z.message:String(Z);new b.Notice(`Failed to add server: ${K}`),U.disabled=!1,U.setText(""),A(U,"plus","af-btn-icon"),U.appendText(" Add Server")}}}createFormField(e,s,n,a,r,o){let c=e.createDiv({cls:"af-form-row"}),l=c.createDiv({cls:"af-form-label"});l.setText(s),a&&this.addTooltip(l,a);let d=c.createEl("input",{cls:"af-form-input",attr:{type:"text",placeholder:n}});o!==void 0&&(d.value=o),d.addEventListener("input",()=>r(d.value))}addTooltip(e,s){let n=e.createSpan({cls:"af-form-tooltip"});(0,b.setIcon)(n,"info"),n.createSpan({cls:"af-tooltip-text",text:s})}};function ko(i){switch(i){case"connected":return"idle";case"connecting":case"reconnecting":return"pending";case"needs-auth":case"error":return"error";case"stopped":case"disabled":default:return"disabled"}}function $a(i){return i>=1e6?`${(i/1e6).toFixed(1)}M`:i>=1e4?`${Math.round(i/1e3)}K`:i>=1e3?`${(i/1e3).toFixed(1)}K`:String(i)}function Sd(i){if(!i?.trim())return"not set";let t={"*/5 * * * *":"Every 5 minutes","*/10 * * * *":"Every 10 minutes","*/15 * * * *":"Every 15 minutes","*/30 * * * *":"Every 30 minutes","0 * * * *":"Every hour","0 */2 * * *":"Every 2 hours","0 */4 * * *":"Every 4 hours","0 */6 * * *":"Every 6 hours","0 */12 * * *":"Every 12 hours"};if(t[i])return t[i];let e=i.trim().split(/\s+/);if(e.length===5){let[s,n,a,,r]=e;if(a==="*"&&n&&s){let o=Number(n),c=Number(s);if(!isNaN(o)&&!isNaN(c)){let l=o>=12?"PM":"AM",h=`${o===0?12:o>12?o-12:o}:${String(c).padStart(2,"0")} ${l}`;return r==="*"?`Daily at ${h}`:r==="1-5"?`Weekdays at ${h}`:`${h} on days ${r}`}}}return i}function Cd(i){switch(i){case"connected":return"green";case"connecting":case"reconnecting":return"blue";case"needs-auth":case"error":return"red";case"stopped":case"disabled":default:return""}}var G=require("obsidian");function jn(i,t){return`${i}::${t}`}function Co(){return Math.random().toString(16).slice(2,10)}function To(){return`New chat ${new Date().toLocaleString(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}`}function Td(i){let t=new Date(i).getTime();if(!Number.isFinite(t))return"";let e=Date.now()-t,s=60*1e3,n=60*s,a=24*n;return e<s?"just now":e<n?`${Math.floor(e/s)}m`:e<a?`${Math.floor(e/n)}h`:e<2*a?"yesterday":e<7*a?`${Math.floor(e/a)}d`:new Date(t).toLocaleDateString(void 0,{month:"short",day:"numeric"})}var _d=480,Ed='<svg class="svg-icon af-convo-toggle-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="1" y="2" width="22" height="20" rx="4"></rect><rect class="af-convo-toggle-bar" x="4" y="5" width="2" height="14" rx="2" fill="currentColor"></rect></svg>',cs=class i extends G.ItemView{constructor(e,s){super(e);this.plugin=s}selectedAgentName=null;selectedConversationId="";sessions=new Map;conversationsCache=[];convoPanelEl=null;collapseBtn=null;convoPanelCollapsed=!1;userToggledCollapse=!1;resizeObserver=null;headerEl;agentSelect;messagesEl;messagesInner;textarea;sendBtn;attachStopBtn;isInStopMode=!1;attachedFiles=[];attachedImages=[];pillsRow;activityEl=null;streamingDot=null;activityUnsub=null;statsEl;statsUnsub=null;statsSourceSession=null;threadExpanded=new Map;getViewType(){return rt}getDisplayText(){if(!this.selectedAgentName)return"Agent Chat";let e=this.getCurrentConversationName();return!e||this.conversationsCache.length<=1?`Chat: ${this.selectedAgentName}`:`Chat: ${this.selectedAgentName} \xB7 ${e}`}getIcon(){return"message-circle"}getState(){return{agentName:this.selectedAgentName??null,conversationId:this.selectedConversationId,convoPanelCollapsed:this.convoPanelCollapsed}}async setState(e,s){await super.setState(e,s);let n=typeof e?.conversationId=="string"&&e.conversationId?e.conversationId:void 0;typeof e?.convoPanelCollapsed=="boolean"&&(this.convoPanelCollapsed=e.convoPanelCollapsed,this.userToggledCollapse=!0,this.applyCollapsedClass()),e?.agentName&&typeof e.agentName=="string"&&this.selectAgent(e.agentName,n)}getCurrentConversationName(){return this.conversationsCache.find(s=>s.id===this.selectedConversationId)?.name??""}async onOpen(){this.plugin.subscribeView(this),this.buildShell(),await this.render(),this.observeContainerWidth()}async onClose(){for(let{session:e}of this.sessions.values())e.abort();this.sessions.clear(),this.statsUnsub?.(),this.statsUnsub=null,this.activityUnsub?.(),this.activityUnsub=null,this.resizeObserver?.disconnect(),this.resizeObserver=null,this.plugin.unsubscribeView(this)}observeContainerWidth(){typeof ResizeObserver>"u"||(this.resizeObserver=new ResizeObserver(e=>{if(!this.userToggledCollapse)for(let s of e){let a=s.contentRect.width<_d;a!==this.convoPanelCollapsed&&(this.convoPanelCollapsed=a,this.applyCollapsedClass())}}),this.resizeObserver.observe(this.contentEl))}selectAgent(e,s){if(s){let n=this.plugin.app.workspace.getLeavesOfType(rt);for(let a of n)if(a.view!==this&&a.view instanceof i&&a.view.selectedAgentName===e&&a.view.selectedConversationId===s){this.plugin.app.workspace.revealLeaf(a);return}}this.selectedAgentName=e,s&&(this.selectedConversationId=s),this.agentSelect&&(this.agentSelect.value=e),this.leaf.updateHeader(),this.switchToAgent(e,s)}buildShell(){let e=this.contentEl;e.empty(),e.addClass("af-root");let s=e.createDiv({cls:"af-chat-view-container"});this.headerEl=s.createDiv({cls:"af-chat-view-header"}),this.collapseBtn=this.headerEl.createEl("button",{cls:"clickable-icon af-chat-convo-collapse-btn",attr:{title:"Toggle conversations panel","aria-label":"Toggle conversations panel"}}),this.collapseBtn.innerHTML=Ed,this.collapseBtn.toggleClass("is-collapsed",this.convoPanelCollapsed),this.collapseBtn.onclick=()=>this.toggleConvoPanel(),this.agentSelect=this.headerEl.createEl("select",{cls:"af-chat-view-agent-select"});let n=this.headerEl.createEl("button",{cls:"af-btn-sm af-chat-view-new-btn"});A(n,"plus","af-btn-icon"),n.appendText(" New Chat"),n.onclick=()=>void this.handleNewChat();let a=s.createDiv({cls:"af-chat-view-body"});this.convoPanelEl=a.createDiv({cls:"af-chat-convo-panel"}),this.convoPanelCollapsed&&this.convoPanelEl.addClass("collapsed");let r=a.createDiv({cls:"af-chat-main"});this.agentSelect.onchange=()=>{let d=this.agentSelect.value;d&&(this.selectedConversationId="",this.textarea.disabled=!1,this.textarea.placeholder="Message the agent\u2026 (Ctrl+Enter to send)",this.switchToAgent(d))},this.messagesEl=r.createDiv({cls:"af-chat-messages"}),this.messagesInner=this.messagesEl.createDiv({cls:"af-chat-messages-inner"});let o=r.createDiv({cls:"af-chat-input-area"});this.pillsRow=o.createDiv({cls:"af-chat-pills-row"}),this.pillsRow.style.display="none";let c=o.createDiv({cls:"af-chat-input-row"});this.attachStopBtn=c.createEl("button",{cls:"af-chat-attach-btn"}),A(this.attachStopBtn,"plus","af-btn-icon"),this.attachStopBtn.title="Attach active document",this.attachStopBtn.onclick=()=>{this.isInStopMode?this.handleStop():this.attachActiveDocument()},this.textarea=c.createEl("textarea",{cls:"af-chat-input",attr:{placeholder:"Message the agent\u2026 (Ctrl+Enter to send)",rows:"1"}}),this.sendBtn=c.createEl("button",{cls:"af-chat-send-btn"}),A(this.sendBtn,"arrow-up","af-btn-icon"),this.sendBtn.style.display="none";let l=()=>{this.textarea.style.height="auto";let d=Math.min(this.textarea.scrollHeight,160);this.textarea.style.height=`${d}px`,this.textarea.style.overflowY=this.textarea.scrollHeight>160?"auto":"hidden",this.sendBtn.style.display=this.textarea.value.trim()?"flex":"none"};this.textarea.addEventListener("input",l),this.textarea.addEventListener("focus",()=>{let d=this.getCurrentSession();d&&this.setStatsSource(d.session)}),this.sendBtn.onclick=()=>void this.handleSend(),this.textarea.onkeydown=d=>{d.key==="Enter"&&(d.ctrlKey||d.metaKey)&&(d.preventDefault(),this.handleSend())},this.textarea.addEventListener("paste",d=>{let h=d.clipboardData?.items;if(h)for(let u=0;u<h.length;u++){let p=h[u];if(p.type.startsWith("image/")){d.preventDefault();let m=p.getAsFile();m&&this.attachImageBlob(m);return}}}),o.addEventListener("dragover",d=>{d.preventDefault(),d.stopPropagation(),o.addClass("af-chat-input-dragover")}),o.addEventListener("dragleave",()=>{o.removeClass("af-chat-input-dragover")}),o.addEventListener("drop",d=>{d.preventDefault(),d.stopPropagation(),o.removeClass("af-chat-input-dragover");let h=d.dataTransfer?.files;if(h)for(let u=0;u<h.length;u++){let p=h[u];p.type.startsWith("image/")&&this.attachImageBlob(p)}}),this.statsEl=o.createDiv({cls:"af-chat-stats"}),this.renderStats(null)}async render(){this.populateAgentDropdown()}setStatsSource(e){this.statsSourceSession!==e&&(this.statsUnsub?.(),this.statsSourceSession=e,this.statsUnsub=e.onStatsChange(s=>this.renderStats(s)))}renderStats(e){if(this.statsEl.empty(),!e||!e.concreteModel){this.statsEl.createSpan({cls:"af-chat-stats-muted",text:"\xA0"});return}let s=this.statsEl.createSpan({cls:"af-chat-stats-line"});if(s.createSpan({cls:"af-chat-stats-model",text:e.concreteModel}),e.contextWindow&&e.contextTokensUsed){let n=Math.min(100,Math.round(e.contextTokensUsed/e.contextWindow*100)),a=10,r=Math.min(a,Math.max(0,Math.round(n/100*a))),o="\u2593".repeat(r)+"\u2591".repeat(a-r),c=s.createSpan({cls:`af-chat-stats-ctx${n>=80?" warn":""}`});c.createSpan({cls:"af-chat-stats-bar",text:o}),c.createSpan({cls:"af-chat-stats-pct",text:`${n}%`}),c.title=`Context: ${ls(e.contextTokensUsed)} / ${ls(e.contextWindow)} tokens (${n}%)
|
|
11924
12093
|
|
|
11925
12094
|
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.
|
|
11926
12095
|
|
|
11927
|
-
To get 1M context on Opus, set the agent's model to claude-opus-4-7[1m] (or us.anthropic.claude-opus-4-7[1m] on Bedrock).`}if(e.lastCompact){let{preTokens:a,postTokens:n}=e.lastCompact,i=s.createSpan({cls:"af-chat-stats-compact"});i.setText(`compacted ${Kt(a)} \u2192 ${Kt(n)}`),i.title=`Conversation was summarized to free up context. ${Kt(a)} tokens reduced to ${Kt(n)}.`}}populateAgentDropdown(){let e=this.plugin.runtime.getSnapshot().agents,s=this.agentSelect.value;if(this.agentSelect.empty(),e.length===0){let n=this.agentSelect.createEl("option",{text:"No agents available",attr:{value:"",disabled:"true"}});n.selected=!0,this.textarea.disabled=!0,this.showEmptyState();return}if(!this.selectedAgentName){let n=this.agentSelect.createEl("option",{text:"Select agent\u2026",attr:{value:"",disabled:"true"}});n.selected=!0}for(let n of e){let i=n.avatar?.trim(),o=i&&!/^[a-z][a-z0-9-]*$/.test(i)?`${i} `:"";this.agentSelect.createEl("option",{text:`${o}${n.name}`,attr:{value:n.name}})}if(this.selectedAgentName&&e.some(n=>n.name===this.selectedAgentName))this.agentSelect.value=this.selectedAgentName,this.textarea.disabled=!1;else if(s&&e.some(n=>n.name===s))this.agentSelect.value=s,this.selectedAgentName=s,this.textarea.disabled=!1;else{this.selectedAgentName=null,this.leaf.updateHeader(),this.textarea.disabled=!0,this.textarea.placeholder="Select an agent to start chatting\u2026",this.showEmptyState();return}this.leaf.updateHeader(),this.textarea.placeholder="Message the agent\u2026 (Ctrl+Enter to send)";let a=this.messagesInner.querySelector(".af-chat-bubble")!==null;this.selectedAgentName&&!a&&this.switchToAgent(this.selectedAgentName)}showEmptyState(){this.messagesInner.empty();let e=this.messagesInner.createDiv({cls:"af-chat-view-empty"}),s=e.createDiv({cls:"af-chat-view-empty-icon"});this.plugin.runtime.getSnapshot().agents.length===0?((0,X.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,X.setIcon)(s,"message-circle"),e.createDiv({cls:"af-chat-view-empty-text",text:"Select an agent to start"}),e.createDiv({cls:"af-chat-view-empty-hint",text:"Choose an agent from the dropdown above"}))}async switchToAgent(e){let a=this.plugin.runtime.getSnapshot().agents.find(o=>o.name===e);if(!a)return;this.selectedAgentName=e,this.leaf.updateHeader(),this.activityEl=null,this.streamingDot=null,this.messagesInner.empty(),this.threadExpanded.clear();let n=this.sessions.get(e);if(!n){let o=new Bt(a,this.plugin.settings,this.plugin.repository,this.app.vault);n={session:o},this.sessions.set(e,n),await o.loadPersistedState()}this.setStatsSource(n.session);for(let o of n.session.messages)if(o.role==="user")this.addBubble("user",o.content,o.attachments);else{let l=this.addBubble("assistant");if(this.renderMarkdownBubble(l,o.content),l._setRawText?.(o.content),this.attachThreadAffordance(l,o.id,n.session),o.toolCalls&&o.toolCalls.length>0){let c=this.getOrCreateAffordancesRow(l);this.buildToolSummary(o.toolCalls,c)}}this.activityUnsub?.();let i=n.session;this.activityUnsub=i.onActivityChange(()=>{this.getCurrentSession()?.session===i&&this.renderIndicators(i)}),this.textarea.disabled=!1,this.textarea.focus()}getCurrentSession(){if(this.selectedAgentName)return this.sessions.get(this.selectedAgentName)}renderMarkdownBubble(e,s){let a=e.querySelector(".af-chat-copy-btn"),n=a?.parentNode?.removeChild(a)??null;e.empty(),e.addClass("af-compact-md"),X.MarkdownRenderer.render(this.app,s,e,"",this.plugin).then(()=>{n&&e.appendChild(n),this.wireBubbleLinks(e),e.querySelectorAll("pre").forEach(i=>{i.querySelector(".copy-code-button")?.remove();let o=i.querySelector("code");if(!o)return;let l=document.createElement("button");l.className="af-code-copy-btn",l.setAttribute("aria-label","Copy code"),(0,X.setIcon)(l,"copy"),l.onclick=c=>{c.stopPropagation(),navigator.clipboard.writeText(o.textContent??"").then(()=>{l.addClass("copied"),(0,X.setIcon)(l,"check"),setTimeout(()=>{l.removeClass("copied"),(0,X.setIcon)(l,"copy")},1500)})},i.style.position="relative",i.appendChild(l)})})}wireBubbleLinks(e){e.addEventListener("click",s=>{let a=s.target.closest("a");if(!a)return;if(a.classList.contains("internal-link")){s.preventDefault(),s.stopPropagation();let i=a.getAttribute("data-href")||a.getAttribute("href")||a.textContent||"";if(!i)return;let o=s.shiftKey?"split":"tab";this.app.workspace.openLinkText(i,"",o);return}let n=a.getAttribute("href")||"";if(n.startsWith("obsidian://")){s.preventDefault(),s.stopPropagation(),window.location.href=n;return}if(a.classList.contains("external-link")||/^https?:\/\//.test(n)){s.preventDefault(),s.stopPropagation(),window.open(n,"_blank");return}})}addCopyBtn(e,s){let a=e.createEl("button",{cls:"af-chat-copy-btn",attr:{"aria-label":"Copy message"}});(0,X.setIcon)(a,"copy"),a.onclick=n=>{n.stopPropagation(),navigator.clipboard.writeText(s()).then(()=>{a.addClass("copied"),(0,X.setIcon)(a,"check"),setTimeout(()=>{a.removeClass("copied"),(0,X.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,X.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,X.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 f=await a.openOrCreateThread(s);this.renderThreadContainer(c,f,a,d),h=!0}catch(f){c.setText(`Failed to open thread: ${f instanceof Error?f.message:String(f)}`)}};o.onclick=()=>void u(),o.onkeydown=p=>{(p.key==="Enter"||p.key===" ")&&(p.preventDefault(),u())}}renderThreadContainer(e,s,a,n){e.empty();let i=e.createDiv({cls:"af-thread-wrap"}),o=i.createDiv({cls:"af-thread-messages"}),l=null,c=null,d=S=>{S?(l||(l=o.createDiv({cls:"af-chat-activity"})),l.setText(`Working\u2026 (${S})`)):l&&(l.remove(),l=null)},h=S=>{if(S&&!c){c=o.createDiv({cls:"af-chat-streaming-dot"});for(let L=0;L<3;L++)c.createSpan()}else!S&&c&&(c.remove(),c=null)},u=(S,L,E)=>{if(S==="user"&&E&&E.length>0){let I=o.createDiv({cls:"af-chat-bubble-attachments"});for(let q of E){let F=I.createSpan({cls:"af-chat-pill af-chat-pill-inline"}),$=F.createSpan({cls:"af-chat-pill-icon"});(0,X.setIcon)($,q.match(/\.(png|jpe?g|gif|webp|svg)$/i)?"image":"file-text"),F.createSpan({cls:"af-chat-pill-name",text:q})}}let N=o.createDiv({cls:`af-thread-bubble af-thread-bubble-${S}`});return S==="assistant"?this.renderMarkdownBubble(N,L):N.setText(L),N};for(let S of s.messages)u(S.role,S.content,S.attachments);let p=[],f=[],m=i.createDiv({cls:"af-thread-composer-wrap"}),g=m.createDiv({cls:"af-chat-pills-row af-thread-pills-row"});g.style.display="none";let k=()=>{if(g.empty(),p.length===0&&f.length===0){g.style.display="none";return}g.style.display="flex";for(let S of p){let L=g.createDiv({cls:"af-chat-pill"}),E=L.createSpan({cls:"af-chat-pill-icon"});(0,X.setIcon)(E,"file-text"),L.createSpan({cls:"af-chat-pill-name",text:S.name});let N=L.createSpan({cls:"af-chat-pill-remove"});(0,X.setIcon)(N,"x"),N.onclick=I=>{I.stopPropagation();let q=p.findIndex(F=>F.path===S.path);q>=0&&p.splice(q,1),k()}}for(let S of f){let L=g.createDiv({cls:"af-chat-pill"}),E=L.createSpan({cls:"af-chat-pill-icon"});(0,X.setIcon)(E,"image"),L.createSpan({cls:"af-chat-pill-name",text:S.name});let N=L.createSpan({cls:"af-chat-pill-remove"});(0,X.setIcon)(N,"x"),N.onclick=I=>{I.stopPropagation();let q=f.findIndex(F=>F.path===S.path);q>=0&&f.splice(q,1),k()}}},b=()=>{let S=this.app.workspace.getActiveFile();if(!S){new X.Notice("No active document to attach");return}if(p.some(E=>E.path===S.path)){new X.Notice(`"${S.name}" is already attached`);return}if(!new Set(["md","txt","json","yaml","yml","toml","csv","xml","html","css","js","ts","py","sh","sql","env","cfg","ini","log"]).has(S.extension.toLowerCase())){new X.Notice(`Can't attach "${S.name}" \u2014 only text files are supported`);return}p.push(S),k()},v=async S=>{let L=S.type.split("/")[1]?.replace("jpeg","jpg")??"png",E=S.name&&S.name!=="image"?S.name:`pasted-${Date.now()}.${L}`;if(f.some(I=>I.name===E)){new X.Notice(`"${E}" is already attached`);return}let N=await this.saveImageBlobToVault(S);N&&(f.push(N),k())},y=m.createDiv({cls:"af-chat-input-row af-thread-composer"}),x=y.createEl("button",{cls:"af-chat-attach-btn"});D(x,"plus","af-btn-icon"),x.title="Attach active document",x.onclick=S=>{S.preventDefault(),b()};let T=y.createEl("textarea",{cls:"af-chat-input af-thread-input",attr:{placeholder:"Message in thread\u2026 (Ctrl+Enter to send)",rows:"1"}});T.addEventListener("paste",S=>{let L=S.clipboardData?.items;if(L)for(let E=0;E<L.length;E++){let N=L[E];if(N.type.startsWith("image/")){S.preventDefault();let I=N.getAsFile();I&&v(I);return}}}),m.addEventListener("dragover",S=>{S.preventDefault(),S.stopPropagation(),m.addClass("af-chat-input-dragover")}),m.addEventListener("dragleave",()=>{m.removeClass("af-chat-input-dragover")}),m.addEventListener("drop",S=>{S.preventDefault(),S.stopPropagation(),m.removeClass("af-chat-input-dragover");let L=S.dataTransfer?.files;if(L)for(let E=0;E<L.length;E++){let N=L[E];N.type.startsWith("image/")&&v(N)}});let _=y.createEl("button",{cls:"af-chat-send-btn"});D(_,"arrow-up","af-btn-icon"),_.style.display="none";let R=()=>{T.style.height="auto",T.style.height=`${Math.min(T.scrollHeight,120)}px`,_.style.display=T.value.trim()?"flex":"none"};T.addEventListener("input",R),T.addEventListener("focus",()=>this.setStatsSource(s));let A=async()=>{let S=T.value.trim();if(!S||s.isStreaming)return;let L=await this.buildAttachmentContextFor(p,f),E=[...p.map(F=>F.name),...f.map(F=>F.name)],N=L?`${L}${S}`:void 0;T.value="",R(),p.length=0,f.length=0,k(),u("user",S,E.length>0?E:void 0),h(!0);let I=null,q="";try{await s.sendMessage(S,F=>{if(F.type==="text"){I||(h(!1),d(),I=u("assistant",""),I.empty()),q+=F.content;let $=I.querySelector(".af-chat-stream-text");$||($=I.createDiv({cls:"af-chat-stream-text"})),$.setText(q)}else F.type==="tool_use"?d(F.toolName):F.type==="result"&&(d(),h(!1),I&&this.renderMarkdownBubble(I,q))},N,E.length>0?E:void 0),n()}catch(F){h(!1),d();let $=F instanceof Error?F.message:String(F);o.createDiv({cls:"af-thread-error",text:`Error: ${$}`})}};_.onclick=()=>void A(),T.onkeydown=S=>{S.key==="Enter"&&(S.ctrlKey||S.metaKey)&&(S.preventDefault(),A())}}buildToolSummary(e,s){let n=(s??this.messagesInner).createDiv({cls:"af-chat-tool-summary"}),i=n.createEl("details"),o=i.createEl("summary"),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,X.setIcon)(c,"wrench"),o.appendText(` ${e.length} tool call${e.length!==1?"s":""}`);let d=i.createDiv({cls:"af-chat-tool-list"});for(let[h,u]of l){let p=u.length||(l.get(h)?.length??1),f=d.createDiv({cls:"af-chat-tool-item"}),m=p>1?`${h} (\xD7${p})`:h;f.createSpan({cls:"af-chat-tool-name",text:m}),u.length===1&&u[0]&&f.createSpan({cls:"af-chat-tool-cmd",text:u[0]})}return n}renderIndicators(e){let s=e.isStreaming,a=e.currentToolName,n=e.hasCurrentTurnText,i=!!this.messagesInner.querySelector(".af-chat-stream-text"),o=null;if(s&&a?o=`Working\u2026 (${a})`:s&&n&&!i&&(o="Replying\u2026"),o?(this.activityEl?(this.activityEl.parentElement!==this.messagesInner||this.activityEl.nextElementSibling!==null)&&this.messagesInner.appendChild(this.activityEl):this.activityEl=this.messagesInner.createDiv({cls:"af-chat-activity"}),this.activityEl.textContent!==o&&this.activityEl.setText(o)):this.activityEl&&(this.activityEl.remove(),this.activityEl=null),s&&!a&&!n)if(this.streamingDot)(this.streamingDot.parentElement!==this.messagesInner||this.streamingDot.nextElementSibling!==null)&&this.messagesInner.appendChild(this.streamingDot);else{this.streamingDot=this.messagesInner.createDiv({cls:"af-chat-streaming-dot"});for(let c=0;c<3;c++)this.streamingDot.createSpan()}else this.streamingDot&&(this.streamingDot.remove(),this.streamingDot=null);this.setAttachStopMode(s)}setAttachStopMode(e){e!==this.isInStopMode&&(this.isInStopMode=e,this.attachStopBtn.empty(),e?(D(this.attachStopBtn,"square","af-btn-icon"),this.attachStopBtn.title="Stop generation",this.attachStopBtn.addClass("af-chat-stop-mode")):(D(this.attachStopBtn,"plus","af-btn-icon"),this.attachStopBtn.title="Attach active document",this.attachStopBtn.removeClass("af-chat-stop-mode")))}handleStop(){let e=this.getCurrentSession();e&&(e.session.abort(),this.addBubble("error","Generation stopped"))}attachActiveDocument(){let e=this.app.workspace.getActiveFile();if(!e){new X.Notice("No active document to attach");return}if(this.attachedFiles.some(n=>n.path===e.path)){new X.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 X.Notice(`Can't attach "${e.name}" \u2014 only text files are supported`);return}this.attachedFiles.push(e),this.renderPills()}async saveImageBlobToVault(e){let s=e.type.split("/")[1]?.replace("jpeg","jpg")??"png",a=Date.now(),n=e.name&&e.name!=="image"?e.name:`pasted-${a}.${s}`,i=`${this.plugin.settings.fleetFolder}/chat-images`,o=`${i}/${a}-${n}`;try{this.app.vault.getAbstractFileByPath(i)||await this.app.vault.createFolder(i);let l=await e.arrayBuffer();await this.app.vault.createBinary(o,l);let d=this.app.vault.adapter.getBasePath?.()??"";return{name:n,path:`${d}/${o}`}}catch(l){let c=l instanceof Error?l.message:String(l);return new X.Notice(`Failed to save image: ${c}`),null}}async buildAttachmentContextFor(e,s){if(e.length===0&&s.length===0)return"";let a=[];for(let n of e)try{let i=await this.app.vault.cachedRead(n);a.push(`### ${n.name}
|
|
12096
|
+
To get 1M context on Opus, set the agent's model to claude-opus-4-7[1m] (or us.anthropic.claude-opus-4-7[1m] on Bedrock).`}if(e.lastCompact){let{preTokens:n,postTokens:a}=e.lastCompact,r=s.createSpan({cls:"af-chat-stats-compact"});r.setText(`compacted ${ls(n)} \u2192 ${ls(a)}`),r.title=`Conversation was summarized to free up context. ${ls(n)} tokens reduced to ${ls(a)}.`}}populateAgentDropdown(){let e=this.plugin.runtime.getSnapshot().agents,s=this.agentSelect.value;if(this.agentSelect.empty(),e.length===0){let a=this.agentSelect.createEl("option",{text:"No agents available",attr:{value:"",disabled:"true"}});a.selected=!0,this.textarea.disabled=!0,this.showEmptyState();return}if(!this.selectedAgentName){let a=this.agentSelect.createEl("option",{text:"Select agent\u2026",attr:{value:"",disabled:"true"}});a.selected=!0}for(let a of e){let r=a.avatar?.trim(),o=r&&!/^[a-z][a-z0-9-]*$/.test(r)?`${r} `:"";this.agentSelect.createEl("option",{text:`${o}${a.name}`,attr:{value:a.name}})}if(this.selectedAgentName&&e.some(a=>a.name===this.selectedAgentName))this.agentSelect.value=this.selectedAgentName,this.textarea.disabled=!1;else if(s&&e.some(a=>a.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 n=this.messagesInner.querySelector(".af-chat-bubble")!==null;this.selectedAgentName&&!n&&this.switchToAgent(this.selectedAgentName,this.selectedConversationId||void 0)}showEmptyState(){this.messagesInner.empty();let e=this.messagesInner.createDiv({cls:"af-chat-view-empty"}),s=e.createDiv({cls:"af-chat-view-empty-icon"});this.plugin.runtime.getSnapshot().agents.length===0?((0,G.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,G.setIcon)(s,"message-circle"),e.createDiv({cls:"af-chat-view-empty-text",text:"Select an agent to start"}),e.createDiv({cls:"af-chat-view-empty-hint",text:"Choose an agent from the dropdown above"}))}async switchToAgent(e,s){let a=this.plugin.runtime.getSnapshot().agents.find(d=>d.name===e);if(!a)return;await this.loadConversations(a);let r;if(s&&this.conversationsCache.some(d=>d.id===s))r=s;else if(this.conversationsCache.length>0)r=this.conversationsCache[0].id;else{r=Co();try{await this.plugin.repository.createConversation(a,r,To())}catch(d){new G.Notice(`Couldn't create conversation: ${d instanceof Error?d.message:String(d)}`);return}await this.loadConversations(a)}if(!s||s!==r){let d=this.plugin.app.workspace.getLeavesOfType(rt);for(let h of d)if(h.view!==this&&h.view instanceof i&&h.view.selectedAgentName===e&&h.view.selectedConversationId===r){this.plugin.app.workspace.revealLeaf(h);return}}this.selectedAgentName=e,this.selectedConversationId=r,this.leaf.updateHeader(),this.activityEl=null,this.streamingDot=null,this.messagesInner.empty(),this.threadExpanded.clear(),this.renderConvoPanel();let o=jn(e,r),c=this.sessions.get(o);if(!c){let d=new Xt(a,this.plugin.settings,this.plugin.repository,this.app.vault,{inAppConversationId:r});c={session:d},this.sessions.set(o,c),await d.loadPersistedState()}this.setStatsSource(c.session);for(let d of c.session.messages)if(d.role==="user")this.addBubble("user",d.content,d.attachments);else{let h=this.addBubble("assistant");if(this.renderMarkdownBubble(h,d.content),h._setRawText?.(d.content),this.attachThreadAffordance(h,d.id,c.session),d.toolCalls&&d.toolCalls.length>0){let u=this.getOrCreateAffordancesRow(h);this.buildToolSummary(d.toolCalls,u)}}this.activityUnsub?.();let l=c.session;this.activityUnsub=l.onActivityChange(()=>{this.getCurrentSession()?.session===l&&this.renderIndicators(l)}),this.textarea.disabled=!1,this.textarea.focus()}getCurrentSession(){if(this.selectedAgentName)return this.sessions.get(jn(this.selectedAgentName,this.selectedConversationId))}async loadConversations(e){this.conversationsCache=await this.plugin.repository.listConversations(e)}async refreshConversationsList(e){await this.loadConversations(e),this.renderConvoPanel(),this.leaf.updateHeader()}renderConvoPanel(){if(!this.convoPanelEl||(this.convoPanelEl.empty(),!this.selectedAgentName))return;this.convoPanelEl.createDiv({cls:"af-chat-convo-header",text:"Conversations"});let e=this.convoPanelEl.createDiv({cls:"af-chat-convo-new"}),s=e.createSpan({cls:"af-chat-convo-new-icon"});(0,G.setIcon)(s,"plus"),e.createSpan({cls:"af-chat-convo-new-label",text:"New chat"}),e.onclick=()=>void this.handleNewChat();let n=this.convoPanelEl.createDiv({cls:"af-chat-convo-list"});for(let a of this.conversationsCache)this.renderConvoRow(n,a)}renderConvoRow(e,s){let n=s.id===this.selectedConversationId,a=e.createDiv({cls:`af-chat-convo-item${n?" active":""}`}),r=a.createDiv({cls:"af-chat-convo-name",text:s.name});r.title=s.name,r.ondblclick=d=>{d.stopPropagation(),this.beginInlineRename(r,s)};let o=a.createDiv({cls:"af-chat-convo-meta"}),c=s.messageCount===1?"1 msg":`${s.messageCount} msgs`;o.appendText(`${c} \xB7 ${Td(s.lastActive)}`);let l=a.createEl("button",{cls:"af-chat-convo-trash",attr:{title:"Delete conversation","aria-label":"Delete conversation"}});(0,G.setIcon)(l,"trash-2"),l.onclick=d=>{d.stopPropagation(),this.handleDeleteConversation(s)},a.onclick=()=>{s.id!==this.selectedConversationId&&this.handleConvoSelected(s.id)}}beginInlineRename(e,s){let n=s.name,a=document.createElement("input");a.type="text",a.value=n,a.className="af-chat-convo-name-input",e.replaceWith(a),a.focus(),a.select();let r=!1,o=l=>{let d=document.createElement("div");return d.className="af-chat-convo-name",d.textContent=l??n,d.title=l??n,d.ondblclick=h=>{h.stopPropagation();let u=this.conversationsCache.find(p=>p.id===s.id)??s;this.beginInlineRename(d,u)},a.replaceWith(d),d},c=async()=>{if(r)return;r=!0;let l=a.value.trim();if(!l||l===n){o(null);return}try{await this.saveConversationName(s.id,l)}catch(d){new G.Notice(`Couldn't rename: ${d instanceof Error?d.message:String(d)}`),o(null);return}this.renderConvoPanel(),this.leaf.updateHeader()};a.addEventListener("keydown",l=>{l.key==="Enter"?(l.preventDefault(),c()):l.key==="Escape"&&(l.preventDefault(),r=!0,o(null))}),a.addEventListener("blur",()=>{c()})}async saveConversationName(e,s){if(!this.selectedAgentName)return;let a=this.plugin.runtime.getSnapshot().agents.find(l=>l.name===this.selectedAgentName);if(!a)return;try{await this.plugin.repository.renameConversation(a,e,s)}catch{}let r=jn(this.selectedAgentName,e),o=this.sessions.get(r);o&&await o.session.setConversationName(s);let c=this.conversationsCache.findIndex(l=>l.id===e);c>=0&&(this.conversationsCache[c]={...this.conversationsCache[c],name:s})}async handleConvoSelected(e){if(!this.selectedAgentName)return;let s=this.plugin.app.workspace.getLeavesOfType(rt);for(let n of s)if(n.view!==this&&n.view instanceof i&&n.view.selectedAgentName===this.selectedAgentName&&n.view.selectedConversationId===e){this.plugin.app.workspace.revealLeaf(n);return}await this.switchToAgent(this.selectedAgentName,e)}async handleDeleteConversation(e){if(!this.selectedAgentName)return;let n=this.plugin.runtime.getSnapshot().agents.find(o=>o.name===this.selectedAgentName);if(!n||!window.confirm(`Delete conversation "${e.name}"?
|
|
12097
|
+
|
|
12098
|
+
This removes its message history. The agent and its other conversations are untouched.`))return;let r=jn(this.selectedAgentName,e.id);this.sessions.get(r)?.session.dispose(),this.sessions.delete(r);try{await this.plugin.repository.deleteConversation(n,e.id)}catch(o){new G.Notice(`Couldn't delete: ${o instanceof Error?o.message:String(o)}`);return}if(await this.refreshConversationsList(n),e.id===this.selectedConversationId){let o=this.conversationsCache[0]?.id;await this.switchToAgent(this.selectedAgentName,o)}}toggleConvoPanel(){this.convoPanelCollapsed=!this.convoPanelCollapsed,this.userToggledCollapse=!0,this.applyCollapsedClass(),this.leaf.updateHeader()}applyCollapsedClass(){this.convoPanelEl&&(this.convoPanelCollapsed?this.convoPanelEl.addClass("collapsed"):this.convoPanelEl.removeClass("collapsed")),this.collapseBtn&&this.collapseBtn.toggleClass("is-collapsed",this.convoPanelCollapsed)}renderMarkdownBubble(e,s){let n=e.querySelector(".af-chat-copy-btn"),a=n?.parentNode?.removeChild(n)??null;e.empty(),e.addClass("af-compact-md"),G.MarkdownRenderer.render(this.app,s,e,"",this.plugin).then(()=>{a&&e.appendChild(a),this.wireBubbleLinks(e),e.querySelectorAll("pre").forEach(r=>{r.querySelector(".copy-code-button")?.remove();let o=r.querySelector("code");if(!o)return;let c=document.createElement("button");c.className="af-code-copy-btn",c.setAttribute("aria-label","Copy code"),(0,G.setIcon)(c,"copy"),c.onclick=l=>{l.stopPropagation(),navigator.clipboard.writeText(o.textContent??"").then(()=>{c.addClass("copied"),(0,G.setIcon)(c,"check"),setTimeout(()=>{c.removeClass("copied"),(0,G.setIcon)(c,"copy")},1500)})},r.style.position="relative",r.appendChild(c)})})}wireBubbleLinks(e){e.addEventListener("click",s=>{let n=s.target.closest("a");if(!n)return;if(n.classList.contains("internal-link")){s.preventDefault(),s.stopPropagation();let r=n.getAttribute("data-href")||n.getAttribute("href")||n.textContent||"";if(!r)return;let o=s.shiftKey?"split":"tab";this.app.workspace.openLinkText(r,"",o);return}let a=n.getAttribute("href")||"";if(a.startsWith("obsidian://")){s.preventDefault(),s.stopPropagation(),window.location.href=a;return}if(n.classList.contains("external-link")||/^https?:\/\//.test(a)){s.preventDefault(),s.stopPropagation(),window.open(a,"_blank");return}})}addCopyBtn(e,s){let n=e.createEl("button",{cls:"af-chat-copy-btn",attr:{"aria-label":"Copy message"}});(0,G.setIcon)(n,"copy"),n.onclick=a=>{a.stopPropagation(),navigator.clipboard.writeText(s()).then(()=>{n.addClass("copied"),(0,G.setIcon)(n,"check"),setTimeout(()=>{n.removeClass("copied"),(0,G.setIcon)(n,"copy")},1500)})}}addBubble(e,s,n){if(e==="user"&&n&&n.length>0){let r=this.messagesInner.createDiv({cls:"af-chat-bubble-attachments"});for(let o of n){let c=r.createSpan({cls:"af-chat-pill af-chat-pill-inline"}),l=c.createSpan({cls:"af-chat-pill-icon"}),d=/\.(png|jpe?g|gif|webp|svg|bmp)$/i.test(o);(0,G.setIcon)(l,d?"image":"file-text"),c.createSpan({cls:"af-chat-pill-name",text:o})}}let a=this.messagesInner.createDiv({cls:`af-chat-bubble af-chat-bubble-${e}`});if(s&&(e==="assistant"?this.renderMarkdownBubble(a,s):a.setText(s)),e==="assistant"){let r=s??"";this.addCopyBtn(a,()=>r),a._setRawText=o=>{r=o}}return this.messagesEl.scrollTop=this.messagesEl.scrollHeight,a}getOrCreateAffordancesRow(e){let s=e.parentElement;if(!s)throw new Error("bubble has no parent");let n=e.nextElementSibling;if(n&&n.classList.contains("af-chat-affordances"))return n;let a=document.createElement("div");return a.className="af-chat-affordances",s.insertBefore(a,e.nextSibling),a}attachThreadAffordance(e,s,n){let a=e.parentElement;if(!a)return;let r=this.getOrCreateAffordancesRow(e);if(r.querySelector(".af-thread-badge"))return;let o=document.createElement("div");o.className="af-thread-badge",o.setAttribute("role","button"),o.setAttribute("tabindex","0"),r.appendChild(o),(0,G.setIcon)(o,"message-circle");let c=o.createSpan({cls:"af-thread-badge-label"}),l=document.createElement("div");l.className="af-thread-container",l.style.display="none",a.insertBefore(l,r.nextSibling);let d=()=>{let f=n.getThreadIndex()[s]?.messageCount??0;f<=0?c.setText("Thread"):c.setText(`${f} ${f===1?"reply":"replies"}`),f>0&&o.addClass("has-replies")};d();let h=!1,u=async()=>{if(this.threadExpanded.get(s)===!0){l.style.display="none",this.threadExpanded.set(s,!1),o.removeClass("expanded"),this.setStatsSource(n);return}if(this.threadExpanded.set(s,!0),l.style.display="",o.addClass("expanded"),!h)try{let m=await n.openOrCreateThread(s);this.renderThreadContainer(l,m,n,d),h=!0}catch(m){l.setText(`Failed to open thread: ${m instanceof Error?m.message:String(m)}`)}};o.onclick=()=>void u(),o.onkeydown=p=>{(p.key==="Enter"||p.key===" ")&&(p.preventDefault(),u())}}renderThreadContainer(e,s,n,a){e.empty();let r=e.createDiv({cls:"af-thread-wrap"}),o=r.createDiv({cls:"af-thread-messages"}),c=null,l=null,d=C=>{C?(c||(c=o.createDiv({cls:"af-chat-activity"})),c.setText(`Working\u2026 (${C})`)):c&&(c.remove(),c=null)},h=C=>{if(C&&!l){l=o.createDiv({cls:"af-chat-streaming-dot"});for(let L=0;L<3;L++)l.createSpan()}else!C&&l&&(l.remove(),l=null)},u=(C,L,R)=>{if(C==="user"&&R&&R.length>0){let F=o.createDiv({cls:"af-chat-bubble-attachments"});for(let Y of R){let U=F.createSpan({cls:"af-chat-pill af-chat-pill-inline"}),W=U.createSpan({cls:"af-chat-pill-icon"});(0,G.setIcon)(W,Y.match(/\.(png|jpe?g|gif|webp|svg)$/i)?"image":"file-text"),U.createSpan({cls:"af-chat-pill-name",text:Y})}}let B=o.createDiv({cls:`af-thread-bubble af-thread-bubble-${C}`});return C==="assistant"?this.renderMarkdownBubble(B,L):B.setText(L),B};for(let C of s.messages)u(C.role,C.content,C.attachments);let p=[],m=[],f=r.createDiv({cls:"af-thread-composer-wrap"}),y=f.createDiv({cls:"af-chat-pills-row af-thread-pills-row"});y.style.display="none";let w=()=>{if(y.empty(),p.length===0&&m.length===0){y.style.display="none";return}y.style.display="flex";for(let C of p){let L=y.createDiv({cls:"af-chat-pill"}),R=L.createSpan({cls:"af-chat-pill-icon"});(0,G.setIcon)(R,"file-text"),L.createSpan({cls:"af-chat-pill-name",text:C.name});let B=L.createSpan({cls:"af-chat-pill-remove"});(0,G.setIcon)(B,"x"),B.onclick=F=>{F.stopPropagation();let Y=p.findIndex(U=>U.path===C.path);Y>=0&&p.splice(Y,1),w()}}for(let C of m){let L=y.createDiv({cls:"af-chat-pill"}),R=L.createSpan({cls:"af-chat-pill-icon"});(0,G.setIcon)(R,"image"),L.createSpan({cls:"af-chat-pill-name",text:C.name});let B=L.createSpan({cls:"af-chat-pill-remove"});(0,G.setIcon)(B,"x"),B.onclick=F=>{F.stopPropagation();let Y=m.findIndex(U=>U.path===C.path);Y>=0&&m.splice(Y,1),w()}}},k=()=>{let C=this.app.workspace.getActiveFile();if(!C){new G.Notice("No active document to attach");return}if(p.some(R=>R.path===C.path)){new G.Notice(`"${C.name}" is already attached`);return}if(!new Set(["md","txt","json","yaml","yml","toml","csv","xml","html","css","js","ts","py","sh","sql","env","cfg","ini","log"]).has(C.extension.toLowerCase())){new G.Notice(`Can't attach "${C.name}" \u2014 only text files are supported`);return}p.push(C),w()},g=async C=>{let L=C.type.split("/")[1]?.replace("jpeg","jpg")??"png",R=C.name&&C.name!=="image"?C.name:`pasted-${Date.now()}.${L}`;if(m.some(F=>F.name===R)){new G.Notice(`"${R}" is already attached`);return}let B=await this.saveImageBlobToVault(C);B&&(m.push(B),w())},v=f.createDiv({cls:"af-chat-input-row af-thread-composer"}),S=v.createEl("button",{cls:"af-chat-attach-btn"});A(S,"plus","af-btn-icon"),S.title="Attach active document",S.onclick=C=>{C.preventDefault(),k()};let T=v.createEl("textarea",{cls:"af-chat-input af-thread-input",attr:{placeholder:"Message in thread\u2026 (Ctrl+Enter to send)",rows:"1"}});T.addEventListener("paste",C=>{let L=C.clipboardData?.items;if(L)for(let R=0;R<L.length;R++){let B=L[R];if(B.type.startsWith("image/")){C.preventDefault();let F=B.getAsFile();F&&g(F);return}}}),f.addEventListener("dragover",C=>{C.preventDefault(),C.stopPropagation(),f.addClass("af-chat-input-dragover")}),f.addEventListener("dragleave",()=>{f.removeClass("af-chat-input-dragover")}),f.addEventListener("drop",C=>{C.preventDefault(),C.stopPropagation(),f.removeClass("af-chat-input-dragover");let L=C.dataTransfer?.files;if(L)for(let R=0;R<L.length;R++){let B=L[R];B.type.startsWith("image/")&&g(B)}});let _=v.createEl("button",{cls:"af-chat-send-btn"});A(_,"arrow-up","af-btn-icon"),_.style.display="none";let D=()=>{T.style.height="auto",T.style.height=`${Math.min(T.scrollHeight,120)}px`,_.style.display=T.value.trim()?"flex":"none"};T.addEventListener("input",D),T.addEventListener("focus",()=>this.setStatsSource(s));let E=async()=>{let C=T.value.trim();if(!C||s.isStreaming)return;let L=await this.buildAttachmentContextFor(p,m),R=[...p.map(U=>U.name),...m.map(U=>U.name)],B=L?`${L}${C}`:void 0;T.value="",D(),p.length=0,m.length=0,w(),u("user",C,R.length>0?R:void 0),h(!0);let F=null,Y="";try{await s.sendMessage(C,U=>{if(U.type==="text"){F||(h(!1),d(),F=u("assistant",""),F.empty()),Y+=U.content;let W=F.querySelector(".af-chat-stream-text");W||(W=F.createDiv({cls:"af-chat-stream-text"})),W.setText(Y)}else U.type==="tool_use"?d(U.toolName):U.type==="result"&&(d(),h(!1),F&&this.renderMarkdownBubble(F,ds(Y)))},B,R.length>0?R:void 0),a()}catch(U){h(!1),d();let W=U instanceof Error?U.message:String(U);o.createDiv({cls:"af-thread-error",text:`Error: ${W}`})}};_.onclick=()=>void E(),T.onkeydown=C=>{C.key==="Enter"&&(C.ctrlKey||C.metaKey)&&(C.preventDefault(),E())}}buildToolSummary(e,s){let a=(s??this.messagesInner).createDiv({cls:"af-chat-tool-summary"}),r=a.createEl("details"),o=r.createEl("summary"),c=new Map;for(let h of e){let u=c.get(h.name)??[];h.command&&u.push(h.command),c.set(h.name,u)}let l=o.createSpan({cls:"af-chat-tool-icon"});(0,G.setIcon)(l,"wrench"),o.appendText(` ${e.length} tool call${e.length!==1?"s":""}`);let d=r.createDiv({cls:"af-chat-tool-list"});for(let[h,u]of c){let p=u.length||(c.get(h)?.length??1),m=d.createDiv({cls:"af-chat-tool-item"}),f=p>1?`${h} (\xD7${p})`:h;m.createSpan({cls:"af-chat-tool-name",text:f}),u.length===1&&u[0]&&m.createSpan({cls:"af-chat-tool-cmd",text:u[0]})}return a}renderIndicators(e){let s=e.isStreaming,n=e.currentToolName,a=e.hasCurrentTurnText,r=!!this.messagesInner.querySelector(".af-chat-stream-text"),o=null;if(s&&n?o=`Working\u2026 (${n})`:s&&a&&!r&&(o="Replying\u2026"),o?(this.activityEl?(this.activityEl.parentElement!==this.messagesInner||this.activityEl.nextElementSibling!==null)&&this.messagesInner.appendChild(this.activityEl):this.activityEl=this.messagesInner.createDiv({cls:"af-chat-activity"}),this.activityEl.textContent!==o&&this.activityEl.setText(o)):this.activityEl&&(this.activityEl.remove(),this.activityEl=null),s&&!n&&!a)if(this.streamingDot)(this.streamingDot.parentElement!==this.messagesInner||this.streamingDot.nextElementSibling!==null)&&this.messagesInner.appendChild(this.streamingDot);else{this.streamingDot=this.messagesInner.createDiv({cls:"af-chat-streaming-dot"});for(let l=0;l<3;l++)this.streamingDot.createSpan()}else this.streamingDot&&(this.streamingDot.remove(),this.streamingDot=null);this.setAttachStopMode(s)}setAttachStopMode(e){e!==this.isInStopMode&&(this.isInStopMode=e,this.attachStopBtn.empty(),e?(A(this.attachStopBtn,"square","af-btn-icon"),this.attachStopBtn.title="Stop generation",this.attachStopBtn.addClass("af-chat-stop-mode")):(A(this.attachStopBtn,"plus","af-btn-icon"),this.attachStopBtn.title="Attach active document",this.attachStopBtn.removeClass("af-chat-stop-mode")))}handleStop(){let e=this.getCurrentSession();e&&(e.session.abort(),this.addBubble("error","Generation stopped"))}attachActiveDocument(){let e=this.app.workspace.getActiveFile();if(!e){new G.Notice("No active document to attach");return}if(this.attachedFiles.some(a=>a.path===e.path)){new G.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 G.Notice(`Can't attach "${e.name}" \u2014 only text files are supported`);return}this.attachedFiles.push(e),this.renderPills()}async saveImageBlobToVault(e){let s=e.type.split("/")[1]?.replace("jpeg","jpg")??"png",n=Date.now(),a=e.name&&e.name!=="image"?e.name:`pasted-${n}.${s}`,r=`${this.plugin.settings.fleetFolder}/chat-images`,o=`${r}/${n}-${a}`;try{this.app.vault.getAbstractFileByPath(r)||await this.app.vault.createFolder(r);let c=await e.arrayBuffer();await this.app.vault.createBinary(o,c);let d=this.app.vault.adapter.getBasePath?.()??"";return{name:a,path:`${d}/${o}`}}catch(c){let l=c instanceof Error?c.message:String(c);return new G.Notice(`Failed to save image: ${l}`),null}}async buildAttachmentContextFor(e,s){if(e.length===0&&s.length===0)return"";let n=[];for(let a of e)try{let r=await this.app.vault.cachedRead(a);n.push(`### ${a.name}
|
|
11928
12099
|
\`\`\`
|
|
11929
|
-
${
|
|
11930
|
-
\`\`\``)}catch{
|
|
11931
|
-
(Could not read file)`)}for(let
|
|
11932
|
-
The image file is located at: ${
|
|
12100
|
+
${r}
|
|
12101
|
+
\`\`\``)}catch{n.push(`### ${a.name}
|
|
12102
|
+
(Could not read file)`)}for(let a of s)n.push(`### Image: ${a.name}
|
|
12103
|
+
The image file is located at: ${a.path}
|
|
11933
12104
|
Please read and analyze this image.`);return`## Attached Files
|
|
11934
12105
|
|
|
11935
|
-
${
|
|
12106
|
+
${n.join(`
|
|
11936
12107
|
|
|
11937
12108
|
`)}
|
|
11938
12109
|
|
|
11939
12110
|
---
|
|
11940
12111
|
|
|
11941
|
-
`}async attachImageBlob(e){let s=e.type.split("/")[1]?.replace("jpeg","jpg")??"png",
|
|
12112
|
+
`}async attachImageBlob(e){let s=e.type.split("/")[1]?.replace("jpeg","jpg")??"png",n=e.name&&e.name!=="image"?e.name:`pasted-${Date.now()}.${s}`;if(this.attachedImages.some(r=>r.name===n)){new G.Notice(`"${n}" is already attached`);return}let a=await this.saveImageBlobToVault(e);a&&(this.attachedImages.push(a),this.renderPills())}removeAttachment(e){this.attachedFiles=this.attachedFiles.filter(s=>s.path!==e),this.attachedImages=this.attachedImages.filter(s=>s.path!==e),this.renderPills()}renderPills(){if(this.pillsRow.empty(),this.attachedFiles.length+this.attachedImages.length===0){this.pillsRow.style.display="none";return}this.pillsRow.style.display="flex";for(let s of this.attachedFiles){let n=this.pillsRow.createDiv({cls:"af-chat-pill"}),a=n.createSpan({cls:"af-chat-pill-icon"});(0,G.setIcon)(a,"file-text"),n.createSpan({cls:"af-chat-pill-name",text:s.name});let r=n.createSpan({cls:"af-chat-pill-remove"});(0,G.setIcon)(r,"x"),r.onclick=o=>{o.stopPropagation(),this.removeAttachment(s.path)}}for(let s of this.attachedImages){let n=this.pillsRow.createDiv({cls:"af-chat-pill"}),a=n.createSpan({cls:"af-chat-pill-icon"});(0,G.setIcon)(a,"image"),n.createSpan({cls:"af-chat-pill-name",text:s.name});let r=n.createSpan({cls:"af-chat-pill-remove"});(0,G.setIcon)(r,"x"),r.onclick=o=>{o.stopPropagation(),this.removeAttachment(s.path)}}}buildAttachmentContext(){return this.buildAttachmentContextFor(this.attachedFiles,this.attachedImages)}async handleSend(){let e=this.getCurrentSession();if(!e)return;let s=this.textarea.value.trim();if(!s)return;let n=await this.buildAttachmentContext(),a=[...this.attachedFiles.map(u=>u.name),...this.attachedImages.map(u=>u.name)];this.textarea.value="",this.textarea.style.height="auto",this.sendBtn.style.display="none",this.attachedFiles=[],this.attachedImages=[],this.renderPills();let r=n?`${n}${s}`:void 0;if(this.addBubble("user",s,a.length>0?a:void 0),e.session.isStreaming){e.session.injectMessage(s,r,a.length>0?a:void 0);return}let o=null,c="",l=!1,d=e.session,h=()=>this.getCurrentSession()?.session===d;try{await e.session.sendMessage(s,u=>{if(!h()){o=null,l=!1,c="";return}if(u.type==="text"){(!l||!o||!o.isConnected)&&(o=this.addBubble("assistant"),l=!0),c+=u.content;let p=o.querySelector(".af-chat-stream-text");p||(p=o.createDiv({cls:"af-chat-stream-text"})),p.setText(c)}else if(u.type==="error"){let p=u.errorMessage?.trim()||"The agent's run ended with an error.";this.addBubble("error",`Error: ${p}`)}else if(u.type!=="compacted"){if(u.type==="result"){if(l&&o&&o.isConnected){let p=ds(c);this.renderMarkdownBubble(o,p),o._setRawText?.(p);let m=e.session.messages[e.session.messages.length-1];if(m&&m.role==="assistant"&&(this.attachThreadAffordance(o,m.id,e.session),u.toolCalls&&u.toolCalls.length>0)){let f=this.getOrCreateAffordancesRow(o);this.buildToolSummary(u.toolCalls,f)}}else u.toolCalls&&u.toolCalls.length>0&&this.buildToolSummary(u.toolCalls);c="",l=!1,o=null}}},r,a.length>0?a:void 0)}catch(u){let p=u instanceof Error?u.message:String(u);p!=="Aborted"&&this.addBubble("error",`Error: ${p}`)}}async handleNewChat(){if(!this.selectedAgentName)return;let s=this.plugin.runtime.getSnapshot().agents.find(a=>a.name===this.selectedAgentName);if(!s)return;let n=Co();try{await this.plugin.repository.createConversation(s,n,To())}catch(a){new G.Notice(`Couldn't create conversation: ${a instanceof Error?a.message:String(a)}`);return}await this.switchToAgent(this.selectedAgentName,n)}startFreshIntro(e){let s="",n=null,a=!1;e.sendMessage("Please introduce yourself and briefly describe your capabilities and what you can help with.",r=>{r.type==="text"&&(a||(n=this.addBubble("assistant"),a=!0),s+=r.content,n.setText(s))}).then(r=>{a&&n?(this.renderMarkdownBubble(n,s),n._setRawText?.(s)):r.text.trim()&&(n=this.addBubble("assistant"),this.renderMarkdownBubble(n,r.text),n._setRawText?.(r.text)),r.toolCalls.length>0&&this.buildToolSummary(r.toolCalls),this.textarea.focus()}).catch(r=>{let o=r instanceof Error?r.message:String(r);o!=="Aborted"&&this.addBubble("error",`Error: ${o}`)})}};function ls(i){return i>=1e3?`${(i/1e3).toFixed(i>=1e4?0:1)}k`:`${i}`}var Wn=class extends Ce.Plugin{settings={...dt};repository;runtime;get mcpManager(){return this.runtime.mcpManager}mcpAuth=new pn;channelCredentials=new bn;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 ms(this.app.vault,this.settings),this.repository.setChannelCredentialGetter(()=>this.channelCredentials.toRecord()),this.runtime=new Ts(this.repository,this.settings),this.registerView(Et,n=>new Ns(n,this)),this.registerView(Lt,n=>new On(n,this)),this.registerView(rt,n=>new cs(n,this)),this.addSettingTab(new tn(this)),await this.repository.ensureFleetStructure()&&await this.repository.ensureSamples();let e=await this.repository.updateDefaults(this.settings.defaultFileHashes??{});JSON.stringify(e)!==JSON.stringify(this.settings.defaultFileHashes??{})&&(this.settings.defaultFileHashes=e,await this.saveData(this.settings)),await this.runtime.initialize(),await this.verifyClaudeCli(!1),await this.maybeResolveCodexCliPath(),this.addRibbonIcon("bot","Agent Fleet Dashboard",()=>void this.activateDashboardView()),this.addRibbonIcon("message-circle","Agent Chat",()=>{let n=this.app.workspace.getLeavesOfType(rt);n.length>0?this.app.workspace.revealLeaf(n[0]):this.openChatView()}),this.addCommands(),this.registerVaultHandlers(),this.registerRuntimeListeners();let s=this.app.secretStorage;this.secretStore=new vn(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(n=>{this.settings.channelCredentials=n,this.saveSettings()}),this.channelManager=new yn({getRepository:()=>this.repository,vault:this.app.vault,getSettings:()=>this.settings,getChannelCredentials:()=>this.channelCredentials.toRecord(),adapterFactory:(n,a)=>{if(n.type==="slack")return new Ln(n,a);if(n.type==="telegram")return new Fn(n,a);throw new Error(`Channel type \`${n.type}\` is not yet supported in this version.`)}});try{await this.channelManager.start(this.runtime.getSnapshot())}catch(n){console.error("Agent Fleet: channel manager failed to start",n),new Ce.Notice("Agent Fleet: channel manager failed to start \u2014 check console.")}this.runtime.onHeartbeatResult((n,a,r)=>{this.channelManager?.broadcastToChannel(a,`*Heartbeat \u2014 ${n}*
|
|
11942
12113
|
|
|
11943
|
-
${
|
|
12114
|
+
${r}`).catch(o=>{console.warn(`Agent Fleet: heartbeat channel post failed for ${n}`,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 Ce.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(Et),this.app.workspace.detachLeavesOfType(Lt),this.app.workspace.detachLeavesOfType(rt),this.runtime?.shutdown(),this.channelManager?.stop(),Oi()}async loadSettings(){this.settings={...dt,...await this.loadData()}}async saveSettings(){this.settings.claudeCliPath=await this.resolveClaudeCliPath(this.settings.claudeCliPath),await this.maybeResolveCodexCliPath(),Ni(),await this.saveData(this.settings),this.repository&&this.runtime&&(this.runtime.shutdown(),this.repository=new ms(this.app.vault,this.settings),this.repository.setChannelCredentialGetter(()=>this.channelCredentials.toRecord()),this.runtime=new Ts(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(Et);if(t.length>0){this.app.workspace.revealLeaf(t[0]);return}await this.app.workspace.getLeaf(!0).setViewState({type:Et,active:!0})}async navigateDashboard(t,e){await this.activateDashboardView();let n=this.app.workspace.getLeavesOfType(Et)[0];if(n){let a=n.view;a instanceof Ns&&a.navigateTo(t,e)}}async activateAgentsView(){let t=this.getLeafForView(Lt,"left");await t.setViewState({type:Lt,active:!0}),this.app.workspace.revealLeaf(t)}async openChatView(t){if(t){let s=this.app.workspace.getLeavesOfType(rt);for(let n of s)if(n.view instanceof cs&&n.view.selectedAgentName===t){this.app.workspace.revealLeaf(n);return}}let e=this.app.workspace.getRightLeaf(!1)??this.app.workspace.getLeaf(!0);await e.setViewState({type:rt,active:!0,state:t?{agentName:t}:{}}),this.app.workspace.revealLeaf(e),t&&e.view instanceof cs&&e.view.selectAgent(t)}async refreshFromVault(){this.suppressVaultEvents=!0;try{await this.runtime.refreshFromVault(),this.notifyViews(),this.refreshStatusBar(),this.channelManager?.reconcile(this.runtime.getSnapshot())}finally{this.suppressTimer&&clearTimeout(this.suppressTimer),this.suppressTimer=setTimeout(()=>{this.suppressTimer=void 0,this.suppressVaultEvents=!1},500)}}refreshStatusBar(){if(!this.settings.showStatusBar){this.statusBarEl?.detach(),this.statusBarEl=void 0;return}this.statusBarEl||(this.statusBarEl=this.addStatusBarItem(),this.statusBarEl.onclick=()=>void this.activateDashboardView());let t=this.runtime.getFleetStatus();this.statusBarEl.setText(`\u{1F916} ${t.running} running \xB7 ${t.pending} pending \xB7 ${t.completedToday} completed today`)}async verifyClaudeCli(t=!0){let e=await this.resolveClaudeCliPath(this.settings.claudeCliPath);return this.settings.claudeCliPath=e,await this.verifyCliBinary(e,"Claude",t)}async verifyCodexCli(t=!0){let e=await this.resolveCliPathFrom(Xn(this.settings.codexCliPath),this.settings.codexCliPath);return this.settings.codexCliPath=e,await this.verifyCliBinary(e,"Codex",t)}async verifyCliBinary(t,e,s){return await new Promise(n=>{let a=qe(t,["--version"]),r="";a.stderr.on("data",o=>{r+=o.toString()}),a.on("close",o=>{let c=o===0;c||console.error(`Agent Fleet: ${e} CLI verification failed`,r),s&&new Ce.Notice(c?`${e} CLI available.`:`${e} CLI verification failed \u2014 check ${e} CLI Path in settings.`),n(c)}),a.on("error",o=>{console.error(`Agent Fleet: ${e} CLI verification error`,o),s&&new Ce.Notice(`${e} CLI verification failed \u2014 check ${e} CLI Path in settings.`),n(!1)})})}async openPath(t){let e=this.app.vault.getAbstractFileByPath((0,Ce.normalizePath)(t));e instanceof Ce.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 Ce.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 Ce.Notice(`Unknown agent: ${t}`);return}await this.openChatView(t)}async deleteAgent(t){if(!this.repository.getAgentByName(t)){new Ce.Notice(`Unknown agent: ${t}`);return}let s=this.repository.getTasksForAgent(t),n=this.runtime.getRecentRuns().filter(o=>o.agent===t),a=this.repository.getMemoryPath(t),r=!!this.app.vault.getAbstractFileByPath(a);new Xs(this.app,{agentName:t,taskCount:s.length,runCount:n.length,hasMemory:r},async o=>{let c=await this.repository.deleteAgent(t,o);await new Promise(l=>setTimeout(l,200)),await this.refreshFromVault(),new Ce.Notice(`Deleted agent "${t}" (${c.trashedFiles.length} files moved to trash)`),await this.navigateDashboard("agents")}).open()}async toggleAgent(t,e){let s=this.repository.getAgentByName(t);if(!s)return;let n=this.app.vault.getAbstractFileByPath(s.filePath);if(!(n instanceof Ce.TFile))return;let a=await this.app.vault.cachedRead(n),{frontmatter:r,body:o}=Q(a);r.enabled=e,await this.app.vault.modify(n,z(r,o)),await this.refreshFromVault()}addCommands(){this.addCommand({id:"open-dashboard",name:"Open Dashboard",callback:()=>void this.activateDashboardView()}),this.addCommand({id:"open-agents-panel",name:"Open Agents Panel",callback:()=>void this.activateAgentsView()}),this.addCommand({id:"open-chat",name:"Open Agent Chat",callback:()=>{let t=this.app.workspace.getLeavesOfType(rt);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 Ce.Notice("No agents configured.")}}),this.addCommand({id:"pause-all",name:"Pause All",callback:()=>{this.runtime.scheduler.pauseAll(),new Ce.Notice("Agent Fleet paused.")}}),this.addCommand({id:"resume-all",name:"Resume All",callback:()=>{this.runtime.scheduler.resumeAll(),new Ce.Notice("Agent Fleet resumed.")}}),this.addCommand({id:"view-fleet-status",name:"View Fleet Status",callback:()=>{let t=this.runtime.getFleetStatus();new Ce.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 Ce.TFile&&t.path.startsWith(`${this.settings.fleetFolder}/`)&&this.debouncedVaultRefresh()})),this.registerEvent(this.app.vault.on("modify",t=>{t instanceof Ce.TFile&&t.path.startsWith(`${this.settings.fleetFolder}/`)&&this.debouncedVaultRefresh()})),this.registerEvent(this.app.vault.on("rename",t=>{t.path.startsWith(`${this.settings.fleetFolder}/`)&&this.debouncedVaultRefresh()})),this.registerEvent(this.app.vault.on("delete",t=>{t.path.startsWith(`${this.settings.fleetFolder}/`)&&this.debouncedVaultRefresh()}))}registerRuntimeListeners(){this.runtimeUnsubscribe?.(),this.runtimeUnsubscribe=this.runtime.subscribe(()=>{this.notifyViews(),this.refreshStatusBar()})}notifyViews(){for(let t of this.subscribedViews)t.render()}async resolveClaudeCliPath(t){return this.resolveCliPathFrom(di(t),t)}async maybeResolveCodexCliPath(){this.runtime?.getSnapshot().agents.some(e=>Bt(e.adapter)==="codex")&&(this.settings.codexCliPath=await this.resolveCliPathFrom(Xn(this.settings.codexCliPath),this.settings.codexCliPath))}async resolveCliPathFrom(t,e){for(let s of t)if(Qn(s)&&(0,_o.existsSync)(s)||!Qn(s)&&await new Promise(a=>{let r=qe(s,["--version"]);r.on("close",o=>a(o===0)),r.on("error",()=>a(!1))}))return s;return e}getLeafForView(t,e){let s=this.app.workspace.getLeavesOfType(t)[0];return s||(e==="right"?this.app.workspace.getRightLeaf(!1)??this.app.workspace.getLeaf(!0):this.app.workspace.getLeftLeaf(!1)??this.app.workspace.getLeaf(!1))}};
|