agentix-cli 0.9.0 → 0.13.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 +51 -1
- package/dist/agent-5KIMJI3F.js +2 -0
- package/dist/{chunk-SBUX74OU.js → chunk-D6EDMQRV.js} +5 -5
- package/dist/chunk-HI24KAEY.js +54 -0
- package/dist/chunk-HI24KAEY.js.map +1 -0
- package/dist/chunk-MCJUAE6V.js +6 -0
- package/dist/chunk-MCJUAE6V.js.map +1 -0
- package/dist/chunk-QOYAX2FT.js +2 -0
- package/dist/chunk-QOYAX2FT.js.map +1 -0
- package/dist/chunk-QZP65I2H.js +45 -0
- package/dist/chunk-QZP65I2H.js.map +1 -0
- package/dist/chunk-T25UEXVK.js +126 -0
- package/dist/chunk-T25UEXVK.js.map +1 -0
- package/dist/chunk-VH23CDHL.js +157 -0
- package/dist/chunk-VH23CDHL.js.map +1 -0
- package/dist/chunk-WJ6ZF6EZ.js +6 -0
- package/dist/chunk-WJ6ZF6EZ.js.map +1 -0
- package/dist/cli.js +26 -29
- package/dist/cli.js.map +1 -1
- package/dist/compaction-7I3E5QUO.js +28 -0
- package/dist/compaction-7I3E5QUO.js.map +1 -0
- package/dist/config-HAD5E4YX.js +2 -0
- package/dist/index.d.ts +1084 -76
- package/dist/index.js +39 -35
- package/dist/index.js.map +1 -1
- package/dist/loader-YQE6XJBT.js +2 -0
- package/dist/registry-CDWY7CLL.js +2 -0
- package/dist/workspace-setup-QQTCJY4B.js +2 -0
- package/dist/workspace-setup-QQTCJY4B.js.map +1 -0
- package/package.json +1 -1
- package/dist/agent-QYQVGKLM.js +0 -2
- package/dist/chunk-BAIQJHQF.js +0 -64
- package/dist/chunk-BAIQJHQF.js.map +0 -1
- package/dist/chunk-BHDLKX3G.js +0 -6
- package/dist/chunk-BHDLKX3G.js.map +0 -1
- package/dist/chunk-FRFR27IN.js +0 -3
- package/dist/chunk-FRFR27IN.js.map +0 -1
- package/dist/chunk-IVVVYXPH.js +0 -136
- package/dist/chunk-IVVVYXPH.js.map +0 -1
- package/dist/chunk-X32247GM.js +0 -2
- package/dist/chunk-X32247GM.js.map +0 -1
- package/dist/compaction-E3MQRLTL.js +0 -28
- package/dist/compaction-E3MQRLTL.js.map +0 -1
- package/dist/config-MSWKC466.js +0 -2
- package/dist/loader-6VSM6FSY.js +0 -2
- package/dist/registry-FP6FUOXF.js +0 -2
- /package/dist/{agent-QYQVGKLM.js.map → agent-5KIMJI3F.js.map} +0 -0
- /package/dist/{chunk-SBUX74OU.js.map → chunk-D6EDMQRV.js.map} +0 -0
- /package/dist/{config-MSWKC466.js.map → config-HAD5E4YX.js.map} +0 -0
- /package/dist/{loader-6VSM6FSY.js.map → loader-YQE6XJBT.js.map} +0 -0
- /package/dist/{registry-FP6FUOXF.js.map → registry-CDWY7CLL.js.map} +0 -0
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{g as a,h as b,i as c}from"./chunk-VH23CDHL.js";import"./chunk-HI24KAEY.js";import"./chunk-34UAFPK4.js";import"./chunk-ZRYBSI5G.js";import"./chunk-QOYAX2FT.js";import"./chunk-4YCH6IZV.js";export{c as AgentRegistry,b as getGlobalRegistry,a as setGlobalRegistry};
|
|
2
|
+
//# sourceMappingURL=registry-CDWY7CLL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
CHANGED
package/dist/agent-QYQVGKLM.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{s as a,t as b,u as c}from"./chunk-SBUX74OU.js";import"./chunk-SFQUP3BP.js";import"./chunk-CJ45Y2IR.js";import"./chunk-X32247GM.js";import"./chunk-KXZMYLHQ.js";import"./chunk-M7HKBG3V.js";import"./chunk-FRFR27IN.js";import"./chunk-DSYKYZMT.js";import"./chunk-4YCH6IZV.js";export{a as createAgentContext,b as generate,c as generateStream};
|
|
2
|
-
//# sourceMappingURL=agent-QYQVGKLM.js.map
|
package/dist/chunk-BAIQJHQF.js
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import{g as fe,i as ye}from"./chunk-IVVVYXPH.js";import{f as pe,g as me}from"./chunk-CJ45Y2IR.js";import{b as be,c as we}from"./chunk-BHDLKX3G.js";import{e as _}from"./chunk-DSYKYZMT.js";import{a as Ce,b as B}from"./chunk-4YCH6IZV.js";var U=class{baseUrl;token;constructor(e,t){this.baseUrl=e.replace(/\/$/,""),this.token=t}async getAgentCard(){let e=await fetch(`${this.baseUrl}/.well-known/agent-card.json`,{headers:this.headers()});if(!e.ok)throw new Error(`Failed to fetch agent card: ${e.status}`);return e.json()}async sendTask(e,t){let n=await this.rpc("tasks/send",{id:`task-${Date.now().toString(36)}`,message:{role:"user",parts:[{type:"text",text:e}]},metadata:t});if(n.error)throw new Error(`A2A error: ${n.error.message}`);return n.result}async*sendTaskStream(e,t){let n=JSON.stringify({jsonrpc:"2.0",id:1,method:"tasks/sendSubscribe",params:{id:`task-${Date.now().toString(36)}`,message:{role:"user",parts:[{type:"text",text:e}]},metadata:t}}),s=await fetch(this.baseUrl,{method:"POST",headers:{...this.headers(),"Content-Type":"application/json",Accept:"text/event-stream"},body:n});if(!s.ok||!s.body)throw new Error(`A2A stream error: ${s.status}`);let i=s.body.getReader(),r=new TextDecoder,o="";for(;;){let{done:a,value:c}=await i.read();if(a)break;o+=r.decode(c,{stream:!0});let l=o.split(`
|
|
2
|
-
`);o=l.pop()||"";for(let h of l)if(h.startsWith("data: "))try{let g=JSON.parse(h.slice(6));yield{state:g.state,message:g.message?.parts?.[0]?.text,final:g.final}}catch{}}}async getTask(e){let t=await this.rpc("tasks/get",{id:e});if(t.error)throw new Error(`A2A error: ${t.error.message}`);return t.result}async cancelTask(e){let t=await this.rpc("tasks/cancel",{id:e});if(t.error)throw new Error(`A2A error: ${t.error.message}`);return t.result}async rpc(e,t){let n=await fetch(this.baseUrl,{method:"POST",headers:{...this.headers(),"Content-Type":"application/json"},body:JSON.stringify({jsonrpc:"2.0",id:Date.now(),method:e,params:t})});if(!n.ok)throw new Error(`A2A HTTP error: ${n.status}`);return n.json()}headers(){let e={};return this.token&&(e.Authorization=`Bearer ${this.token}`),e}};var F=class{peers=new Map;healthTimer;config;log;constructor(e,t=console.error.bind(console,"[mesh]")){this.config=e,this.log=t;for(let n of e.mesh.peers)this.peers.set(n.name,{peer:n,client:new U(n.url,n.token),healthy:!1,agents:[]})}async start(){this.log(`Mesh starting with ${this.peers.size} peer(s)`),await this.discoverAll();let e=this.config.mesh.healthCheck.interval*1e3;this.healthTimer=setInterval(()=>this.discoverAll(),e)}async stop(){this.healthTimer&&clearInterval(this.healthTimer)}async discoverAll(){let e=await Promise.allSettled(Array.from(this.peers.entries()).map(([n,s])=>this.discoverPeer(n,s))),t=Array.from(this.peers.values()).filter(n=>n.healthy).length;this.log(`Discovery complete: ${t}/${this.peers.size} peers healthy`)}async discoverPeer(e,t){let n=this.config.mesh.healthCheck.timeout*1e3;try{let s=new AbortController,i=setTimeout(()=>s.abort(),n),r=await t.client.getAgentCard();clearTimeout(i),t.healthy=!0,t.lastCheck=new Date,t.agentCard=r,t.agents=r.skills||[],this.log(`Peer "${e}" healthy: ${r.name} (${t.agents.length} skills)`)}catch(s){t.healthy=!1,t.lastCheck=new Date,this.log(`Peer "${e}" unreachable: ${s.message}`)}}async sendTask(e,t,n){let s=this.peers.get(e);if(!s)throw new Error(`Unknown peer: ${e}`);if(!s.healthy)throw new Error(`Peer "${e}" is not healthy`);let i=n||s.agents[0]?.id;if(!i)throw new Error(`Peer "${e}" has no agents`);let r=`${s.peer.url}/task`,o={"Content-Type":"application/json"};s.peer.token&&(o.Authorization=`Bearer ${s.peer.token}`);let a=await fetch(r,{method:"POST",headers:o,body:JSON.stringify({agent:i,message:t})});if(!a.ok)throw new Error(`Peer "${e}" /task error: ${a.status}`);let c=await a.json();if(c.error)throw new Error(`Peer "${e}" agent error: ${c.error}`);return c.content||"No response"}findPeerWithSkill(e){for(let t of this.peers.values())if(t.healthy&&t.agents.some(n=>n.id===e))return t}directory(){return Array.from(this.peers.entries()).map(([e,t])=>({peer:e,peerUrl:t.peer.url,healthy:t.healthy,skills:t.agents,lastCheck:t.lastCheck}))}};import{readFileSync as xe,writeFileSync as Ie,existsSync as Re,mkdirSync as Pe}from"fs";import{resolve as ve}from"path";var _e=30,Ee=6e3,J=class{dir;cache=new Map;constructor(e=ve(process.cwd(),".agentx/groups")){this.dir=e,Pe(this.dir,{recursive:!0})}add(e,t,n){let s=this.load(e);for(s.push({sender:t,text:n.slice(0,500),timestamp:Date.now()});s.length>_e;)s.shift();this.cache.set(e,s),this.save(e,s)}buildContext(e){let t=this.load(e);if(t.length<=1)return"";let n=["[Recent group conversation]"],s=0;for(let i=t.length-2;i>=0;i--){let r=t[i],a=`[${new Date(r.timestamp).toLocaleTimeString("en-US",{hour:"2-digit",minute:"2-digit",hour12:!1})}] ${r.sender}: ${r.text}`;if(s+a.length>Ee)break;n.splice(1,0,a),s+=a.length}return n.length<=1?"":(n.push("[End of conversation \u2014 respond to the latest message]"),n.join(`
|
|
3
|
-
`))}getEntries(e){return[...this.load(e)]}filePath(e){let t=e.replace(/[^a-zA-Z0-9_-]/g,"_");return ve(this.dir,`${t}.json`)}load(e){if(this.cache.has(e))return this.cache.get(e);let t=this.filePath(e);if(!Re(t))return this.cache.set(e,[]),[];try{let n=JSON.parse(xe(t,"utf-8")),s=Array.isArray(n)?n:[];return this.cache.set(e,s),s}catch{return this.cache.set(e,[]),[]}}save(e,t){try{Ie(this.filePath(e),JSON.stringify(t))}catch{}}};var De={minChars:80,maxChars:4e3,idleFlushMs:2e3,minIntervalMs:1500,humanPacing:!1},je={telegram:{minChars:60,maxChars:4096,minIntervalMs:1500},whatsapp:{minChars:40,maxChars:3e3,minIntervalMs:2e3,humanPacing:!0},discord:{minChars:80,maxChars:2e3,minIntervalMs:1e3},gitlab:{minChars:200,maxChars:6e4,minIntervalMs:3e3}},W=class{config;buffer="";emitter;lastEmitTime=0;idleTimer;insideCodeFence=!1;fencePattern=/^```/;constructor(e,t,n){let s=n?je[n]:{};this.config={...De,...s,...t},this.emitter=e}push(e){this.buffer+=e,this.idleTimer&&clearTimeout(this.idleTimer),this.updateCodeFenceState(e),this.shouldEmit()?this.emit():this.idleTimer=setTimeout(()=>{this.buffer.length>0&&this.emit()},this.config.idleFlushMs)}flush(){this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=void 0),this.buffer.length>0&&(this.emitImmediate(this.buffer),this.buffer="")}get pendingLength(){return this.buffer.length}shouldEmit(){return this.insideCodeFence||this.buffer.length<this.config.minChars||Date.now()-this.lastEmitTime<this.config.minIntervalMs?!1:this.findBreakPoint()>0}emit(){let e=this.findBreakPoint();if(e<=0&&this.buffer.length<this.config.maxChars)return;let t=e>0?e:Math.min(this.buffer.length,this.config.maxChars),n=this.buffer.slice(0,t);if(this.buffer=this.buffer.slice(t),this.config.humanPacing){let s=800+Math.random()*1700;setTimeout(()=>this.emitImmediate(n),s)}else this.emitImmediate(n)}emitImmediate(e){this.lastEmitTime=Date.now();try{this.emitter(e)}catch{}}findBreakPoint(){let e=this.buffer,t=Math.min(e.length,this.config.maxChars),n=e.lastIndexOf(`
|
|
4
|
-
|
|
5
|
-
`,t);if(n>=this.config.minChars)return n+2;let s=e.lastIndexOf(`
|
|
6
|
-
`,t);if(s>=this.config.minChars)return s+1;for(let i=t-1;i>=this.config.minChars;i--)if((e[i]==="."||e[i]==="!"||e[i]==="?")&&(i+1>=e.length||e[i+1]===" "||e[i+1]===`
|
|
7
|
-
`))return i+1;if(e.length>=this.config.maxChars){let i=e.lastIndexOf(" ",t);return i>=this.config.minChars?i+1:t}return-1}updateCodeFenceState(e){let t=e.split(`
|
|
8
|
-
`);for(let n of t)this.fencePattern.test(n.trim())&&(this.insideCodeFence=!this.insideCodeFence)}};var Le=4e3,ae=class{registry;config;channels=new Map;hooks;mesh;groupLog;log;constructor(e,t,n,s=console.error.bind(console,"[router]")){this.registry=e,this.config=t,this.hooks=n,this.log=s,this.groupLog=new J}setMesh(e){this.mesh=e}addChannel(e){this.channels.set(e.name,e),e.onMessage(t=>this.handleMessage(e,t))}async sendOutbound(e){let t=this.channels.get(e.channel);if(!t)throw new Error(`Unknown channel: "${e.channel}". Available: ${[...this.channels.keys()].join(", ")}`);let n=e.accountId;return t.name==="telegram"&&!n&&e.agentId&&(n=this.getAccountForAgent(e.agentId)),this.log(`Outbound [${e.channel}] -> ${e.chatId}: ${e.text.slice(0,80)}`),t.name==="telegram"&&n?this.adapterSend(t,{...e,accountId:n}):t.send(e)}getChannelNames(){return[...this.channels.keys()]}async startAll(){for(let[e,t]of this.channels)this.log(`Starting channel: ${e}`),await t.start()}async stopAll(){for(let[e,t]of this.channels)this.log(`Stopping channel: ${e}`),await t.stop()}async handleMessage(e,t){if(this.hooks?.has("pre:channel-message")){let y=await this.hooks.execute("pre:channel-message",{event:"pre:channel-message",channel:t.channel,sender:t.sender.name,text:t.text,group:t.group?.name});if(y.blocked){this.log(`Message blocked by hook: ${y.message}`);return}y.modified?.text&&(t={...t,text:y.modified.text})}if(t.group){let y=t.group.id;this.groupLog.add(y,t.sender.name,t.text)}let n=this.resolveAgent(t);if(!n)return;if(t.group&&t.channel==="telegram"){let y=this.getAccountForAgent(n);if(y&&y!==t.accountId)return}let s=t.group?.id||t.sender.id,r=this.registry.getAgent(n)?.name||n,o=this.getAccountForAgent(n)||t.accountId;this.log(`Routing [${t.channel}/${t.sender.name}] -> "${r}": ${t.text.slice(0,80)}`),this.adapterReact(e,s,t.id,"\u{1F440}",o);let a=this.startTypingLoop(e,s,o),c=typeof e.editMessage=="function",l,h="",g=c?new W(async y=>{if(h+=y,l)try{await this.adapterEdit(e,s,l,h,void 0,o)}catch{}else{let $=h.length>20?h:`_${r} is writing..._
|
|
9
|
-
|
|
10
|
-
${h}`;try{l=await this.adapterSend(e,{channel:t.channel,chatId:s,text:$,replyTo:t.id,accountId:o})}catch{}}},void 0,t.channel):void 0,d=g?(y,$)=>{g.push(y)}:void 0;if(t.channelMeta?.agents){for(let y of t.channelMeta.agents)if(!y.handle){let $=this.registry.getAgent(y.id);$&&(y.handle=$.mentions.find(k=>k.startsWith("@")),y.name=$.name)}}let p=t.group?this.groupLog.buildContext(s):"",m=p?`${p}
|
|
11
|
-
|
|
12
|
-
${t.sender.name}: ${t.text}`:t.text,f=await this.registry.execute({message:m,agentId:n,context:{channel:t.channel,sender:t.sender.name,senderId:t.sender.id,senderUsername:t.sender.username,group:t.group?.name,chatId:s,mediaPath:t.media?.path,mediaType:t.media?.type,replyToText:t.replyToText,channelMeta:t.channelMeta}},d);if(clearInterval(a),g?.flush(),f.error){if(f.error.startsWith("__queued__")){clearInterval(a),this.log(`Message queued for ${r}`);return}this.log(`Agent error: ${f.error}`);let y=`Error: ${f.error}`;l?await this.adapterEdit(e,s,l,y,"plain",o):await this.adapterSend(e,{channel:t.channel,chatId:s,text:y,replyTo:t.id,parseMode:"plain",accountId:o});return}let u=f.content;if(this.hooks?.has("post:channel-message")){let y=await this.hooks.execute("post:channel-message",{event:"post:channel-message",channel:t.channel,sender:t.sender.name,response:u,agentId:n});if(y.blocked){this.log(`Response blocked by hook: ${y.message}`);return}y.modified?.response&&(u=y.modified.response)}t.channel==="gitlab"&&u&&(this.channels.get("gitlab")?.getAgentToken?.(n)||(u=`> **${r}** (${n})
|
|
13
|
-
|
|
14
|
-
${u}`)),t.channel==="whatsapp"&&u&&(u=`*${r}*
|
|
15
|
-
|
|
16
|
-
${u}`);let w;u&&(l?(await this.adapterEdit(e,s,l,u,void 0,o),w=l):w=await this.adapterSend(e,{channel:t.channel,chatId:s,text:u,replyTo:t.id,accountId:o,agentId:n})),t.group&&u&&this.groupLog.add(s,r,u),u&&w&&["telegram","whatsapp","discord"].includes(t.channel)&&this.handleBotToBotChain(e,t,n,u,w,0).catch(y=>{this.log(`Bot-to-bot error: ${y.message}`)})}async handleBotToBotChain(e,t,n,s,i,r,o=new Set){if(r>=ae.MAX_BOT_CHAIN_DEPTH){this.log(`Bot-to-bot: max depth (${r}) reached, stopping`);return}o.add(n);for(let[a,c]of Object.entries(this.config.agents)){if(a===n)continue;if(o.has(a)){this.log(`Bot-to-bot: "${a}" already participated, stopping chain`);continue}if(!c.mentions.filter(m=>m.startsWith("@")).some(m=>s.toLowerCase().includes(m.toLowerCase())))continue;this.log(`Bot-to-bot [${r+1}]: "${n}" -> "${a}"`);let g=t.group?.id||t.sender.id,d=this.getAccountForAgent(a),p=this.getAccountForAgent(n);try{this.adapterReact(e,g,i,"\u{1F440}",d);let m=this.startTypingLoop(e,g,d),f=r===0?`[Original from ${t.sender.name}]: ${t.text}
|
|
17
|
-
|
|
18
|
-
[${n} said]: ${s}`:s,u=await this.registry.execute({message:f,agentId:a,context:{channel:t.channel,sender:`agent:${n}`,group:t.group?.name}});if(clearInterval(m),u.content&&!u.error){let w=u.content;t.channel==="whatsapp"&&(w=`*${c.name}*
|
|
19
|
-
|
|
20
|
-
${w}`);let v=await this.adapterSend(e,{channel:t.channel,chatId:g,text:w,accountId:d});v&&u.content&&await this.handleBotToBotChain(e,t,a,u.content,v,r+1,o)}else u.error&&this.log(`Bot-to-bot "${a}" error: ${u.error}`)}catch(m){this.log(`Bot-to-bot "${a}" failed: ${m.message}`)}break}}async adapterSend(e,t){return e.name==="telegram"&&t.accountId?e.send({...t,parseMode:t.parseMode,accountId:t.accountId}):e.send(t)||""}async adapterEdit(e,t,n,s,i,r){return e.name==="telegram"&&r?e.editMessage(t,n,s,i,r):e.editMessage?.(t,n,s,i)??!1}adapterReact(e,t,n,s,i){e.name==="telegram"&&i?e.react(t,n,s,i):e.react?.(t,n,s)}startTypingLoop(e,t,n){let s=()=>{e.name==="telegram"&&n?e.sendTyping(t,n):e.sendTyping?.(t)};return s(),setInterval(s,Le)}async handleViaMesh(e,t){if(!this.mesh)return!1;let n=t.text.toLowerCase(),s=this.mesh.directory();for(let i of s)if(i.healthy){for(let r of i.skills)if(n.includes(r.id.toLowerCase())||n.includes(r.name.toLowerCase())){this.log(`Mesh routing [${t.channel}/${t.sender.name}] -> peer "${i.peer}" agent "${r.id}"`);let o=t.group?.id||t.sender.id,a=t.accountId;this.adapterReact(e,o,t.id,"\u{1F440}",a);let c=this.startTypingLoop(e,o,a);try{let l=await this.mesh.sendTask(i.peer,t.text,r.id);if(clearInterval(c),l){let h=`**${r.name}** _(${i.peer})_:
|
|
21
|
-
|
|
22
|
-
`;await this.adapterSend(e,{channel:t.channel,chatId:o,text:h+l,replyTo:t.id,accountId:a})}return!0}catch(l){return clearInterval(c),this.log(`Mesh routing error: ${l.message}`),await this.adapterSend(e,{channel:t.channel,chatId:o,text:`Error from ${i.peer}/${r.name}: ${l.message}`,replyTo:t.id,parseMode:"plain",accountId:a}),!0}}}return!1}getAccountForAgent(e){for(let[t,n]of Object.entries(this.config.channels.telegram.accounts))if(n.agentBinding===e)return t}resolveAgent(e){if(e.channel==="gitlab")return e.resolvedAgent?(this.log(`GitLab agent resolved by adapter: ${e.resolvedAgent}`),e.resolvedAgent):void 0;if(e.resolvedAgent)return e.resolvedAgent;if(!e.group)return e.channel==="telegram"?this.config.channels.telegram.accounts[e.accountId]?.agentBinding:e.channel==="whatsapp"?this.config.channels.whatsapp.defaultAgent:void 0;if(e.channel==="telegram"&&this.config.channels.telegram.policy.group==="mention-required"){let s=this.registry.findByMention(e.text);return s||void 0}let t=this.registry.findByMention(e.text);return t||(e.channel==="telegram"?this.config.channels.telegram.accounts[e.accountId]?.agentBinding:this.config.channels.whatsapp.defaultAgent)}},E=ae;B(E,"MAX_BOT_CHAIN_DEPTH",3);function q(b){return b.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}function ce(b){let t=b.replace(/\\([_*\[\]()~`>#+=|{}.!\\-])/g,"$1").split(`
|
|
23
|
-
`),n=[],s=!1,i="",r=[],o=!1,a=[],c=!1;for(let h=0;h<t.length;h++){let g=t[h];if(g.trimStart().startsWith("```"))if(s){s=!1;let f=q(r.join(`
|
|
24
|
-
`)),u=i?`// ${i}
|
|
25
|
-
`:"";n.push(`<pre><code>${u}${f}</code></pre>`),i="";continue}else{s=!0,i=g.trimStart().slice(3).trim(),r=[];continue}if(s){r.push(g);continue}if(o&&!g.trimStart().startsWith(">")&&(n.push("</blockquote>"),o=!1),c&&!g.trim().startsWith("|")&&(c=!1,a=[]),g.trimStart().startsWith("> ")){let f=g.replace(/^>\s*/,"");o||(n.push("<blockquote>"),o=!0),n.push(C(f));continue}let d=g.match(/^(#{1,6})\s+(.+)$/);if(d){n.push(""),n.push(`<b>${C(d[2])}</b>`);continue}if(/^[-*_]{3,}\s*$/.test(g.trim())){n.push("\u2014\u2014\u2014");continue}if(g.trim().startsWith("|")&&g.trim().endsWith("|")){let f=g.split("|").slice(1,-1).map(u=>u.trim());if(/^\|[\s\-:|]+\|$/.test(g.trim())){c=!0;continue}if(!c){a=f,c=!0;continue}if(a.length>0&&f.length>0)if(f.length>=2){let u=[];for(let w=0;w<f.length;w++)if(w===0)u.push(`<b>${C(f[w])}</b>`);else{let v=a[w]?`${C(a[w])}: `:"";u.push(`${v}${C(f[w])}`)}n.push(`\u2022 ${u.join(" \u2014 ")}`)}else n.push(`\u2022 ${C(f[0])}`);continue}let p=g.match(/^(\s*)[-*+]\s+(.+)$/);if(p){let f=p[1].length>0?" ":"";n.push(`${f}\u2022 ${C(p[2])}`);continue}let m=g.match(/^(\s*)\d+[.)]\s+(.+)$/);if(m){let f=m[1].length>0?" ":"",u=g.match(/^(\s*)(\d+)/)?.[2]||"1";n.push(`${f}${u}. ${C(m[2])}`);continue}if(!g.trim()){n.push("");continue}n.push(C(g))}s&&n.push(`<pre><code>${q(r.join(`
|
|
26
|
-
`))}</code></pre>`),o&&n.push("</blockquote>");let l=n.join(`
|
|
27
|
-
`).trim();return l=Ne(l),l}function C(b){let e=q(b);return e=e.replace(/`([^`]+)`/g,"<code>$1</code>"),e=e.replace(/\[([^\]]+)\]\(([^)]+)\)/g,(t,n,s)=>`<a href="${q(s)}">${n}</a>`),e=e.replace(/\*\*\*(.+?)\*\*\*/g,"<b><i>$1</i></b>"),e=e.replace(/\*\*(.+?)\*\*/g,"<b>$1</b>"),e=e.replace(/(?<!\*)\*([^*]+?)\*(?!\*)/g,"<i>$1</i>"),e=e.replace(/~~(.+?)~~/g,"<s>$1</s>"),e=e.replace(/\|\|(.+?)\|\|/g,"<tg-spoiler>$1</tg-spoiler>"),e}var Oe=/(?<=\w)\.(ts|js|py|rs|go|rb|cs|sh|md|yml|yaml|toml|json|env|css|html|xml|sql|tf|hcl)(?=[\s,;:)\]}<]|$)/gi;function Ne(b){let e=b.split(/(<\/?(?:code|pre|a)[^>]*>)/gi),t=!1;return e.map(n=>/<(?:code|pre|a)\b/i.test(n)?(t=!0,n):/<\/(?:code|pre|a)>/i.test(n)?(t=!1,n):t?n:n.replace(Oe,"<code>.$1</code>")).join("")}import{readFileSync as Ge,writeFileSync as He,mkdirSync as Be,existsSync as Ue}from"fs";import{resolve as Fe,dirname as Je}from"path";var le=class{data={};filePath;dirty=!1;constructor(e){this.filePath=Fe(e,".agentx/telegram/groups.json"),this.load()}load(){try{Ue(this.filePath)&&(this.data=JSON.parse(Ge(this.filePath,"utf-8")))}catch{this.data={}}}save(){try{Be(Je(this.filePath),{recursive:!0}),He(this.filePath,JSON.stringify(this.data,null,2)),this.dirty=!1}catch{}}setBotStatus(e,t,n,s,i,r){this.data[e]||(this.data[e]={name:t,bots:{}}),t&&(this.data[e].name=t),this.data[e].bots[n]={username:i,agentId:s,status:r,updatedAt:new Date().toISOString()},this.dirty=!0,this.save()}getGroupBots(e){let t=this.data[e];return t?Object.entries(t.bots).filter(([,n])=>n.status!=="left"&&n.status!=="kicked").map(([n,s])=>({accountId:n,agentId:s.agentId,username:s.username})):[]}hasGroup(e){return!!this.data[e]}},z=class{name="telegram";accounts;offsets=new Map;handler;polling=!1;log;constructor(e,t=console.error.bind(console,"[telegram]")){this.accounts=new Map(Object.entries(e)),this.log=t,this.groupStore=new le(process.cwd())}onMessage(e){this.handler=e}async start(){this.polling=!0;let e=Array.from(this.accounts.entries());this.log(`${e.length} Telegram account(s) to start`);for(let t=0;t<e.length;t++){let[n,s]=e[t];this.log(`Starting polling for account "${n}" (${t+1}/${e.length})`);try{await this.apiCall(s.token,"deleteWebhook",{drop_pending_updates:!1}).catch(()=>{});let i=await this.apiCall(s.token,"getMe"),r=i.result?.id,o=i.result?.username;this.log(`Bot @${o} ready (account: ${n})`),r&&this.botInfo.set(n,{userId:r,username:o||n}),this.pollLoop(n,s)}catch(i){this.log(`Failed to verify bot for account "${n}": ${i.message}`)}t<e.length-1&&await new Promise(i=>setTimeout(i,300))}this.log(`All ${e.length} Telegram account(s) started`)}async stop(){this.polling=!1;let e=Array.from(this.accounts.entries()).map(async([t,n])=>{try{await this.apiCall(n.token,"getUpdates",{offset:-1,timeout:0})}catch{}});await Promise.allSettled(e)}getTokenForAccount(e){return this.accounts.get(e)?.token}getDefaultToken(){let[,e]=Array.from(this.accounts.entries())[0];return e?.token}resolveToken(e,t){if(t){let n=this.getTokenForAccount(t);if(n)return n}return this.chatAccountMap.get(e)?this.getTokenForAccount(this.chatAccountMap.get(e)):this.getDefaultToken()}chatAccountMap=new Map;botInfo=new Map;groupStore;async send(e){let t=this.resolveToken(e.chatId,e.accountId);if(!t)return this.log("No telegram token found for sending"),"";let n=4096,s=e.text.length>n?e.text.slice(0,n-3)+"...":e.text,i=e.parseMode==="markdown"||e.parseMode===void 0?ce(s):s,r={chat_id:e.chatId,text:i,parse_mode:"HTML"};e.replyTo&&(r.reply_to_message_id=parseInt(e.replyTo,10)),e.parseMode==="html"?(r.parse_mode="HTML",r.text=s):e.parseMode==="plain"&&(delete r.parse_mode,r.text=s);try{let o=await this.apiCall(t,"sendMessage",r);return String(o.result?.message_id||"")}catch(o){if(r.parse_mode){delete r.parse_mode,r.text=s;let a=await this.apiCall(t,"sendMessage",r);return String(a.result?.message_id||"")}throw o}}async editMessage(e,t,n,s,i){let r=this.resolveToken(e,i);if(!r)return!1;let o=4096,a=n.length>o?n.slice(0,o-3)+"...":n,c=s!=="html"&&s!=="plain"?ce(a):a,l={chat_id:e,message_id:parseInt(t,10),text:c,parse_mode:"HTML"};s==="html"?(l.parse_mode="HTML",l.text=a):s==="plain"&&(delete l.parse_mode,l.text=a);try{return await this.apiCall(r,"editMessageText",l),!0}catch(h){if(h.message?.includes("message is not modified"))return!0;if(l.parse_mode){delete l.parse_mode,l.text=a;try{return await this.apiCall(r,"editMessageText",l),!0}catch{return!1}}return!1}}async react(e,t,n="\u{1F440}",s){let i=this.resolveToken(e,s);if(i)try{await this.apiCall(i,"setMessageReaction",{chat_id:e,message_id:parseInt(t,10),reaction:[{type:"emoji",emoji:n}]})}catch{}}async sendTyping(e,t){let n=this.resolveToken(e,t);if(n)try{await this.apiCall(n,"sendChatAction",{chat_id:e,action:"typing"})}catch{}}async pollLoop(e,t){let n=0;for(;this.polling;)try{let s=this.offsets.get(e)||0,r=(await this.apiCall(t.token,"getUpdates",{offset:s||void 0,timeout:30,allowed_updates:["message","my_chat_member"]})).result||[];for(let o of r){if(this.offsets.set(e,o.update_id+1),o.message&&this.handler){let a=o.message,c=a.text||a.caption||"",l,h=a.photo&&a.photo.length>0,g=!!a.voice,d=!!a.audio,p=!!a.video,m=!!a.document;if(h||g||d||p||m){let $,k="application/octet-stream";if(h?($=a.photo[a.photo.length-1].file_id,k="image/jpeg",c||(c="[Photo attached \u2014 please describe what you see]")):g?($=a.voice.file_id,k=a.voice.mime_type||"audio/ogg",c||(c="[Voice message \u2014 please transcribe and respond]")):d?($=a.audio.file_id,k=a.audio.mime_type||"audio/mpeg",c||(c=`[Audio: ${a.audio.title||"audio file"}]`)):p?($=a.video.file_id,k=a.video.mime_type||"video/mp4",c||(c="[Video attached]")):m&&($=a.document.file_id,k=a.document.mime_type||"application/octet-stream",c||(c=`[Document: ${a.document.file_name||"file"}]`)),$)try{let N=(await this.apiCall(t.token,"getFile",{file_id:$})).result?.file_path;if(N){let ue=`https://api.telegram.org/file/bot${t.token}/${N}`,M=await fetch(ue);if(M.ok){let se=Buffer.from(await M.arrayBuffer()),R=k.split("/")[1]?.split(";")[0]||"bin",{mkdirSync:G,writeFileSync:H}=await import("fs"),{randomUUID:ie}=await import("crypto"),{resolve:A,join:x}=await import("path"),S=A(process.cwd(),".agentx/media/telegram");G(S,{recursive:!0});let P=a.document?.file_name||`${ie()}.${R}`,I=x(S,P);H(I,se),l={path:I,type:k,fileName:P}}}}catch(T){this.log(`Media download failed: ${T.message}`)}}if(!c)continue;let u=a.chat.type!=="private",w=String(a.chat.id),v;u&&(this.groupStore.hasGroup(w)||await this.seedGroupMembership(w,a.chat.title),v=await this.getChannelMeta(w));let y={id:String(a.message_id),channel:"telegram",accountId:e,sender:{id:String(a.from.id),name:[a.from.first_name,a.from.last_name].filter(Boolean).join(" "),username:a.from.username},group:u?{id:w,name:a.chat.title||""}:void 0,text:c,media:l,replyTo:a.reply_to_message?String(a.reply_to_message.message_id):void 0,replyToText:a.reply_to_message?a.reply_to_message.text||a.reply_to_message.caption||`[message from ${a.reply_to_message.from?.first_name||"unknown"}]`:void 0,timestamp:new Date(a.date*1e3),raw:o,channelMeta:v};this.chatAccountMap.set(String(a.chat.id),e),this.handler(y).catch($=>{this.log(`Error handling message: ${$.message}`)})}if(o.my_chat_member){let a=o.my_chat_member,c=String(a.chat.id),l=a.chat.title,h=a.new_chat_member.status,g=a.new_chat_member.user.username||e;this.groupStore.setBotStatus(c,l,e,t.agentBinding,g,h),this.log(`Group membership: @${g} is now "${h}" in "${l||c}"`)}}n=0}catch(s){n++;let i=Math.min(5e3*Math.pow(2,n-1),6e4);this.log(`Poll error (${e}): ${s.message} [retry in ${i/1e3}s, errors: ${n}]`),await new Promise(r=>setTimeout(r,i))}}async getChannelMeta(e){let t=this.groupStore.getGroupBots(e);if(t.length)return{channel:"telegram",agents:t.map(n=>({id:n.agentId,name:n.username,handle:`@${n.username}`})),facts:[`${t.length} bot(s) verified in this group`]}}async seedGroupMembership(e,t){this.log(`Seeding group membership for "${t||e}"`);for(let[n,s]of this.accounts){let i=this.botInfo.get(n);if(i)try{let o=(await this.apiCall(s.token,"getChatMember",{chat_id:Number(e),user_id:i.userId})).result?.status;o&&this.groupStore.setBotStatus(e,t,n,s.agentBinding,i.username,o)}catch{this.groupStore.setBotStatus(e,t,n,s.agentBinding,i.username,"left")}}this.log(`Seeded: ${this.groupStore.getGroupBots(e).length} bot(s) in "${t||e}"`)}async apiCall(e,t,n){let s=`https://api.telegram.org/bot${e}/${t}`,i=await fetch(s,{method:"POST",headers:{"Content-Type":"application/json"},body:n?JSON.stringify(n):void 0});if(!i.ok){let r=await i.text();throw new Error(`Telegram API error: ${i.status} ${r}`)}return i.json()}};import{mkdirSync as ke,writeFileSync as We}from"fs";import{resolve as $e,join as qe}from"path";import{randomUUID as ze}from"crypto";var V=class{name="whatsapp";sessionDir;defaultAgent;allowFrom;routes;handler;sock=null;reconnectAttempts=0;generation=0;sentMessageIds=new Set;log;constructor(e,t=console.error.bind(console,"[whatsapp]")){this.sessionDir=$e(e.sessionDir),this.defaultAgent=e.defaultAgent,this.allowFrom=e.allowFrom,this.routes=e.routes||[],this.log=t}resolveAgent(e,t,n){for(let s of this.routes){if(s.contact){let i=s.contact.replace(/\+/g,"");if(e.includes(i)||i.includes(e))return s.agent}if(s.group&&(t||n)){let i=s.group.toLowerCase();if(t?.toLowerCase().includes(i)||n?.toLowerCase().includes(i))return s.agent}}return this.defaultAgent}onMessage(e){this.handler=e}async start(){let e,t,n,s;try{s=await import("@whiskeysockets/baileys"),e=s.default||s.makeWASocket,t=s.useMultiFileAuthState,n=s.DisconnectReason}catch{this.log("WhatsApp requires @whiskeysockets/baileys. Install with:"),this.log(" npm install @whiskeysockets/baileys");return}ke(this.sessionDir,{recursive:!0});let i=++this.generation;if(this.sock){try{this.sock.end(void 0)}catch{}this.sock=null}let{state:r,saveCreds:o}=await t(this.sessionDir),a;try{let{version:l}=await s.fetchLatestBaileysVersion();a=l,this.log(`WhatsApp Web version: ${l.join(".")}`)}catch{this.log("Could not fetch WA version, using default")}let c={level:"silent",trace:()=>{},debug:()=>{},info:()=>{},warn:()=>{},fatal:()=>{},error:(...l)=>this.log("WA error:",...l),child:()=>c};this.sock=e({auth:{creds:r.creds,keys:s.makeCacheableSignalKeyStore?s.makeCacheableSignalKeyStore(r.keys,c):r.keys},...a?{version:a}:{},logger:c,printQRInTerminal:!1,browser:["agentx","server","1.0"],syncFullHistory:!1,markOnlineOnConnect:!1}),this.sock.ev.on("creds.update",o),this.sock.ev.on("messaging-history.set",l=>{this.log(`WA history sync: ${l.messages?.length||0} messages, ${l.isLatest?"latest":"partial"}`)}),this.sock.ev.on("connection.update",async l=>{let{connection:h,lastDisconnect:g,qr:d}=l;if(i===this.generation){if(d){this.log("Scan QR code with WhatsApp to connect:");try{let{default:p}=await import("qrcode-terminal");p.generate(d,{small:!0})}catch{this.log(`QR: ${d}`),this.log("Install qrcode-terminal for visual QR: npm install qrcode-terminal")}}if(h==="close"){let p=g?.error?.output?.statusCode,m=g?.error?.output?.payload?.message||"";if(this.log(`WhatsApp connection closed (status: ${p}, reason: ${m})`),p===n?.loggedOut||p===401)this.log("Logged out. Delete session dir and restart to re-scan QR.");else if(p===440||p===408){if(this.reconnectAttempts++,this.reconnectAttempts>=5){this.log(`WhatsApp: giving up after ${this.reconnectAttempts} conflict attempts. Another session is active for this number \u2014 close it or re-scan QR.`);return}let f=Math.min(1e4*Math.pow(2,this.reconnectAttempts-1),12e4);this.log(`Reconnecting in ${f/1e3}s (attempt ${this.reconnectAttempts}/5)...`),setTimeout(()=>this.start(),f)}else p===515?(this.log("Stream error, reconnecting in 5s..."),setTimeout(()=>this.start(),5e3)):p!==void 0&&(this.log("Reconnecting in 3s..."),setTimeout(()=>this.start(),3e3))}h==="open"&&this.log("WhatsApp connected")}}),this.sock.ev.on("messages.upsert",async l=>{if(this.reconnectAttempts>0&&(this.log(`WhatsApp connection stable, resetting reconnect counter (was ${this.reconnectAttempts})`),this.reconnectAttempts=0),this.log(`WA messages.upsert: ${l.messages?.length||0} messages, type: ${l.type}`),!!this.handler)for(let h of l.messages||[]){let g=(h.key.remoteJid||"").replace(/@.*/,"").slice(-6),d=!!(h.message?.conversation||h.message?.extendedTextMessage?.text);if(this.log(`WA msg: from=${g} fromMe=${h.key.fromMe} hasText=${d} type=${Object.keys(h.message||{}).join(",")}`),h.key.remoteJid==="status@broadcast")continue;if(h.key.id&&this.sentMessageIds.has(h.key.id)){this.sentMessageIds.delete(h.key.id);continue}if(h.key.fromMe){let A=this.sock?.user,x=h.key.remoteJid||"",S=A?.id?.replace(/:.*/,"")||"",P=A?.lid?.replace(/:.*/,"")||"",I=x.replace(/:.*/,"").replace(/@.*/,"");if(!(I===S||I===P))continue}let p=h.message?.conversation||h.message?.extendedTextMessage?.text||h.message?.imageMessage?.caption||h.message?.videoMessage?.caption||"",m=h.message||{},f=!!m.imageMessage,u=!!m.audioMessage,w=!!m.videoMessage,v=!!m.documentMessage,y=!!m.stickerMessage,$=f||u||w||v||y;if($&&!p&&(f?p="[Image attached \u2014 please describe what you see]":u?p="[Voice message attached \u2014 please transcribe and respond]":w?p="[Video attached]":v?p=`[Document: ${m.documentMessage?.fileName||"file"}]`:y&&(p="[Sticker]")),!p)continue;let k=h.key.remoteJid||"",T=k.endsWith("@g.us"),N=k.replace(/@.*$/,""),M=(T?h.key.participant||"":k).replace(/@.*$/,"");if(this.allowFrom?.length&&!h.key.fromMe&&!this.allowFrom.some(x=>{let S=x.replace(/\+/g,"");return M.includes(S)||N.includes(S)}))continue;let se=h.key.fromMe?"me":h.pushName||M,R;if(T&&this.sock)try{R=(await this.sock.groupMetadata(k)).subject}catch{}let G=this.resolveAgent(M,R,T?k:void 0);if(!G){this.log(`No route for ${T?`group ${R||k}`:M}, skipping`);continue}let H;if($&&this.sock)try{let x=await(await import("@whiskeysockets/baileys")).downloadMediaMessage(h,"buffer",{},{reuploadRequest:this.sock.updateMediaMessage,logger:this.sock.logger});if(x){let S=m.imageMessage?.mimetype||m.audioMessage?.mimetype||"audio/ogg",P=S.split("/")[1]?.split(";")[0]||"bin",I=$e(this.sessionDir,"../media/inbound");ke(I,{recursive:!0});let re=m.documentMessage?.fileName||`${ze()}.${P}`,oe=qe(I,re);We(oe,x),H={path:oe,type:S,fileName:re},this.log(`WA media saved: ${S} -> ${oe}`)}}catch(A){this.log(`WA media download failed: ${A.message}`)}let ie={id:h.key.id||String(Date.now()),channel:"whatsapp",accountId:"default",sender:{id:h.key.fromMe?(this.sock?.user?.id?.replace(/:.*/,"")||M)+"@s.whatsapp.net":M,name:se,username:M},group:T?{id:k,name:R||k}:void 0,text:p,media:H,replyTo:h.message?.extendedTextMessage?.contextInfo?.stanzaId,timestamp:new Date((h.messageTimestamp||0)*1e3),raw:h,resolvedAgent:G};this.handler(ie).catch(A=>{this.log(`Error handling message: ${A.message}`)})}})}async stop(){this.sock&&(this.sock.end(),this.sock=null)}async send(e){if(!this.sock)return this.log("WhatsApp not connected"),"";let t=e.chatId.includes("@")?e.chatId:`${e.chatId}@s.whatsapp.net`;try{let s=(await this.sock.sendMessage(t,{text:e.text}))?.key?.id||"";return s&&this.sentMessageIds.add(s),s}catch(n){return this.log(`Send error: ${n.message}`),""}}async editMessage(e,t,n){if(!this.sock)return!1;let s=e.includes("@")?e:`${e}@s.whatsapp.net`;try{return await this.sock.sendMessage(s,{text:n,edit:{remoteJid:s,id:t,fromMe:!0}}),!0}catch{return!1}}async sendTyping(e){if(!this.sock)return;let t=e.includes("@")?e:`${e}@s.whatsapp.net`;try{await this.sock.sendPresenceUpdate("composing",t)}catch{}}async react(e,t,n="\u{1F440}"){if(!this.sock)return;let s=e.includes("@")?e:`${e}@s.whatsapp.net`;try{await this.sock.sendMessage(s,{react:{text:n,key:{remoteJid:s,id:t}}})}catch{}}};import{writeFileSync as Te,mkdirSync as he,existsSync as X}from"fs";import{resolve as D}from"path";function j(b,e,t){let n=[];for(let s of b.split(","))if(s==="*")for(let i=e;i<=t;i++)n.push(i);else if(s.includes("/")){let[i,r]=s.split("/"),o=parseInt(r,10),a=i==="*"?e:parseInt(i,10);for(let c=a;c<=t;c+=o)n.push(c)}else if(s.includes("-")){let[i,r]=s.split("-").map(Number);for(let o=i;o<=r;o++)n.push(o)}else n.push(parseInt(s,10));return[...new Set(n)].sort((s,i)=>s-i)}function Ae(b,e,t){let n=b.trim().split(/\s+/);if(n.length!==5)throw new Error(`Invalid cron: ${b}`);let s=j(n[0],0,59),i=j(n[1],0,23),r=j(n[2],1,31),o=j(n[3],1,12),a=j(n[4],0,6),c=new Intl.DateTimeFormat("en-US",{timeZone:t,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",hour12:!1}),l=new Date(e.getTime()+6e4);l.setSeconds(0,0);let h=new Date(l.getTime()+366*24*60*60*1e3);for(;l<h;){let g=c.formatToParts(l),d=v=>parseInt(g.find(y=>y.type===v)?.value||"0",10),p=d("minute"),m=d("hour"),f=d("day"),u=d("month"),w=l.getDay();if(s.includes(p)&&i.includes(m)&&r.includes(f)&&o.includes(u)&&a.includes(w))return l;l.setTime(l.getTime()+6e4)}throw new Error(`No next run found for cron "${b}" within 1 year`)}var ge=5,de=[3e4,6e4,3e5,9e5,36e5],Y=class{jobs=new Map;timers=new Map;registry;hooks;runsDir;lastRunFile;running=!1;notifyCallback;log;constructor(e,t,n,s=console.error.bind(console,"[cron]")){this.registry=t,this.hooks=n,this.log=s,this.runsDir=D(process.cwd(),".agentx/cron/runs"),this.lastRunFile=D(process.cwd(),".agentx/cron/last-runs.json");for(let[i,r]of Object.entries(e.crons))this.jobs.set(i,{id:i,enabled:r.enabled,schedule:r.schedule,timezone:r.timezone,agent:r.agent,prompt:r.prompt,timeout:r.timeout,model:r.model,onError:r.onError,consecutiveErrors:0,totalRuns:0,totalFailures:0})}setNotifyCallback(e){this.notifyCallback=e}async start(){this.running=!0,X(this.runsDir)||he(this.runsDir,{recursive:!0});let e=this.detectMissedRuns();if(e.length>0){this.log(`Detected ${e.length} missed cron run(s) while daemon was down:`);for(let{jobId:t,missedAt:n}of e)this.log(` "${t}" should have run at ${n.toISOString()}`);this.executeMissedRuns(e).catch(t=>{this.log(`Error executing missed runs: ${t.message}`)})}for(let[t,n]of this.jobs){if(!n.enabled){this.log(`Job "${t}" is disabled, skipping`);continue}this.scheduleNext(t)}this.log(`${this.jobs.size} cron job(s) loaded, ${Array.from(this.jobs.values()).filter(t=>t.enabled).length} enabled`)}async stop(){this.running=!1;for(let e of this.timers.values())clearTimeout(e);this.timers.clear(),this.saveLastRuns()}scheduleNext(e){let t=this.jobs.get(e);if(!t||!t.enabled||!this.running)return;let n=this.timers.get(e);n&&clearTimeout(n);try{let s=Ae(t.schedule,new Date,t.timezone);t.nextRun=s,t.retryPending=!1;let i=s.getTime()-Date.now();this.log(`Job "${e}" next run: ${s.toISOString()} (in ${Math.round(i/1e3)}s)`);let r=setTimeout(()=>this.executeJob(e),i);this.timers.set(e,r)}catch(s){this.log(`Failed to schedule "${e}": ${s.message}`)}}scheduleRetry(e,t){let n=this.jobs.get(e);if(!n||!n.enabled||!this.running)return;if(t>=ge){this.log(`Job "${e}" exhausted all ${ge} retries, scheduling next regular run`),this.scheduleNext(e);return}let s=de[t]||de[de.length-1];n.retryPending=!0,this.log(`Job "${e}" retry ${t+1}/${ge} in ${Math.round(s/1e3)}s`);let i=setTimeout(()=>this.executeJob(e,t+1),s);this.timers.set(e,i)}async executeJob(e,t=0){let n=this.jobs.get(e);if(!n||!this.running)return;let s=t>0;if(this.hooks?.has("pre:cron-run")){let r=await this.hooks.execute("pre:cron-run",{event:"pre:cron-run",jobId:e,agent:n.agent,prompt:n.prompt});if(r.blocked){this.log(`Job "${e}" blocked by hook: ${r.message}`),this.scheduleNext(e);return}}this.log(`${s?`[retry ${t}] `:""}Executing job "${e}" -> agent "${n.agent}"`);let i=new Date;n.lastRun=i,n.totalRuns++;try{let r=await this.registry.execute({message:n.prompt,agentId:n.agent,context:{channel:"cron"}}),o={jobId:e,startedAt:i,completedAt:new Date,success:!r.error,response:r.content,error:r.error,duration:r.duration||Date.now()-i.getTime(),isRetry:s,retryAttempt:t};if(r.error){n.consecutiveErrors++,n.totalFailures++,n.lastError=r.error,this.log(`Job "${e}" failed (${n.consecutiveErrors} consecutive): ${r.error}`),await this.notifyFailure(n,r.error),n.onError==="disable"&&n.consecutiveErrors>=3&&(n.enabled=!1,this.log(`Job "${e}" disabled after ${n.consecutiveErrors} consecutive errors`),await this.notifyDisabled(n)),this.logRun(o),this.scheduleRetry(e,t);return}else n.consecutiveErrors>0&&this.log(`Job "${e}" recovered after ${n.consecutiveErrors} failure(s)`),n.consecutiveErrors=0,n.lastSuccess=new Date,n.lastError=void 0,n.retryPending=!1,this.log(`Job "${e}" completed in ${o.duration}ms`);this.logRun(o),this.hooks?.has("post:cron-run")&&await this.hooks.execute("post:cron-run",{event:"post:cron-run",jobId:e,success:o.success,duration:o.duration,error:o.error?new Error(o.error):void 0})}catch(r){n.consecutiveErrors++,n.totalFailures++,n.lastError=r.message,this.log(`Job "${e}" threw: ${r.message}`),await this.notifyFailure(n,r.message),this.scheduleRetry(e,t);return}this.saveLastRuns(),this.scheduleNext(e)}detectMissedRuns(){let e=[],t=this.loadLastRuns();if(!t)return e;for(let[n,s]of this.jobs){if(!s.enabled)continue;let i=t[n];if(!i)continue;let r=new Date(i);try{let o=Ae(s.schedule,r,s.timezone);o.getTime()<Date.now()&&e.push({jobId:n,missedAt:o})}catch{}}return e}async executeMissedRuns(e){for(let{jobId:t,missedAt:n}of e){let s=this.jobs.get(t);if(!(!s||!s.enabled)){this.log(`Running missed job "${t}" (was due at ${n.toISOString()})`);try{let i=await this.registry.execute({message:`[MISSED RUN \u2014 was scheduled for ${n.toISOString()}]
|
|
28
|
-
|
|
29
|
-
${s.prompt}`,agentId:s.agent,context:{channel:"cron"}}),r={jobId:t,startedAt:new Date,completedAt:new Date,success:!i.error,response:i.content,error:i.error,duration:i.duration||0,isRetry:!1};this.logRun(r),i.error?this.log(`Missed job "${t}" failed: ${i.error}`):(this.log(`Missed job "${t}" completed`),s.lastSuccess=new Date),s.lastRun=new Date,s.totalRuns++}catch(i){this.log(`Missed job "${t}" threw: ${i.message}`)}}}this.saveLastRuns()}async notifyFailure(e,t){if(!(e.onError!=="notify"&&e.consecutiveErrors<2)){if(!this.notifyCallback){this.log(`[ALERT] Cron "${e.id}" failed (${e.consecutiveErrors}x): ${t.slice(0,200)}`);return}try{await this.notifyCallback(e.id,e.agent,t,e.consecutiveErrors)}catch{}}}async notifyDisabled(e){if(!this.notifyCallback){this.log(`[ALERT] Cron "${e.id}" has been DISABLED after ${e.consecutiveErrors} consecutive failures`);return}try{await this.notifyCallback(e.id,e.agent,`AUTO-DISABLED after ${e.consecutiveErrors} consecutive failures. Last error: ${e.lastError||"unknown"}`,e.consecutiveErrors)}catch{}}saveLastRuns(){try{let e={};for(let[n,s]of this.jobs)s.lastRun&&(e[n]=s.lastRun.toISOString());let t=D(process.cwd(),".agentx/cron");X(t)||he(t,{recursive:!0}),Te(this.lastRunFile,JSON.stringify(e,null,2))}catch{}}loadLastRuns(){try{if(!X(this.lastRunFile))return null;let{readFileSync:e}=Ce("fs");return JSON.parse(e(this.lastRunFile,"utf-8"))}catch{return null}}logRun(e){try{let t=D(this.runsDir,e.jobId);X(t)||he(t,{recursive:!0});let n=`${e.startedAt.toISOString().replace(/[:.]/g,"-")}.json`;Te(D(t,n),JSON.stringify(e,null,2))}catch{}}list(){return Array.from(this.jobs.values())}health(){let e=Array.from(this.jobs.values()),t=this.detectMissedRuns();return{healthy:e.filter(n=>n.enabled&&n.consecutiveErrors===0).length,failing:e.filter(n=>n.enabled&&n.consecutiveErrors>0).length,disabled:e.filter(n=>!n.enabled).length,missed:t.length,jobs:e.map(n=>({id:n.id,status:n.enabled?n.retryPending?"retrying":n.consecutiveErrors>0?"failing":"healthy":"disabled",consecutiveErrors:n.consecutiveErrors,lastError:n.lastError}))}}};import{createServer as Xe}from"http";import{writeFileSync as Ye,existsSync as Ke,unlinkSync as Qe,mkdirSync as Ze}from"fs";import{resolve as Se,dirname as et}from"path";var K=class{name="discord";token;agentBinding;handler;client=null;log;constructor(e,t=console.error.bind(console,"[discord]")){this.token=e.token,this.agentBinding=e.agentBinding,this.log=t}onMessage(e){this.handler=e}async start(){let e;try{e=await import("discord.js")}catch{this.log("Discord requires discord.js. Install with:"),this.log(" npm install discord.js");return}let{Client:t,GatewayIntentBits:n}=e;this.client=new t({intents:[n.Guilds,n.GuildMessages,n.MessageContent,n.DirectMessages]}),this.client.on("ready",()=>{this.log(`Discord connected as ${this.client.user?.tag}`)}),this.client.on("messageCreate",async s=>{if(!this.handler||s.author.bot)return;let i=s.mentions.users.has(this.client.user?.id),r=!s.guild;if(!i&&!r)return;let o=s.content;if(this.client.user&&(o=o.replace(new RegExp(`<@!?${this.client.user.id}>`,"g"),"").trim()),!o)return;let a={id:s.id,channel:"discord",accountId:"default",sender:{id:s.author.id,name:s.author.displayName||s.author.username,username:s.author.username},group:s.guild?{id:s.channelId,name:s.channel?.name||s.channelId}:void 0,text:o,replyTo:s.reference?.messageId,timestamp:s.createdAt,raw:s};this.handler(a).catch(c=>{this.log(`Error handling message: ${c.message}`)})});try{await this.client.login(this.token)}catch(s){this.log(`Discord login failed: ${s.message}`)}}async stop(){this.client&&(this.client.destroy(),this.client=null)}async send(e){if(!this.client)return"";try{let t=await this.client.channels.fetch(e.chatId);return t?.isTextBased()?(await t.send({content:e.text,...e.replyTo?{reply:{messageReference:e.replyTo}}:{}})).id:""}catch(t){return this.log(`Send error: ${t.message}`),""}}async editMessage(e,t,n){if(!this.client)return!1;try{let s=await this.client.channels.fetch(e);return s?.isTextBased()?(await(await s.messages.fetch(t)).edit(n),!0):!1}catch{return!1}}async sendTyping(e){if(this.client)try{let t=await this.client.channels.fetch(e);t?.isTextBased()&&await t.sendTyping()}catch{}}async react(e,t,n="\u{1F440}"){if(this.client)try{let s=await this.client.channels.fetch(e);if(!s?.isTextBased())return;await(await s.messages.fetch(t)).react(n)}catch{}}};import{createServer as Ve}from"http";var Q=class{name="gitlab";config;handler;server;botUsername;botUsernames=new Set;sentNoteIds=new Set;usernameToAgent=new Map;agentToUsername=new Map;log;constructor(e,t=console.error.bind(console,"[gitlab]")){this.config=e,this.log=t}onMessage(e){this.handler=e}async start(){let e=[];this.config.token&&e.push({label:"global",token:this.config.token});for(let n of this.config.agentMappings||[])n.token&&e.push({label:n.agentId,token:n.token});let t=await Promise.allSettled(e.map(async({label:n,token:s})=>{let i=await fetch(`${this.config.host}/api/v4/user`,{headers:{"PRIVATE-TOKEN":s}});if(!i.ok)throw new Error(`${i.status}`);let r=await i.json();return{label:n,username:r.username}}));for(let n of t)if(n.status==="fulfilled"){let{label:s,username:i}=n.value;this.botUsernames.add(i),s==="global"?(this.botUsername=i,this.log(`Global bot user: ${i}`)):(this.usernameToAgent.set(i.toLowerCase(),s),this.agentToUsername.set(s,i.toLowerCase()),this.log(`Agent "${s}" -> GitLab user @${i}`))}for(let n of this.config.agentMappings||[]){for(let s of n.gitlabUsernames)this.botUsernames.add(s),this.usernameToAgent.has(s.toLowerCase())||this.usernameToAgent.set(s.toLowerCase(),n.agentId);!this.agentToUsername.has(n.agentId)&&n.gitlabUsernames.length>0&&this.agentToUsername.set(n.agentId,n.gitlabUsernames[0].toLowerCase())}this.log(`Bot users (${this.botUsernames.size}): ${[...this.botUsernames].join(", ")}`),this.log(`Username->Agent map: ${[...this.usernameToAgent.entries()].map(([n,s])=>`@${n}->${s}`).join(", ")}`),this.server=Ve(async(n,s)=>{n.method==="POST"?await this.handleWebhook(n,s):(s.writeHead(200,{"Content-Type":"text/plain"}),s.end("GitLab webhook endpoint. POST events here."))}),this.server.on("error",n=>{n.code==="EADDRINUSE"?(this.log(`GitLab webhook port ${this.config.webhookPort} in use, retrying in 5s...`),setTimeout(()=>{this.server?.close(),this.server?.listen(this.config.webhookPort)},5e3)):this.log(`GitLab webhook server error: ${n.message}`)}),this.server.listen(this.config.webhookPort,()=>{this.log(`GitLab webhook listening on :${this.config.webhookPort}`)})}async stop(){this.server&&this.server.close()}async send(e){let t=e.chatId.split(":");if(t.length<3)return this.log(`Invalid chatId for GitLab reply: ${e.chatId}`),"";let n=t.pop(),s=t.pop(),i=t.join(":"),r=encodeURIComponent(i),o;switch(s){case"issue":o=`${this.config.host}/api/v4/projects/${r}/issues/${n}/notes`;break;case"merge_request":o=`${this.config.host}/api/v4/projects/${r}/merge_requests/${n}/notes`;break;default:return this.log(`Unsupported noteable type: ${s}`),""}let a=this.getAgentToken(e.agentId),c=a||this.config.token;_.webhook("gitlab","send",`agentId="${e.agentId}" token=${a?"per-agent":"GLOBAL("+this.botUsername+")"}`);try{let l=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json","PRIVATE-TOKEN":c},body:JSON.stringify({body:`${e.text}
|
|
30
|
-
|
|
31
|
-
<!-- agentx:${e.agentId||"unknown"} -->`})});if(!l.ok){let d=await l.text();return this.log(`GitLab API error: ${l.status} ${d}`),""}let h=await l.json(),g=String(h.id||"");return g&&this.sentNoteIds.add(g),g}catch(l){return this.log(`GitLab send error: ${l.message}`),""}}async handleWebhook(e,t){if(this.config.webhookSecret&&e.headers["x-gitlab-token"]!==this.config.webhookSecret){t.writeHead(401,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Invalid token"}));return}let s=await this.readBody(e),i=s.object_kind||s.event_type||"unknown";switch(this.log(`Event: ${i} from ${s.project?.path_with_namespace||"unknown"}`),i){case"note":await this.handleNote(s,t);break;case"issue":await this.handleIssue(s,t);break;case"merge_request":await this.handleMR(s,t);break;case"pipeline":await this.handlePipeline(s,t);break;default:this.log(`Unhandled event: ${i}`),t.writeHead(200),t.end("ok")}}async handleNote(e,t){if(!this.handler){t.writeHead(200),t.end("ok");return}let n=e.object_attributes.note,s=e.project.path_with_namespace,i=e.user,r=String(e.object_attributes.id),o=n.match(/<!-- agentx:(\S+) -->/);if(o){let v=o[1],y=n.match(/@(\w[\w.-]*)/g)?.map(k=>k.slice(1).replace(/[.]+$/,""))||[];if(!y.some(k=>{let T=this.usernameToAgent.get(k.toLowerCase());return T&&T!==v})){this.log(`AgentX comment from ${v}, no cross-agent mention, skipping (note ${r})`),t.writeHead(200),t.end("ok");return}this.log(`Bot-to-bot handoff: ${v} \u2192 ${y.join(", ")} (note ${r})`)}if(this.sentNoteIds.has(r)){this.sentNoteIds.delete(r),t.writeHead(200),t.end("ok");return}let a=n.match(/@(\w[\w.-]*)/g)?.map(v=>v.slice(1).replace(/[.]+$/,""))||[];if(a.length===0){this.log(`No @mention in note ${r}, skipping`),t.writeHead(200),t.end("ok");return}if(this.isBotUser(i.username)&&!o){this.log(`Bot comment from ${i.username} without signature, skipping (note ${r})`),t.writeHead(200),t.end("ok");return}let c="",l="",h="";e.issue?(c="issue",l=String(e.issue.iid),h=e.issue.title):e.merge_request&&(c="merge_request",l=String(e.merge_request.iid),h=e.merge_request.title),_.webhook("gitlab","handleNote",`noteId=${r} mentions=[${a.join(",")}] user=${i.username}`);let g=this.resolveAgentFromMention(n);if(_.webhook("gitlab","resolve",`agentId=${g??"NONE"}`),!g){this.log(`[handleNote] @mentions in note ${r} are not agents (${a.join(", ")}), skipping`),t.writeHead(200),t.end("ok");return}let d=g,p=this.config.agentMappings?.find(v=>v.agentId===d),m=p?.token;_.webhook("gitlab","token",`agent="${d}" mapping=${p?"found":"MISSING"} hasToken=${!!m}`),this.reactToNote(s,c,l,e.object_attributes.id,m).catch(()=>{});let f=`${s}:${c}:${l}`,u=await this.getChannelMeta(f),w={id:String(e.object_attributes.id),channel:"gitlab",accountId:"default",sender:{id:f,name:i.name,username:i.username},text:`[GitLab ${c} #${l}: ${h}]
|
|
32
|
-
${i.name} commented:
|
|
33
|
-
${n.replace(/\n*<!-- agentx:\S+ -->/g,"")}`,timestamp:new Date,raw:e,resolvedAgent:d,channelMeta:u?{...u,issue:{type:c,iid:l,title:h}}:void 0};this.handler(w).catch(v=>{this.log(`Error handling note: ${v.message}`)}),t.writeHead(200),t.end("ok")}async handleIssue(e,t){if(!this.handler){t.writeHead(200),t.end("ok");return}if(this.isBotUser(e.user.username)){t.writeHead(200),t.end("ok");return}let n=e.object_attributes,s=e.project.path_with_namespace,i=this.resolveAgent(s),r={id:`issue-${n.iid}-${n.action}`,channel:"gitlab",accountId:"default",sender:{id:`${s}:issue:${n.iid}`,name:e.user.name,username:e.user.username},text:`[GitLab Issue #${n.iid} ${n.action}]: ${n.title}
|
|
34
|
-
${n.description?.slice(0,500)||""}
|
|
35
|
-
URL: ${n.url}`,timestamp:new Date,raw:e,resolvedAgent:i};this.handler(r).catch(o=>this.log(`Error handling issue: ${o.message}`)),t.writeHead(200),t.end("ok")}async handleMR(e,t){if(!this.handler){t.writeHead(200),t.end("ok");return}if(this.isBotUser(e.user.username)){t.writeHead(200),t.end("ok");return}let n=e.object_attributes,s=e.project.path_with_namespace,i=this.resolveAgent(s),r={id:`mr-${n.iid}-${n.action}`,channel:"gitlab",accountId:"default",sender:{id:`${s}:merge_request:${n.iid}`,name:e.user.name,username:e.user.username},text:`[GitLab MR !${n.iid} ${n.action}]: ${n.title}
|
|
36
|
-
Branch: ${n.source_branch} -> ${n.target_branch}
|
|
37
|
-
${n.description?.slice(0,500)||""}
|
|
38
|
-
URL: ${n.url}`,timestamp:new Date,raw:e,resolvedAgent:i};this.handler(r).catch(o=>this.log(`Error handling MR: ${o.message}`)),t.writeHead(200),t.end("ok")}async handlePipeline(e,t){if(!this.handler){t.writeHead(200),t.end("ok");return}if(e.object_attributes.status!=="failed"){t.writeHead(200),t.end("ok");return}let n=e.object_attributes,s=e.project.path_with_namespace,i=this.resolveAgent(s),r={id:`pipeline-${n.id}`,channel:"gitlab",accountId:"default",sender:{id:`${s}:pipeline:${n.id}`,name:e.user.name,username:e.user.username},text:`[GitLab Pipeline FAILED] Project: ${s}
|
|
39
|
-
Ref: ${n.ref}
|
|
40
|
-
Duration: ${n.duration}s
|
|
41
|
-
Please investigate the failure.`,timestamp:new Date,raw:e,resolvedAgent:i};this.handler(r).catch(o=>this.log(`Error handling pipeline: ${o.message}`)),t.writeHead(200),t.end("ok")}resolveAgentFromMention(e){let t=e.match(/@(\w[\w.-]*)/g)?.map(n=>n.slice(1).replace(/[.]+$/,"").toLowerCase())||[];if(t.length!==0)for(let n of t){let s=this.usernameToAgent.get(n);if(s)return this.log(`Mention @${n} -> agent "${s}" (resolved from token)`),s}}resolveAgent(e){for(let t of this.config.routes)if(t.project===e||t.project==="*")return t.agent}async getChannelMeta(e){let t=e.split(":"),n=t[0],s=t[1],i=t[2],r=[];for(let a of this.config.routes)(a.project===n||a.project==="*")&&r.push({id:a.agent,name:a.agent});for(let a of this.config.agentMappings||[])r.some(c=>c.id===a.agentId)||r.push({id:a.agentId,name:a.agentId});return{channel:"gitlab",agents:r,project:n,issue:s&&i?{type:s,iid:i,title:""}:void 0,facts:["This is a GitLab webhook event \u2014 respond as a GitLab comment","Do NOT use Telegram handles or delegate to other agents","Do NOT mention other agents by name \u2014 you are the only agent responding to this event","Stay in your role \u2014 do not act as or speak for other agents"]}}isBotUser(e){return this.botUsernames.has(e)}getAgentToken(e){return!e||!this.config.agentMappings?.length?void 0:this.config.agentMappings.find(n=>n.agentId===e)?.token}getTokenForMentionedAgent(e){if(!this.config.agentMappings?.length)return;let t=e.match(/@(\w[\w.-]*)/g)?.map(n=>n.slice(1).replace(/[.]+$/,"").toLowerCase())||[];for(let n of this.config.agentMappings)if(n.token&&n.gitlabUsernames.some(s=>t.includes(s.toLowerCase())))return n.token}async reactToNote(e,t,n,s,i){if(!i){this.log(`No agent token for reaction on note ${s} \u2014 skipping (would react as wrong user)`);return}let r=encodeURIComponent(e),o;switch(t){case"issue":o=`${this.config.host}/api/v4/projects/${r}/issues/${n}/notes/${s}/award_emoji`;break;case"merge_request":o=`${this.config.host}/api/v4/projects/${r}/merge_requests/${n}/notes/${s}/award_emoji`;break;default:return}try{let a=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json","PRIVATE-TOKEN":i},body:JSON.stringify({name:"eyes"})});a.ok||this.log(`Reaction failed on note ${s}: ${a.status}`)}catch(a){this.log(`Failed to react to note ${s}: ${a.message}`)}}async readBody(e){return new Promise(t=>{let n="";e.on("data",s=>n+=s.toString()),e.on("end",()=>{try{t(n?JSON.parse(n):{})}catch{t({raw:n})}}),e.on("error",()=>t({}))})}};var O=class{module;minLevel;constructor(e,t="info"){this.module=e,this.minLevel=t}child(e){return new O(`${this.module}:${e}`,this.minLevel)}debug(e,t){this.emit("debug",e,t)}info(e,t){this.emit("info",e,t)}warn(e,t){this.emit("warn",e,t)}error(e,t){this.emit("error",e,t)}asConsoleLog(){return(...e)=>{let n=e.map(s=>typeof s=="string"?s:JSON.stringify(s)).join(" ").replace(/^\[agentx\]\s*/,"");n&&this.info(n)}}emit(e,t,n){if(O.levelOrder[e]<O.levelOrder[this.minLevel])return;let s={time:new Date().toISOString(),level:e,module:this.module,msg:t,...n},i=e==="error"?"ERROR":e==="warn"?"WARN":"",r=`[${this.module}]`,o=i?`${r} ${i}: ${t}`:`${r} ${t}`;console.error(o)}},L=O;B(L,"levelOrder",{debug:0,info:1,warn:2,error:3});var Z=class{registry;config;log;constructor(e,t={},n=console.error.bind(console,"[webhook]")){this.registry=e,this.config=t,this.log=n}async handle(e,t,n){let s=n.replace(/^\/webhook\/?/,"").split("/").filter(Boolean),i=s[0],r=s[1];if(!i){this.sendJson(t,400,{error:"Missing agent ID. Use /webhook/:agentId"});return}let o=await this.readBody(e),a=r||this.detectSource(e.headers),c=this.buildSummary(a,o,e.headers);this.log(`Webhook [${a}] -> ${i}: ${c.slice(0,100)}`);try{let l=await this.registry.execute({message:c,agentId:i,context:{channel:`webhook:${a}`,sender:`webhook:${a}`}});this.sendJson(t,l.error?500:200,{ok:!l.error,agent:i,source:a,response:l.content?.slice(0,500),error:l.error,duration:l.duration})}catch(l){this.sendJson(t,500,{error:l.message})}}detectSource(e){return e["x-gitlab-event"]||e["x-gitlab-token"]?"gitlab":e["x-github-event"]?"github":e["stripe-signature"]?"stripe":e["sentry-hook-resource"]?"sentry":e["x-vercel-signature"]?"vercel":e["x-hub-signature-256"]?"github":"unknown"}buildSummary(e,t,n){let s=[`[Webhook from ${e}]`];switch(e){case"gitlab":{let r=n["x-gitlab-event"]||t.object_kind||"event",o=t.project?.path_with_namespace||"",a=t.user?.name||t.user_username||"";if(s.push(`Event: ${r}`),o&&s.push(`Project: ${o}`),a&&s.push(`User: ${a}`),t.ref&&s.push(`Ref: ${t.ref}`),t.commits&&Array.isArray(t.commits)){s.push(`Commits: ${t.commits.length}`);for(let l of t.commits.slice(0,3))s.push(` - ${l.message?.split(`
|
|
42
|
-
`)[0]||"no message"} (${l.author?.name||""})`)}let c=t.object_attributes;if(c?.title&&(s.push(`Title: ${c.title}`),s.push(`State: ${c.state||""}`),s.push(`Action: ${c.action||""}`),c.source_branch&&s.push(`Branch: ${c.source_branch} -> ${c.target_branch}`),c.url&&s.push(`URL: ${c.url}`)),c?.iid&&!c?.source_branch&&(s.push(`Issue #${c.iid}: ${c.title||""}`),c.description&&s.push(`Description: ${c.description.slice(0,200)}`)),t.object_kind==="pipeline"){let l=t.object_attributes;s.push(`Pipeline: ${l?.status||""} (${l?.ref||""})`),s.push(`Duration: ${l?.duration||0}s`)}break}case"github":{let r=n["x-github-event"]||"event",o=t.repository?.full_name||"",a=t.sender?.login||"";if(s.push(`Event: ${r}`),o&&s.push(`Repository: ${o}`),a&&s.push(`Sender: ${a}`),t.ref&&s.push(`Ref: ${t.ref}`),t.commits&&Array.isArray(t.commits))for(let h of t.commits.slice(0,3))s.push(` - ${h.message?.split(`
|
|
43
|
-
`)[0]||""} (${h.author?.name||""})`);let c=t.pull_request;c&&(s.push(`PR #${c.number}: ${c.title}`),s.push(`Action: ${t.action}`),s.push(`Branch: ${c.head?.ref} -> ${c.base?.ref}`));let l=t.issue;l&&(s.push(`Issue #${l.number}: ${l.title}`),s.push(`Action: ${t.action}`));break}case"stripe":{let r=t.type||"event",o=t.data?.object||{};s.push(`Event: ${r}`),o.amount&&s.push(`Amount: ${(o.amount/100).toFixed(2)} ${o.currency?.toUpperCase()||""}`),o.customer_email&&s.push(`Customer: ${o.customer_email}`),o.description&&s.push(`Description: ${o.description}`),o.status&&s.push(`Status: ${o.status}`);break}case"sentry":{let r=n["sentry-hook-resource"]||"event";s.push(`Resource: ${r}`);let o=t.data||t;o.error?.title&&s.push(`Error: ${o.error.title}`),o.error?.culprit&&s.push(`Culprit: ${o.error.culprit}`),o.error?.metadata?.value&&s.push(`Message: ${o.error.metadata.value}`),t.url&&s.push(`URL: ${t.url}`);break}default:s.push(`Headers: ${JSON.stringify(Object.keys(n).filter(r=>r.startsWith("x-")).slice(0,5))}`),s.push(`Payload keys: ${Object.keys(t).slice(0,10).join(", ")}`);let i=JSON.stringify(t).slice(0,500);s.push(`Body: ${i}`)}return s.join(`
|
|
44
|
-
`)}async readBody(e){return new Promise(t=>{let n="";e.on("data",s=>n+=s.toString()),e.on("end",()=>{try{t(n?JSON.parse(n):{})}catch{t({raw:n})}}),e.on("error",()=>t({}))})}sendJson(e,t,n){e.writeHead(t,{"Content-Type":"application/json"}),e.end(JSON.stringify(n,null,2))}};var ee=class{cache=new Map;config;meshPeers=[];constructor(e){this.config=e}build(e){e&&(this.meshPeers=e),this.cache.clear();for(let t of Object.keys(this.config.agents))this.cache.set(t,this.renderForAgent(t))}getForAgent(e){return this.cache.get(e)}refresh(e){this.build(e)}renderForAgent(e){let t=["[Landscape]"],n=this.config.agents[e];t.push(`Node: ${this.config.node.name} (${this.config.node.id})`);let s=this.getPrimaryHandle(e),i=this.extractCapability(n),r=s?` (${s})`:"";t.push(`You: ${n.name}${r} \u2014 ${i}`),t.push("");let o=Object.entries(this.config.agents).filter(([g])=>g!==e);t.push(`Available agents on this node (${o.length} \u2014 not all may be in the current group):`);for(let[g,d]of o){let p=this.getPrimaryHandle(g),m=this.extractCapability(d),f=p?` (${p})`:"";t.push(`\u2022 ${d.name}${f} \u2014 ${m}`)}let a=this.meshPeers.filter(g=>g.healthy&&g.skills.length>0);if(a.length){t.push("Remote:");for(let g of a)for(let d of g.skills)t.push(`\u2022 ${d.name} [${g.peer}] \u2014 ${d.description.slice(0,60)}`)}t.push(""),t.push(this.renderChannels()),t.push(""),t.push("[Rules]");let c=[s,e].filter(Boolean).join(" / ");t.push(`- Only respond when YOU (${c}) are mentioned or this is a DM to you`),t.push("- If another agent was mentioned and you were NOT, stay silent \u2014 they will handle it"),t.push("- The agent list above shows all agents on the node \u2014 do NOT assume they are all in the current chat group"),t.push("- When asked about group members, only mention agents you have seen in the conversation history"),t.push("- To delegate on Telegram: mention the agent's handle in your response"),t.push("- On GitLab: reply directly, no Telegram handles. To send to other channels, use the /send API below"),t.push("- On WhatsApp: mention another agent's handle to delegate (shared number \u2014 name prefixed automatically). To send to other channels, use /send API");let[,l]=this.config.node.bind.split(":"),h=l||"19900";return t.push(""),t.push("[Cross-Channel Messaging]"),t.push("You can send messages to ANY channel proactively (not just reply):"),t.push(` curl -X POST http://localhost:${h}/send -H "Content-Type: application/json" -d '{"channel":"<channel>","chatId":"<id>","text":"<message>"}'`),t.push("Channels: telegram, whatsapp, gitlab, discord"),t.push("ChatId formats:"),t.push(' telegram: numeric (e.g. "-1001234567890" for group, "123456" for DM)'),t.push(' gitlab: "group/project:issue:123" or "group/project:merge_request:45"'),t.push(' whatsapp: JID (e.g. "+21612345678@s.whatsapp.net")'),t.push("Use this when asked to notify someone on a different channel, post to an issue, or broadcast updates."),t.join(`
|
|
45
|
-
`)}renderChannels(){let e=[],t=this.config.channels;if(t.telegram.enabled){let n=t.telegram.policy;e.push(`telegram(groups:${n.group}, DMs:${n.dm}, delegation:yes)`)}return t.gitlab?.enabled&&e.push("gitlab(webhooks, no delegation)"),t.whatsapp.enabled&&e.push("whatsapp(DMs only, no delegation)"),t.discord?.enabled&&e.push("discord(enabled)"),`Channels: ${e.join(", ")}`}getPrimaryHandle(e){let t=this.config.agents[e];if(t)return t.mentions.find(n=>n.startsWith("@"))}extractCapability(e){if(!e.systemPrompt)return e.name;let t=e.systemPrompt.split(/[.\n]/)[0]?.trim()||e.name;return t.length>80?t.slice(0,77)+"...":t}};var te=class{registry;states=new Map;log;constructor(e,t=console.error.bind(console,"[heartbeat]")){this.registry=e,this.log=t}register(e,t){if(!t.enabled)return;this.stop(e);let n={agentId:e,config:t,runCount:0,errors:0},s=(t.intervalMinutes||30)*60*1e3;n.timer=setInterval(()=>{this.runHeartbeat(n).catch(i=>{this.log(`[${e}] heartbeat error: ${i.message}`),n.errors++})},s),this.states.set(e,n),this.log(`Registered heartbeat for "${e}" every ${t.intervalMinutes}min`)}stop(e){let t=this.states.get(e);t?.timer&&(clearInterval(t.timer),t.timer=void 0),this.states.delete(e)}stopAll(){for(let[e]of this.states)this.stop(e)}async runHeartbeat(e){let{agentId:t,config:n}=e;this.log(`[${t}] heartbeat running...`),e.lastRun=new Date,e.runCount++;let s=n.channel||"heartbeat";try{let i=await this.registry.execute({message:n.prompt,agentId:t,context:{channel:s,sender:"system:heartbeat",chatId:`heartbeat:${t}`}});i.error?(this.log(`[${t}] heartbeat error: ${i.error}`),e.errors++):this.log(`[${t}] heartbeat completed (${i.duration}ms, ${i.content.length} chars)`)}catch(i){this.log(`[${t}] heartbeat failed: ${i.message}`),e.errors++}}async trigger(e){let t=this.states.get(e);if(!t){this.log(`[${e}] no heartbeat registered`);return}await this.runHeartbeat(t)}list(){return Array.from(this.states.values()).map(e=>({agentId:e.agentId,intervalMinutes:e.config.intervalMinutes,lastRun:e.lastRun,runCount:e.runCount,errors:e.errors}))}};var Me=class{config;registry;router;cron;mesh;hooks;landscape;heartbeat;httpServer;webhooks;log;sseClients=new Set;constructor(e){let n=new L("agentx").asConsoleLog();this.log=(...i)=>{n(...i);let r=i.map(o=>typeof o=="string"?o:JSON.stringify(o)).join(" ");this.broadcastSSE("log",r)},this.log("Loading configuration..."),this.config=be(e);let s=we(this.config);for(let i of s)this.log(` \u26A0 ${i}`);this.hooks=new pe,me(process.cwd(),this.hooks),this.landscape=new ee(this.config),this.registry=new ye(this.config,this.log),fe(this.registry),this.registry.setLandscape(this.landscape),this.heartbeat=new te(this.registry,this.log);for(let[i,r]of Object.entries(this.config.agents))r.heartbeat?.enabled&&this.heartbeat.register(i,r.heartbeat);this.router=new E(this.registry,this.config,this.hooks,this.log),this.webhooks=new Z(this.registry,{},this.log),this.cron=new Y(this.config,this.registry,this.hooks,this.log),this.cron.setNotifyCallback(async(i,r,o,a)=>{let c=`Cron "${i}" failed (${a}x)
|
|
46
|
-
Agent: ${r}
|
|
47
|
-
Error: ${o.slice(0,300)}`;this.log(`[CRON ALERT] ${c}`),this.broadcastSSE("cron-failure",JSON.stringify({jobId:i,agent:r,error:o,consecutiveErrors:a}));let l=this.config.crons[i];if(l?.notify)try{await this.router.sendOutbound({channel:l.notify.channel,chatId:l.notify.chatId,text:`\u{1F534} **Cron "${i}" failed** (${a}x)
|
|
48
|
-
${o.slice(0,300)}`,agentId:r,accountId:l.notify.accountId})}catch(h){this.log(`[CRON ALERT] notify send failed: ${h.message}`)}}),this.config.mesh.enabled&&(this.mesh=new F(this.config,this.log),this.router.setMesh(this.mesh))}async start(){this.log(""),this.log(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"),this.log(" \u2502 agentx daemon \u2502"),this.log(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"),this.log(""),this.log(` Node: ${this.config.node.name} (${this.config.node.id})`),this.log(` Bind: ${this.config.node.bind}`),this.log(""),await this.startChannels(),await this.cron.start(),this.mesh&&await this.mesh.start();let e=this.mesh?.directory().map(r=>({peer:r.peer,healthy:r.healthy,skills:r.skills}))||[];this.landscape.build(e),this.log(` Landscape: built for ${Object.keys(this.config.agents).length} agents`),this.scheduleMidnightHook(),await this.startHttpApi(),this.log(""),this.log(" Agents:");for(let r of this.registry.list())this.log(` ${r.id} (${r.tier}) \u2192 ${r.workspace}`);let t=this.cron.list();if(t.length){this.log(""),this.log(" Cron Jobs:");for(let r of t){let o=r.enabled?"enabled":"disabled";this.log(` ${r.id} [${o}] \u2192 ${r.agent} (${r.schedule})`)}}if(this.mesh){this.log(""),this.log(" Mesh Peers:");for(let r of this.mesh.directory()){let o=r.healthy?"\u2713":"\u2717";this.log(` ${o} ${r.peer} (${r.peerUrl})`)}}this.log(""),this.log(" Ready."),this.log("");let n=Se(process.cwd(),".agentx/daemon.pid");Ze(et(n),{recursive:!0}),Ye(n,String(process.pid)),this.log(` PID: ${process.pid} (${n})`),process.on("uncaughtException",r=>{this.log(`UNCAUGHT EXCEPTION: ${r.message}`),this.log(r.stack||"")}),process.on("unhandledRejection",r=>{this.log(`UNHANDLED REJECTION: ${r}`)});let s=!1,i=async r=>{s||(s=!0,this.log(`
|
|
49
|
-
Received ${r}, shutting down gracefully...`),await this.stop())};process.on("SIGINT",()=>i("SIGINT")),process.on("SIGTERM",()=>i("SIGTERM"))}async stop(){let e=Date.now();try{this.log(" Stopping channels..."),await Promise.race([this.router.stopAll(),new Promise(t=>setTimeout(t,5e3))])}catch(t){this.log(` Channel stop error: ${t.message}`)}try{this.log(" Stopping crons (saving last run times)..."),await this.cron.stop()}catch{}try{this.log(" Stopping heartbeats..."),this.heartbeat.stopAll()}catch{}try{this.mesh&&(this.log(" Stopping mesh..."),await this.mesh.stop())}catch{}this.midnightTimer&&clearTimeout(this.midnightTimer),this.httpServer&&this.httpServer.close();try{let t=Se(process.cwd(),".agentx/daemon.pid");Ke(t)&&Qe(t)}catch{}this.log(` Shutdown complete (${Date.now()-e}ms)`),process.exit(0)}midnightTimer;scheduleMidnightHook(){let e=()=>{let t=new Date,n=t.toLocaleString("en-US",{timeZone:"Africa/Tunis"}),s=new Date(n),i=new Date(s);i.setDate(i.getDate()+1),i.setHours(0,0,5,0);let r=s.getTime()-t.getTime(),o=i.getTime()-r-t.getTime();this.log(` Cost tracking: next run in ${Math.round(o/6e4)}min`),this.midnightTimer=setTimeout(async()=>{await this.runDailyCostReport(),e()},Math.max(o,6e4))};e()}async runDailyCostReport(){let e=new Date(Date.now()-864e5).toISOString().slice(0,10),t=this.registry.getTokenTracker(),n=t.generateDailyReport(e);if(!n||n.totalTasks===0){this.log(` Cost tracking: no usage for ${e}, skipping`);return}t.appendToTokenCosts(n),this.log(` Cost tracking: ${e} \u2014 ${n.totalTasks} tasks, $${n.totalCost.toFixed(4)} (top: ${n.topAgent} $${n.topCost.toFixed(4)})`)}async startChannels(){if(this.config.channels.telegram.enabled){let e=this.config.channels.telegram.accounts;if(Object.keys(e).length>0){let t=new z(e,this.log);this.router.addChannel(t),this.log(" Telegram: enabled")}}if(this.config.channels.whatsapp.enabled){let e=new V({sessionDir:this.config.channels.whatsapp.sessionDir,defaultAgent:this.config.channels.whatsapp.defaultAgent,allowFrom:this.config.channels.whatsapp.allowFrom,routes:this.config.channels.whatsapp.routes},this.log);this.router.addChannel(e),this.log(` WhatsApp: enabled (${this.config.channels.whatsapp.routes.length} routes)`)}if(this.config.channels.discord?.enabled&&this.config.channels.discord.token){let e=new K({token:this.config.channels.discord.token,agentBinding:this.config.channels.discord.agentBinding},this.log);this.router.addChannel(e),this.log(" Discord: enabled")}if(this.config.channels.gitlab?.enabled&&this.config.channels.gitlab.token){let e=new Q({webhookPort:this.config.channels.gitlab.webhookPort,webhookSecret:this.config.channels.gitlab.webhookSecret,host:this.config.channels.gitlab.host,token:this.config.channels.gitlab.token,routes:this.config.channels.gitlab.routes,agentMappings:this.config.channels.gitlab.agentMappings},this.log);this.router.addChannel(e),this.log(` GitLab: enabled (${this.config.channels.gitlab.routes.length} project routes, webhook :${this.config.channels.gitlab.webhookPort})`)}await this.router.startAll()}async startHttpApi(){let[e,t]=this.config.node.bind.split(":"),n=parseInt(t||"18800",10);this.httpServer=Xe(async(s,i)=>{if(i.setHeader("Access-Control-Allow-Origin","*"),i.setHeader("Access-Control-Allow-Methods","GET, POST, OPTIONS"),i.setHeader("Access-Control-Allow-Headers","Content-Type, Authorization"),s.method==="OPTIONS"){i.writeHead(204),i.end();return}await this.handleHttp(s,i)}),this.httpServer.on("error",s=>{s.code==="EADDRINUSE"?(this.log(` ERROR: Port ${n} is already in use. Retrying in 5s...`),setTimeout(()=>{this.httpServer?.close(),this.httpServer?.listen(n,e||"0.0.0.0")},5e3)):this.log(` HTTP error: ${s.message}`)}),this.httpServer.listen(n,e||"0.0.0.0",()=>{this.log(` HTTP API: http://${e||"0.0.0.0"}:${n}`)})}broadcastSSE(e,t){if(this.sseClients.size===0)return;let n=`event: ${e}
|
|
50
|
-
data: ${JSON.stringify({time:new Date().toISOString(),message:t})}
|
|
51
|
-
|
|
52
|
-
`;for(let s of this.sseClients)try{s.write(n)}catch{this.sseClients.delete(s)}}handleSSE(e,t){t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","Access-Control-Allow-Origin":"*"});let n=this.registry.list(),s=n.filter(i=>i.active>0);t.write(`event: status
|
|
53
|
-
data: ${JSON.stringify({node:this.config.node.name,agents:n.length,active:s.map(i=>({id:i.id,name:i.name,tasks:i.active})),mesh:this.mesh?.directory().map(i=>({peer:i.peer,healthy:i.healthy}))||[]})}
|
|
54
|
-
|
|
55
|
-
`),this.sseClients.add(t),e.on("close",()=>this.sseClients.delete(t))}async handleHttp(e,t){let n=new URL(e.url||"/",`http://${e.headers.host||"localhost"}`),s=n.pathname;try{if(e.method==="GET"&&s==="/events"){this.handleSSE(e,t);return}if(e.method==="POST"&&s.startsWith("/webhook/")){await this.webhooks.handle(e,t,s);return}if(e.method==="POST"&&(s==="/v1/chat/completions"||s.match(/^\/llm\/[^/]+\/v1\/chat\/completions$/))){await this.handleOpenAICompat(e,t,s);return}switch(`${e.method} ${s}`){case"GET /health":this.json(t,200,{status:"ok",node:this.config.node,uptime:process.uptime(),agents:this.registry.list(),crons:this.cron.list().map(i=>({id:i.id,enabled:i.enabled,nextRun:i.nextRun})),mesh:this.mesh?.directory()||[],usage:this.registry.getTodayUsage()});break;case"GET /usage":this.json(t,200,this.registry.getUsage(7));break;case"GET /agents":this.json(t,200,this.registry.list());break;case"GET /crons":this.json(t,200,this.cron.list());break;case"GET /crons/health":this.json(t,200,this.cron.health());break;case"GET /mesh":this.json(t,200,this.mesh?.directory()||[]);break;case"GET /debug":{let{getDebugState:i,getDebugLogs:r}=await import("./debug-N3B5NVJU.js");this.json(t,200,{...i(),recentLogs:r(50)});break}case"POST /debug/on":{let{setDebug:i}=await import("./debug-N3B5NVJU.js"),r=n.searchParams.get("categories")?.split(",")||["all"];i(!0,r),this.log(`Debug enabled: ${r.join(", ")}`),this.json(t,200,{enabled:!0,categories:r});break}case"POST /debug/off":{let{setDebug:i}=await import("./debug-N3B5NVJU.js");i(!1),this.log("Debug disabled"),this.json(t,200,{enabled:!1});break}case"POST /send":{let i=await ne(e);if(!i.channel||!i.chatId||!i.text){this.json(t,400,{error:"Required: channel, chatId, text"});return}try{let r=await this.router.sendOutbound({channel:i.channel,chatId:i.chatId,text:i.text,replyTo:i.replyTo,parseMode:i.parseMode,agentId:i.agentId,accountId:i.accountId});this.json(t,200,{ok:!0,messageId:r||null})}catch(r){this.json(t,400,{error:r.message})}break}case"GET /channels":this.json(t,200,this.router.getChannelNames());break;case"POST /task":{let i=await ne(e);if(!i.agent||!i.message){this.json(t,400,{error:"Missing: agent, message"});return}let r=await this.registry.execute({agentId:i.agent,message:i.message,context:i.context});this.json(t,r.error?500:200,r);break}case"POST /mesh/task":{let i=await ne(e);if(!i.peer||!i.message){this.json(t,400,{error:"Missing: peer, message"});return}if(!this.mesh){this.json(t,400,{error:"Mesh not enabled"});return}let r=await this.mesh.sendTask(i.peer,i.message);this.json(t,200,{response:r});break}case"GET /.well-known/agent-card.json":this.json(t,200,{name:this.config.node.name,description:`AgentX daemon node "${this.config.node.name}"`,url:`http://${this.config.node.bind}`,version:"1.0.0",capabilities:{streaming:!1,pushNotifications:!1,stateTransitionHistory:!1},skills:this.registry.list().map(i=>({id:i.id,name:i.name,description:`Agent "${i.name}" (${i.tier})`,tags:[i.tier]})),defaultInputModes:["text"],defaultOutputModes:["text"]});break;case"GET /wiki/agents":{let i=this.registry.getWikiHub();this.json(t,200,{nodeId:this.config.node.id,agents:i.summary()});break}case"GET /wiki/entries":{let i=this.registry.getWikiHub(),r=n.searchParams.get("agent")||void 0,o=n.searchParams.get("after")||void 0,a=r?i.getAgentEntries(r):i.getSharedStore().listEntries({after:o});this.json(t,200,{nodeId:this.config.node.id,count:a.length,entries:a.map(c=>({id:c.id,date:c.date,agentId:c.agentId,source:c.source,sourceContext:c.sourceContext,content:c.content}))});break}case"GET /wiki/articles":{let i=this.registry.getWikiHub(),r=n.searchParams.get("agent");if(!r){this.json(t,400,{error:"?agent= required"});break}let a=i.getAgentWiki(r).rebuildIndex();this.json(t,200,{nodeId:this.config.node.id,agentId:r,articles:a.articles});break}case"GET /wiki/article":{let i=this.registry.getWikiHub(),r=n.searchParams.get("agent"),o=n.searchParams.get("path");if(!r||!o){this.json(t,400,{error:"?agent= and ?path= required"});break}let c=i.getAgentWiki(r).readArticle(o);if(!c){this.json(t,404,{error:"Article not found"});break}this.json(t,200,{nodeId:this.config.node.id,agentId:r,path:c.path,title:c.meta.title,tags:c.meta.tags,owner:c.meta.owner,created:c.meta.created,lastUpdated:c.meta.lastUpdated,sources:c.meta.sources,content:c.content});break}default:this.json(t,404,{error:"Not found",endpoints:["GET /health","GET /agents","GET /crons","GET /mesh","GET /wiki/agents","GET /wiki/entries[?agent=X&after=YYYY-MM-DD]","GET /wiki/articles?agent=X","POST /task { agent, message, context? }","POST /mesh/task { peer, message }","POST /webhook/:agentId[/:source] \u2014 webhook callback","GET /.well-known/agent-card.json"]})}}catch(i){this.json(t,500,{error:i.message})}}async handleOpenAICompat(e,t,n){let s=await ne(e),i=n.match(/^\/llm\/([^/]+)\//),r=Object.keys(this.config.agents)[0]||"default",o=i?.[1]||s.model||r,a=s.messages||[],c=[...a].reverse().find(d=>d.role==="user");if(!c?.content){this.json(t,400,{error:{message:"No user message found",type:"invalid_request_error"}});return}let l=a.slice(0,-1).map(d=>`${d.role==="user"?"User":"Assistant"}: ${d.content.slice(0,200)}`),h=l.length>0?`[Conversation]
|
|
56
|
-
${l.slice(-10).join(`
|
|
57
|
-
`)}
|
|
58
|
-
|
|
59
|
-
`:"";if(s.stream===!0){t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"});let d=`chatcmpl-${Date.now().toString(36)}`,p=await this.registry.execute({agentId:o,message:h+c.content,context:{channel:"api",sender:"openai-compat"}}),m=p.error||p.content||"",f={id:d,object:"chat.completion.chunk",created:Math.floor(Date.now()/1e3),model:o,choices:[{index:0,delta:{role:"assistant",content:m},finish_reason:"stop"}]};t.write(`data: ${JSON.stringify(f)}
|
|
60
|
-
|
|
61
|
-
`),t.write(`data: [DONE]
|
|
62
|
-
|
|
63
|
-
`),t.end()}else{let d=await this.registry.execute({agentId:o,message:h+c.content,context:{channel:"api",sender:"openai-compat"}}),p=d.error||d.content||"",m=Math.ceil(p.length/4);this.json(t,200,{id:`chatcmpl-${Date.now().toString(36)}`,object:"chat.completion",created:Math.floor(Date.now()/1e3),model:o,choices:[{index:0,message:{role:"assistant",content:p},finish_reason:"stop"}],usage:{prompt_tokens:Math.ceil(c.content.length/4),completion_tokens:m,total_tokens:Math.ceil(c.content.length/4)+m}})}}json(e,t,n){e.writeHead(t,{"Content-Type":"application/json"}),e.end(JSON.stringify(n,null,2))}};async function ne(b){return new Promise((e,t)=>{let n="";b.on("data",s=>n+=s.toString()),b.on("end",()=>{try{e(n?JSON.parse(n):{})}catch{e({})}}),b.on("error",t)})}import tt from"path";import nt from"fs-extra";function nn(){let b=tt.join("package.json");return nt.readJSONSync(b)}export{U as a,F as b,E as c,z as d,V as e,Y as f,Me as g,nn as h};
|
|
64
|
-
//# sourceMappingURL=chunk-BAIQJHQF.js.map
|