agentix-cli 0.15.0 → 0.17.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.
Files changed (148) hide show
  1. package/README.md +12 -6
  2. package/dist/agent-4AOMVXRJ.js +2 -0
  3. package/dist/board-dashboard-CXIVJKDP.js +7282 -0
  4. package/dist/board-dashboard-CXIVJKDP.js.map +1 -0
  5. package/dist/chunk-34NG7UDX.js +11 -0
  6. package/dist/chunk-34NG7UDX.js.map +1 -0
  7. package/dist/chunk-4HP6UC2X.js +13 -0
  8. package/dist/chunk-4HP6UC2X.js.map +1 -0
  9. package/dist/chunk-5F3J55LG.js +2 -0
  10. package/dist/chunk-5F3J55LG.js.map +1 -0
  11. package/dist/chunk-5WNGHDFC.js +164 -0
  12. package/dist/chunk-5WNGHDFC.js.map +1 -0
  13. package/dist/{chunk-7MBTMCKY.js → chunk-63UYRJIC.js} +2 -2
  14. package/dist/chunk-64P4QHSZ.js +2 -0
  15. package/dist/chunk-64P4QHSZ.js.map +1 -0
  16. package/dist/chunk-6PPIFDOQ.js +1742 -0
  17. package/dist/chunk-6PPIFDOQ.js.map +1 -0
  18. package/dist/chunk-ABCHHNG6.js +625 -0
  19. package/dist/chunk-ABCHHNG6.js.map +1 -0
  20. package/dist/chunk-AFRHKA7B.js +2 -0
  21. package/dist/chunk-AFRHKA7B.js.map +1 -0
  22. package/dist/chunk-BFZUG66X.js +45 -0
  23. package/dist/chunk-BFZUG66X.js.map +1 -0
  24. package/dist/chunk-CDNWCYF7.js +2 -0
  25. package/dist/chunk-CDNWCYF7.js.map +1 -0
  26. package/dist/{chunk-6MKAXEE5.js → chunk-EKTPCSN4.js} +2 -2
  27. package/dist/{chunk-6MKAXEE5.js.map → chunk-EKTPCSN4.js.map} +1 -1
  28. package/dist/chunk-EW27SCCC.js +2 -0
  29. package/dist/chunk-EW27SCCC.js.map +1 -0
  30. package/dist/chunk-HCFSHGK4.js +46 -0
  31. package/dist/chunk-HCFSHGK4.js.map +1 -0
  32. package/dist/chunk-HHJGKD5B.js +71 -0
  33. package/dist/chunk-HHJGKD5B.js.map +1 -0
  34. package/dist/chunk-LEIYC3BF.js +104 -0
  35. package/dist/chunk-LEIYC3BF.js.map +1 -0
  36. package/dist/{chunk-HLRVDCNH.js → chunk-MBEQASP3.js} +2 -2
  37. package/dist/chunk-N67JZAXU.js +2 -0
  38. package/dist/chunk-N67JZAXU.js.map +1 -0
  39. package/dist/{chunk-RGUYBH77.js → chunk-SRHLXZRF.js} +2 -2
  40. package/dist/chunk-TT2Q7YMZ.js +118 -0
  41. package/dist/chunk-TT2Q7YMZ.js.map +1 -0
  42. package/dist/{chunk-HI24KAEY.js → chunk-WJXU5F4Z.js} +3 -4
  43. package/dist/{chunk-HI24KAEY.js.map → chunk-WJXU5F4Z.js.map} +1 -1
  44. package/dist/chunk-WZEYII2S.js +6 -0
  45. package/dist/chunk-WZEYII2S.js.map +1 -0
  46. package/dist/chunk-XDJSYQST.js +6 -0
  47. package/dist/chunk-XDJSYQST.js.map +1 -0
  48. package/dist/chunk-YPOELN6Z.js +798 -0
  49. package/dist/chunk-YPOELN6Z.js.map +1 -0
  50. package/dist/{chunk-QZP65I2H.js → chunk-ZE2YFX4K.js} +2 -2
  51. package/dist/cli.js +1 -302
  52. package/dist/cli.js.map +1 -1
  53. package/dist/{compaction-JBZZ7I7Y.js → compaction-X46EQEPS.js} +6 -6
  54. package/dist/compaction-X46EQEPS.js.map +1 -0
  55. package/dist/{config-DCJYECL4.js → config-KCYHMKCT.js} +2 -2
  56. package/dist/config-mutator-QP4GLBOE.js +2 -0
  57. package/dist/context-planner-G2J7FNNU.js +17 -0
  58. package/dist/context-planner-G2J7FNNU.js.map +1 -0
  59. package/dist/editor-chat-R54E65W5.js +137 -0
  60. package/dist/editor-chat-R54E65W5.js.map +1 -0
  61. package/dist/heal-JDXUPRET.js +2 -0
  62. package/dist/inbox-L5ZZ623Y.js +315 -0
  63. package/dist/inbox-L5ZZ623Y.js.map +1 -0
  64. package/dist/index.d.ts +1996 -18
  65. package/dist/index.js +22 -49
  66. package/dist/index.js.map +1 -1
  67. package/dist/ingest-whatsapp-NS4OWDOU.js +6 -0
  68. package/dist/ingest-whatsapp-NS4OWDOU.js.map +1 -0
  69. package/dist/memory-extract-WH4FZ456.js +2 -0
  70. package/dist/processes-XF5CV4LK.js +256 -0
  71. package/dist/processes-XF5CV4LK.js.map +1 -0
  72. package/dist/program-FFWEILAS.js +2 -0
  73. package/dist/providers-AKQ4HTB4.js +2 -0
  74. package/dist/query-PKGYE5HR.js +2 -0
  75. package/dist/registry-IFMDJWQ7.js +2 -0
  76. package/dist/scheduler-63D5KFO7.js +2 -0
  77. package/dist/scheduler-63D5KFO7.js.map +1 -0
  78. package/dist/slack-PSQLVTGB.js +3 -0
  79. package/dist/slack-PSQLVTGB.js.map +1 -0
  80. package/dist/store-5MJOO74U.js +2 -0
  81. package/dist/store-5MJOO74U.js.map +1 -0
  82. package/dist/task-store-NM7VKNLE.js +2 -0
  83. package/dist/task-store-NM7VKNLE.js.map +1 -0
  84. package/dist/telegram-W4SLFMNY.js +3 -0
  85. package/dist/telegram-W4SLFMNY.js.map +1 -0
  86. package/dist/timers-PCB4ZIBD.js +2 -0
  87. package/dist/timers-PCB4ZIBD.js.map +1 -0
  88. package/dist/types-C7IFH42Z.js +2 -0
  89. package/dist/types-C7IFH42Z.js.map +1 -0
  90. package/dist/types-OIFU3KSQ.js +2 -0
  91. package/dist/types-OIFU3KSQ.js.map +1 -0
  92. package/dist/usage-dashboard-AXPXB2KP.js +803 -0
  93. package/dist/usage-dashboard-AXPXB2KP.js.map +1 -0
  94. package/dist/web/workflow-editor.global.js +1427 -0
  95. package/dist/web/workflow-editor.global.js.map +1 -0
  96. package/dist/whatsapp-DCHRBRVV.js +3 -0
  97. package/dist/whatsapp-DCHRBRVV.js.map +1 -0
  98. package/dist/wiki-UDPYFJBL.js +2 -0
  99. package/dist/wiki-UDPYFJBL.js.map +1 -0
  100. package/dist/workspace-setup-C4VB3QZV.js +2 -0
  101. package/dist/workspace-setup-C4VB3QZV.js.map +1 -0
  102. package/package.json +10 -3
  103. package/dist/agent-LRSSVFDR.js +0 -2
  104. package/dist/board-dashboard-DAPHL6W2.js +0 -4508
  105. package/dist/board-dashboard-DAPHL6W2.js.map +0 -1
  106. package/dist/chunk-3XULOWYB.js +0 -199
  107. package/dist/chunk-3XULOWYB.js.map +0 -1
  108. package/dist/chunk-6U2XOQHU.js +0 -7
  109. package/dist/chunk-6U2XOQHU.js.map +0 -1
  110. package/dist/chunk-AU37OREL.js +0 -108
  111. package/dist/chunk-AU37OREL.js.map +0 -1
  112. package/dist/chunk-B4Q4X7D5.js +0 -6
  113. package/dist/chunk-B4Q4X7D5.js.map +0 -1
  114. package/dist/chunk-CXY2RYNI.js +0 -11
  115. package/dist/chunk-CXY2RYNI.js.map +0 -1
  116. package/dist/chunk-IEVEMKAI.js +0 -168
  117. package/dist/chunk-IEVEMKAI.js.map +0 -1
  118. package/dist/chunk-KXZMYLHQ.js +0 -46
  119. package/dist/chunk-KXZMYLHQ.js.map +0 -1
  120. package/dist/chunk-M7HKBG3V.js +0 -2
  121. package/dist/chunk-M7HKBG3V.js.map +0 -1
  122. package/dist/chunk-RB7265LO.js +0 -12
  123. package/dist/chunk-RB7265LO.js.map +0 -1
  124. package/dist/chunk-WVNSWBCU.js +0 -138
  125. package/dist/chunk-WVNSWBCU.js.map +0 -1
  126. package/dist/compaction-JBZZ7I7Y.js.map +0 -1
  127. package/dist/config-mutator-6EJHSP3Y.js +0 -2
  128. package/dist/heal-WKY5I4KB.js +0 -2
  129. package/dist/memory-extract-AD7KMRPC.js +0 -2
  130. package/dist/providers-VUIWO3ZK.js +0 -2
  131. package/dist/registry-5ND7GLFJ.js +0 -2
  132. package/dist/scheduler-FCXOXCN5.js +0 -2
  133. package/dist/usage-dashboard-QX3CF6KK.js +0 -404
  134. package/dist/usage-dashboard-QX3CF6KK.js.map +0 -1
  135. package/dist/workspace-setup-ZWTT42XT.js +0 -2
  136. /package/dist/{agent-LRSSVFDR.js.map → agent-4AOMVXRJ.js.map} +0 -0
  137. /package/dist/{chunk-7MBTMCKY.js.map → chunk-63UYRJIC.js.map} +0 -0
  138. /package/dist/{chunk-HLRVDCNH.js.map → chunk-MBEQASP3.js.map} +0 -0
  139. /package/dist/{chunk-RGUYBH77.js.map → chunk-SRHLXZRF.js.map} +0 -0
  140. /package/dist/{chunk-QZP65I2H.js.map → chunk-ZE2YFX4K.js.map} +0 -0
  141. /package/dist/{config-DCJYECL4.js.map → config-KCYHMKCT.js.map} +0 -0
  142. /package/dist/{config-mutator-6EJHSP3Y.js.map → config-mutator-QP4GLBOE.js.map} +0 -0
  143. /package/dist/{heal-WKY5I4KB.js.map → heal-JDXUPRET.js.map} +0 -0
  144. /package/dist/{memory-extract-AD7KMRPC.js.map → memory-extract-WH4FZ456.js.map} +0 -0
  145. /package/dist/{providers-VUIWO3ZK.js.map → program-FFWEILAS.js.map} +0 -0
  146. /package/dist/{registry-5ND7GLFJ.js.map → providers-AKQ4HTB4.js.map} +0 -0
  147. /package/dist/{scheduler-FCXOXCN5.js.map → query-PKGYE5HR.js.map} +0 -0
  148. /package/dist/{workspace-setup-ZWTT42XT.js.map → registry-IFMDJWQ7.js.map} +0 -0
@@ -1,138 +0,0 @@
1
- import{b as Ge}from"./chunk-6U2XOQHU.js";import{g as Ee,i as Le}from"./chunk-IEVEMKAI.js";import{f as De,g as je}from"./chunk-RB7265LO.js";import{b as ye}from"./chunk-RGUYBH77.js";import{f as fe,g as Ne,i as at}from"./chunk-B4Q4X7D5.js";import{b as Be}from"./chunk-QZP65I2H.js";import{e as j}from"./chunk-DSYKYZMT.js";import{e as J}from"./chunk-FIOB3MEX.js";var z=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 s=await this.rpc("tasks/send",{id:`task-${Date.now().toString(36)}`,message:{role:"user",parts:[{type:"text",text:e}]},metadata:t});if(s.error)throw new Error(`A2A error: ${s.error.message}`);return s.result}async*sendTaskStream(e,t){let s=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}}),n=await fetch(this.baseUrl,{method:"POST",headers:{...this.headers(),"Content-Type":"application/json",Accept:"text/event-stream"},body:s});if(!n.ok||!n.body)throw new Error(`A2A stream error: ${n.status}`);let i=n.body.getReader(),r=new TextDecoder,c="";for(;;){let{done:o,value:a}=await i.read();if(o)break;c+=r.decode(a,{stream:!0});let l=c.split(`
2
- `);c=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 s=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(!s.ok)throw new Error(`A2A HTTP error: ${s.status}`);return s.json()}headers(){let e={};return this.token&&(e.Authorization=`Bearer ${this.token}`),e}};var K=class{peers=new Map;healthTimer;config;log;constructor(e,t=console.error.bind(console,"[mesh]")){this.config=e,this.log=t;for(let s of e.mesh.peers)this.peers.set(s.name,{peer:s,client:new z(s.url,s.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(([s,n])=>this.discoverPeer(s,n))),t=Array.from(this.peers.values()).filter(s=>s.healthy).length;this.log(`Discovery complete: ${t}/${this.peers.size} peers healthy`)}async discoverPeer(e,t){let s=this.config.mesh.healthCheck.timeout*1e3;try{let n=new AbortController,i=setTimeout(()=>n.abort(),s),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(n){t.healthy=!1,t.lastCheck=new Date,this.log(`Peer "${e}" unreachable: ${n.message}`)}}async sendTask(e,t,s){let n=this.peers.get(e);if(!n)throw new Error(`Unknown peer: ${e}`);if(!n.healthy)throw new Error(`Peer "${e}" is not healthy`);let i=s||n.agents[0]?.id;if(!i)throw new Error(`Peer "${e}" has no agents`);let r=`${n.peer.url}/task`,c={"Content-Type":"application/json"};n.peer.token&&(c.Authorization=`Bearer ${n.peer.token}`);let o=await fetch(r,{method:"POST",headers:c,body:JSON.stringify({agent:i,message:t})});if(!o.ok)throw new Error(`Peer "${e}" /task error: ${o.status}`);let a=await o.json();if(a.error)throw new Error(`Peer "${e}" agent error: ${a.error}`);return a.content||"No response"}findPeerWithSkill(e){for(let t of this.peers.values())if(t.healthy&&t.agents.some(s=>s.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 ct,writeFileSync as lt,existsSync as ht,mkdirSync as gt}from"fs";import{resolve as He}from"path";function B(d,e,t="\u2026"){if(!d)return"";if(d.length<=e)return d;let s=e-t.length;if(s<=0)return d.slice(0,e);let n=d.slice(0,s),i=n.lastIndexOf(`
3
- `);if(i>s*.6)return n.slice(0,i)+`
4
- `+t;let r=Math.max(n.lastIndexOf(" "),n.lastIndexOf(" "));return r>s*.6?n.slice(0,r)+t:n+t}function be(d,e,t){if(!d)return"";let n=d.split(`
5
- `).slice(0,e).join(`
6
- `);return B(n,t)}var dt=30,Ue=6e3,pt=1500,ut=6e4;function mt(d){let e=new Date(d),t=e.getUTCHours().toString().padStart(2,"0"),s=Math.floor(e.getUTCMinutes()/15)*15;return`${t}:${s.toString().padStart(2,"0")}`}var V=class{dir;cache=new Map;constructor(e=He(process.cwd(),".agentx/groups")){this.dir=e,gt(this.dir,{recursive:!0})}add(e,t,s){let n=this.load(e),i=Date.now(),r=B(s,pt),c=n[n.length-1];if(!(c&&c.sender===t&&c.text===r&&i-c.timestamp<ut)){for(n.push({sender:t,text:r,timestamp:i});n.length>dt;)n.shift();this.cache.set(e,n),this.save(e,n)}}buildContext(e){let t=this.load(e);if(t.length<=1)return"";let s=["[Recent group conversation]"],n=0,i=null,r=[],c="",o=()=>{if(!i)return!0;let a=i.repeat>1?`${i.line} [\xD7${i.repeat}]`:i.line;if(n+a.length>Ue)return!1;if(r.push(a),n+=a.length,i.bucket!==c){let l=`\u2014 ${i.bucket} \u2014`;if(n+l.length>Ue)return i=null,!1;r.push(l),n+=l.length,c=i.bucket}return i=null,!0};for(let a=t.length-2;a>=0;a--){let l=t[a],h=`${l.sender}: ${l.text}`,g=mt(l.timestamp);if(i&&i.line===h&&i.bucket===g){i.repeat++;continue}if(!o())break;i={line:h,repeat:1,bucket:g}}o();for(let a=r.length-1;a>=0;a--)s.splice(1,0,r[a]);return s.length<=1?"":(s.push("[End of conversation \u2014 respond to the latest message]"),s.join(`
7
- `))}getEntries(e){return[...this.load(e)]}filePath(e){let t=e.replace(/[^a-zA-Z0-9_-]/g,"_");return He(this.dir,`${t}.json`)}load(e){if(this.cache.has(e))return this.cache.get(e);let t=this.filePath(e);if(!ht(t))return this.cache.set(e,[]),[];try{let s=JSON.parse(ct(t,"utf-8")),n=Array.isArray(s)?s:[];return this.cache.set(e,n),n}catch{return this.cache.set(e,[]),[]}}save(e,t){try{lt(this.filePath(e),JSON.stringify(t))}catch{}}};var ft={minChars:80,maxChars:4e3,idleFlushMs:2e3,minIntervalMs:1500,humanPacing:!1},yt={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}},q=class{config;buffer="";emitter;lastEmitTime=0;idleTimer;insideCodeFence=!1;fencePattern=/^```/;lastEmission=Promise.resolve();constructor(e,t,s){let n=s?yt[s]:{};this.config={...ft,...n,...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)}async flush(){this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=void 0),this.buffer.length>0&&(this.emitImmediate(this.buffer),this.buffer=""),await this.lastEmission}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),s=this.buffer.slice(0,t);if(this.buffer=this.buffer.slice(t),this.config.humanPacing){let n=800+Math.random()*1700;setTimeout(()=>this.emitImmediate(s),n)}else this.emitImmediate(s)}emitImmediate(e){this.lastEmitTime=Date.now(),this.lastEmission=this.lastEmission.then(()=>Promise.resolve().then(()=>this.emitter(e)).catch(()=>{}))}findBreakPoint(){let e=this.buffer,t=Math.min(e.length,this.config.maxChars),s=e.lastIndexOf(`
8
-
9
- `,t);if(s>=this.config.minChars)return s+2;let n=e.lastIndexOf(`
10
- `,t);if(n>=this.config.minChars)return n+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]===`
11
- `))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(`
12
- `);for(let s of t)this.fencePattern.test(s.trim())&&(this.insideCodeFence=!this.insideCodeFence)}};import{readFileSync as We,writeFileSync as Je,existsSync as ke,mkdirSync as ze,appendFileSync as Fe}from"fs";import{resolve as we,dirname as Ke}from"path";var $e=class{filePath;constructor(e){this.filePath=we(e,".agentx/router/inflight.jsonl"),ze(Ke(this.filePath),{recursive:!0})}start(e){try{let t=JSON.stringify({type:"start",ts:Date.now(),...e})+`
13
- `;Fe(this.filePath,t)}catch{}}done(e){try{Fe(this.filePath,JSON.stringify({type:"done",id:e,ts:Date.now()})+`
14
- `)}catch{}}loadUnfinished(){if(!ke(this.filePath))return[];try{let e=We(this.filePath,"utf-8"),t=new Map;for(let s of e.split(`
15
- `))if(s.trim())try{let n=JSON.parse(s);n.type==="start"?t.set(n.id,n):n.type==="done"&&t.delete(n.id)}catch{}return[...t.values()]}catch{return[]}}compact(){try{let e=this.loadUnfinished(),t=e.map(s=>JSON.stringify(s)).join(`
16
- `)+(e.length?`
17
- `:"");Je(this.filePath,t)}catch{}}};var bt=4e3,ve=class{registry;config;channels=new Map;hooks;mesh;serviceMatcher;business;groupLog;log;recentMessageIds=new Map;MESSAGE_ID_TTL_MS=5*60*1e3;MESSAGE_ID_MAX_ENTRIES=2e3;DEDUP_STORE_PATH=".agentx/router/dedup.json";dedupDirtyCount=0;dedupSaveTimer;inflight;constructor(e,t,s,n=console.error.bind(console,"[router]")){this.registry=e,this.config=t,this.hooks=s,this.log=n,this.groupLog=new V,this.inflight=new $e(process.cwd()),this.loadDedupFromDisk()}loadDedupFromDisk(){try{let e=we(process.cwd(),this.DEDUP_STORE_PATH);if(!ke(e))return;let t=JSON.parse(We(e,"utf-8")),s=Date.now(),n=0;for(let[i,r]of Object.entries(t))typeof r=="number"&&s-r<this.MESSAGE_ID_TTL_MS&&(this.recentMessageIds.set(i,r),n++);n>0&&this.log(`Router dedup: restored ${n} recent message ids from disk`)}catch{}}scheduleDedupSave(){if(this.dedupDirtyCount++,this.dedupDirtyCount>=20){this.flushDedupToDisk();return}this.dedupSaveTimer||(this.dedupSaveTimer=setTimeout(()=>this.flushDedupToDisk(),5e3))}flushDedupToDisk(){this.dedupSaveTimer&&(clearTimeout(this.dedupSaveTimer),this.dedupSaveTimer=void 0),this.dedupDirtyCount=0;try{let e=Date.now(),t={};for(let[i,r]of this.recentMessageIds)e-r<this.MESSAGE_ID_TTL_MS&&(t[i]=r);let s=we(process.cwd(),this.DEDUP_STORE_PATH),n=Ke(s);ke(n)||ze(n,{recursive:!0}),Je(s,JSON.stringify(t))}catch{}}flushPersistence(){this.flushDedupToDisk(),this.inflight.compact()}setMesh(e){this.mesh=e}setServiceMatcher(e){this.serviceMatcher=e}setBusiness(e){this.business=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 s=e.accountId;return t.name==="telegram"&&!s&&e.agentId&&(s=this.getAccountForAgent(e.agentId)),this.log(`Outbound [${e.channel}] -> ${e.chatId}: ${e.text.slice(0,80)}`),t.name==="telegram"&&s?this.adapterSend(t,{...e,accountId:s}):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();this.replayInflight().catch(e=>this.log(`Inflight replay error: ${e.message}`))}async replayInflight(){let e=this.inflight.loadUnfinished();if(e.length!==0){this.log(`Inflight replay: ${e.length} task(s) to resume from previous run`);for(let t of e){let s=this.channels.get(t.channel);if(!s){this.log(`Inflight replay: adapter "${t.channel}" not available, skipping task ${t.id}`),this.inflight.done(t.id);continue}let n={id:t.id,channel:t.channel,accountId:t.accountId,sender:t.sender,group:t.group,text:t.text,replyTo:t.replyTo,replyToText:t.replyToText,timestamp:new Date(t.timestamp),resolvedAgent:t.resolvedAgent,preferNode:t.preferNode};this.log(`Inflight replay: ${t.channel}/${t.id} (agent=${t.resolvedAgent||"resolve"})`),this.handleMessage(s,n,{replay:!0}).catch(i=>this.log(`Inflight replay failed for ${t.id}: ${i.message}`)).finally(()=>this.inflight.done(t.id))}this.inflight.compact()}}async stopAll(){for(let[e,t]of this.channels)this.log(`Stopping channel: ${e}`),await t.stop()}isDuplicateMessage(e){if(!e.id)return!1;let t=`${e.channel}:${e.accountId||"default"}:${e.id}`,s=Date.now();if(this.recentMessageIds.size>=this.MESSAGE_ID_MAX_ENTRIES)for(let[i,r]of this.recentMessageIds)s-r>this.MESSAGE_ID_TTL_MS&&this.recentMessageIds.delete(i);let n=this.recentMessageIds.get(t);return n&&s-n<this.MESSAGE_ID_TTL_MS?!0:(this.recentMessageIds.set(t,s),this.scheduleDedupSave(),!1)}async handleMessage(e,t,s={}){if(!s.replay&&this.isDuplicateMessage(t)){this.log(`Duplicate ${t.channel} message dropped: ${t.accountId||"default"}/${t.id}`);return}if(this.hooks?.has("pre:channel-message")){let r=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(r.blocked){this.log(`Message blocked by hook: ${r.message}`);return}r.modified?.text&&(t={...t,text:r.modified.text})}if(t.group){let r=t.group.id;this.groupLog.add(r,t.sender.name,t.text)}if(this.serviceMatcher){let r=t.group?.id||t.sender.id,c=this.serviceMatcher.match(t.text,t.sender.id,t.channel);if(c){this.log(`Service matched: "${c.service.name}" for ${t.sender.name} (trigger: ${c.trigger})`);let o=t.accountId;this.adapterReact(e,r,t.id,"\u{1F440}",o);let a=this.startTypingLoop(e,r,o);await this.serviceMatcher.execute(c.service,this.registry,{channel:t.channel,sender:t.sender.name,chatId:r},async l=>{clearInterval(a),await this.adapterSend(e,{channel:t.channel,chatId:r,text:l,replyTo:t.id,accountId:o})});return}}let n=this.resolveAgent(t);if(!n)return;if(t.group&&t.channel==="telegram"){let r=this.getAccountForAgent(n);if(r&&r!==t.accountId)return}let i=t.group?.id||t.sender.id;this.inflight.start({id:t.id,channel:t.channel,accountId:t.accountId,text:t.text,sender:t.sender,group:t.group,replyTo:t.replyTo,replyToText:t.replyToText,timestamp:(t.timestamp instanceof Date?t.timestamp:new Date).toISOString(),resolvedAgent:n,preferNode:t.preferNode});try{return await this.processResolvedMessage(e,t,n,i)}finally{this.inflight.done(t.id)}}async processResolvedMessage(e,t,s,n){if(t.preferNode){this.log(`preferNode="${t.preferNode}" \u2014 forcing mesh routing for agent "${s}"`),await this.handleViaMeshByPeer(e,t,s,t.preferNode)||this.log(`Mesh peer "${t.preferNode}" not found or unhealthy for agent "${s}"`);return}let i=this.registry.getAgent(s);if(!i){await this.handleViaMeshByAgentId(e,t,s)||this.log(`Agent "${s}" not found locally or on any mesh peer`);return}let r=i.name||s,c=this.getAccountForAgent(s)||t.accountId;this.log(`Routing [${t.channel}/${t.sender.name}] -> "${r}": ${t.text.slice(0,80)}`),this.adapterReact(e,n,t.id,"\u{1F440}",c);let o=this.startTypingLoop(e,n,c),a=typeof e.editMessage=="function",l,h="",g=a?new q(async k=>{if(h+=k,l)try{await this.adapterEdit(e,n,l,h,void 0,c)}catch{}else{let w=h.length>20?h:`_${r} is writing..._
18
-
19
- ${h}`;try{l=await this.adapterSend(e,{channel:t.channel,chatId:n,text:w,replyTo:t.id,accountId:c})}catch{}}},void 0,t.channel):void 0,m=g?(k,w)=>{g.push(k)}:void 0;if(t.channelMeta?.agents){for(let k of t.channelMeta.agents)if(!k.handle){let w=this.registry.getAgent(k.id);w&&(k.handle=w.mentions.find(S=>S.startsWith("@")),k.name=w.name)}}let p=t.group?this.groupLog.buildContext(n):"",f=p?`${p}
20
-
21
- ${t.sender.name}: ${t.text}`:t.text,u=await this.registry.execute({message:f,agentId:s,context:{channel:t.channel,sender:t.sender.name,senderId:t.sender.id,senderUsername:t.sender.username,group:t.group?.name,chatId:n,mediaPath:t.media?.path,mediaType:t.media?.type,replyToText:t.replyToText,channelMeta:t.channelMeta}},m);if(clearInterval(o),await g?.flush(),u.error){if(u.error.startsWith("__queued__")){clearInterval(o),this.log(`Message queued for ${r}`);return}this.log(`Agent error: ${u.error}`);let k=`Error: ${u.error}`;l?await this.adapterEdit(e,n,l,k,"plain",c):await this.adapterSend(e,{channel:t.channel,chatId:n,text:k,replyTo:t.id,parseMode:"plain",accountId:c});return}let y=u.content;if(this.hooks?.has("post:channel-message")){let k=await this.hooks.execute("post:channel-message",{event:"post:channel-message",channel:t.channel,sender:t.sender.name,response:y,agentId:s});if(k.blocked){this.log(`Response blocked by hook: ${k.message}`);return}k.modified?.response&&(y=k.modified.response)}t.channel==="gitlab"&&y&&(this.channels.get("gitlab")?.getAgentToken?.(s)||(y=`> **${r}** (${s})
22
-
23
- ${y}`)),t.channel==="whatsapp"&&y&&(y=`*${r}*
24
-
25
- ${y}`);let b;y&&(l?(await this.adapterEdit(e,n,l,y,void 0,c),b=l):b=await this.adapterSend(e,{channel:t.channel,chatId:n,text:y,replyTo:t.id,accountId:c,agentId:s})),t.group&&y&&this.groupLog.add(n,r,y);let $=this.config.notifications;if($?.destination&&u.duration){let k=($.longTaskThreshold||30)*1e3;if(u.duration>=k&&!u.error&&$.on?.taskComplete||u.error&&$.on?.taskError){let S=Math.round(u.duration/1e3),A=u.error?"failed":"completed",T=be(t.text,6,300),M=u.error?B(u.error,300):be(u.content,6,400),N=`${A==="failed"?"\u{1F534}":"\u2705"} **${r}** ${A} (${S}s)
26
- ${t.channel}/${t.sender.name}: ${T}
27
- ${M}`;this.sendOutbound({channel:$.destination.channel,chatId:$.destination.chatId,text:N,agentId:s,accountId:$.destination.accountId}).catch(_=>{this.log(`Task notification failed: ${_.message}`)})}}if(this.business&&u.duration&&this.business.recordTaskCompletion(s,Math.round(u.duration/1e3),!u.error,t.channel),t.channel==="gitlab"&&u.duration&&!u.error){let k=this.channels.get("gitlab");k&&k.logTimeSpent(n,u.duration,s).catch(w=>{this.log(`GitLab time tracking failed: ${w.message}`)})}y&&b&&["telegram","whatsapp","discord","slack"].includes(t.channel)&&this.handleBotToBotChain(e,t,s,y,b,0).catch(k=>{this.log(`Bot-to-bot error: ${k.message}`)})}async handleBotToBotChain(e,t,s,n,i,r,c=new Set){if(r>=ve.MAX_BOT_CHAIN_DEPTH){this.log(`Bot-to-bot: max depth (${r}) reached, stopping`);return}c.add(s);for(let[o,a]of Object.entries(this.config.agents)){if(o===s)continue;if(c.has(o)){this.log(`Bot-to-bot: "${o}" already participated, stopping chain`);continue}if(!a.mentions.filter(f=>f.startsWith("@")).some(f=>n.toLowerCase().includes(f.toLowerCase())))continue;this.log(`Bot-to-bot [${r+1}]: "${s}" -> "${o}"`);let g=t.group?.id||t.sender.id,m=this.getAccountForAgent(o),p=this.getAccountForAgent(s);try{this.adapterReact(e,g,i,"\u{1F440}",m);let f=this.startTypingLoop(e,g,m),u=r===0?`[Original from ${t.sender.name}]: ${t.text}
28
-
29
- [${s} said]: ${n}`:n,y=await this.registry.execute({message:u,agentId:o,context:{channel:t.channel,sender:`agent:${s}`,group:t.group?.name}});if(clearInterval(f),y.content&&!y.error){let b=y.content;t.channel==="whatsapp"&&(b=`*${a.name}*
30
-
31
- ${b}`);let $=await this.adapterSend(e,{channel:t.channel,chatId:g,text:b,accountId:m});$&&y.content&&await this.handleBotToBotChain(e,t,o,y.content,$,r+1,c)}else y.error&&this.log(`Bot-to-bot "${o}" error: ${y.error}`)}catch(f){this.log(`Bot-to-bot "${o}" failed: ${f.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,s,n,i,r){return e.name==="telegram"&&r?e.editMessage(t,s,n,i,r):e.editMessage?.(t,s,n,i)??!1}adapterReact(e,t,s,n,i){e.name==="telegram"&&i?e.react(t,s,n,i):e.react?.(t,s,n)}startTypingLoop(e,t,s){let n=()=>{e.name==="telegram"&&s?e.sendTyping(t,s):e.sendTyping?.(t)};return n(),setInterval(n,bt)}async handleViaMesh(e,t){if(!this.mesh)return!1;let s=t.text.toLowerCase(),n=this.mesh.directory();for(let i of n)if(i.healthy){for(let r of i.skills)if(s.includes(r.id.toLowerCase())||s.includes(r.name.toLowerCase())){this.log(`Mesh routing [${t.channel}/${t.sender.name}] -> peer "${i.peer}" agent "${r.id}"`);let c=t.group?.id||t.sender.id,o=t.accountId;this.adapterReact(e,c,t.id,"\u{1F440}",o);let a=this.startTypingLoop(e,c,o);try{let l=await this.mesh.sendTask(i.peer,t.text,r.id);if(clearInterval(a),l){let h=`**${r.name}** _(${i.peer})_:
32
-
33
- `;await this.adapterSend(e,{channel:t.channel,chatId:c,text:h+l,replyTo:t.id,accountId:o})}return!0}catch(l){return clearInterval(a),this.log(`Mesh routing error: ${l.message}`),await this.adapterSend(e,{channel:t.channel,chatId:c,text:`Error from ${i.peer}/${r.name}: ${l.message}`,replyTo:t.id,parseMode:"plain",accountId:o}),!0}}}return!1}async handleViaMeshByAgentId(e,t,s){if(!this.mesh)return!1;let n=this.mesh.directory();for(let i of n){if(!i.healthy)continue;let r=i.skills.find(l=>l.id===s);if(!r)continue;this.log(`Mesh routing by agentId [${t.channel}/${t.sender.name}] -> peer "${i.peer}" agent "${s}"`);let c=t.group?.id||t.sender.id,o=t.accountId;this.adapterReact(e,c,t.id,"\u{1F440}",o);let a=this.startTypingLoop(e,c,o);try{let l=await this.mesh.sendTask(i.peer,t.text,s);return clearInterval(a),l&&await this.adapterSend(e,{channel:t.channel,chatId:c,text:l,replyTo:t.id,accountId:o}),!0}catch(l){return clearInterval(a),this.log(`Mesh routing error for ${s}: ${l.message}`),await this.adapterSend(e,{channel:t.channel,chatId:c,text:`Error from ${i.peer}/${r.name}: ${l.message}`,replyTo:t.id,parseMode:"plain",accountId:o}),!0}}return!1}async handleViaMeshByPeer(e,t,s,n){if(!this.mesh)return!1;let r=this.mesh.directory().find(g=>g.peer===n);if(!r||!r.healthy)return!1;let c=r.skills.find(g=>g.id===s)??{name:s,id:s};this.log(`Mesh routing by preferNode [${t.channel}/${t.sender.name}] -> peer "${n}" agent "${s}"`);let o=t.group?.id||t.sender.id,a=t.accountId;this.adapterReact(e,o,t.id,"\u{1F440}",a);let l=this.startTypingLoop(e,o,a),h=Date.now();try{let g=await this.mesh.sendTask(n,t.text,s),m=Date.now()-h;if(clearInterval(l),g&&await this.adapterSend(e,{channel:t.channel,chatId:o,text:g,replyTo:t.id,accountId:a}),t.channel==="gitlab"&&m){let p=this.channels.get("gitlab");p&&p.logTimeSpent(o,m,s).catch(f=>{this.log(`GitLab time tracking (mesh) failed: ${f.message}`)})}return!0}catch(g){return clearInterval(l),this.log(`Mesh routing error for ${n}/${s}: ${g.message}`),await this.adapterSend(e,{channel:t.channel,chatId:o,text:`Error from ${n}/${c.name}: ${g.message}`,replyTo:t.id,parseMode:"plain",accountId:a}),!0}}getAccountForAgent(e){for(let[t,s]of Object.entries(this.config.channels.telegram.accounts))if(s.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;let t=e.sender.isBot===!0;if(e.channel==="telegram"&&this.config.channels.telegram.policy.group==="mention-required"){let i=this.registry.findByMention(e.text,{atMentionsOnly:t});return i||void 0}let s=this.registry.findByMention(e.text,{atMentionsOnly:t});if(s)return s;if(!t)return e.channel==="telegram"?this.config.channels.telegram.accounts[e.accountId]?.agentBinding:this.config.channels.whatsapp.defaultAgent}},G=ve;J(G,"MAX_BOT_CHAIN_DEPTH",3);function X(d){return d.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function Te(d){let t=d.replace(/\\([_*\[\]()~`>#+=|{}.!\\-])/g,"$1").split(`
34
- `),s=[],n=!1,i="",r=[],c=!1,o=[],a=!1;for(let h=0;h<t.length;h++){let g=t[h];if(g.trimStart().startsWith("```"))if(n){n=!1;let u=X(r.join(`
35
- `)),y=i?`// ${i}
36
- `:"";s.push(`<pre><code>${y}${u}</code></pre>`),i="";continue}else{n=!0,i=g.trimStart().slice(3).trim(),r=[];continue}if(n){r.push(g);continue}if(c&&!g.trimStart().startsWith(">")&&(s.push("</blockquote>"),c=!1),a&&!g.trim().startsWith("|")&&(a=!1,o=[]),g.trimStart().startsWith("> ")){let u=g.replace(/^>\s*/,"");c||(s.push("<blockquote>"),c=!0),s.push(x(u));continue}let m=g.match(/^(#{1,6})\s+(.+)$/);if(m){s.push(""),s.push(`<b>${x(m[2])}</b>`);continue}if(/^[-*_]{3,}\s*$/.test(g.trim())){s.push("\u2014\u2014\u2014");continue}if(g.trim().startsWith("|")&&g.trim().endsWith("|")){let u=g.split("|").slice(1,-1).map(y=>y.trim());if(/^\|[\s\-:|]+\|$/.test(g.trim())){a=!0;continue}if(!a){o=u,a=!0;continue}if(o.length>0&&u.length>0)if(u.length>=2){let y=[];for(let b=0;b<u.length;b++)if(b===0)y.push(`<b>${x(u[b])}</b>`);else{let $=o[b]?`${x(o[b])}: `:"";y.push(`${$}${x(u[b])}`)}s.push(`\u2022 ${y.join(" \u2014 ")}`)}else s.push(`\u2022 ${x(u[0])}`);continue}let p=g.match(/^(\s*)[-*+]\s+(.+)$/);if(p){let u=p[1].length>0?" ":"";s.push(`${u}\u2022 ${x(p[2])}`);continue}let f=g.match(/^(\s*)\d+[.)]\s+(.+)$/);if(f){let u=f[1].length>0?" ":"",y=g.match(/^(\s*)(\d+)/)?.[2]||"1";s.push(`${u}${y}. ${x(f[2])}`);continue}if(!g.trim()){s.push("");continue}s.push(x(g))}n&&s.push(`<pre><code>${X(r.join(`
37
- `))}</code></pre>`),c&&s.push("</blockquote>");let l=s.join(`
38
- `).trim();return l=wt(l),l}function x(d){let e=X(d);return e=e.replace(/`([^`]+)`/g,"<code>$1</code>"),e=e.replace(/\[([^\]]+)\]\(([^)]+)\)/g,(t,s,n)=>`<a href="${X(n)}">${s}</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 kt=/(?<=\w)\.(ts|js|py|rs|go|rb|cs|sh|md|yml|yaml|toml|json|env|css|html|xml|sql|tf|hcl)(?=[\s,;:)\]}<]|$)/gi;function wt(d){let e=d.split(/(<\/?(?:code|pre|a)[^>]*>)/gi),t=!1;return e.map(s=>/<(?:code|pre|a)\b/i.test(s)?(t=!0,s):/<\/(?:code|pre|a)>/i.test(s)?(t=!1,s):t?s:s.replace(kt,"<code>.$1</code>")).join("")}import{readFileSync as Ve,writeFileSync as qe,mkdirSync as Xe,existsSync as Se}from"fs";import{resolve as Qe,dirname as Ye}from"path";var Me=class{data={};filePath;dirty=!1;constructor(e){this.filePath=Qe(e,".agentx/telegram/groups.json"),this.load()}load(){try{Se(this.filePath)&&(this.data=JSON.parse(Ve(this.filePath,"utf-8")))}catch{this.data={}}}save(){try{Xe(Ye(this.filePath),{recursive:!0}),qe(this.filePath,JSON.stringify(this.data,null,2)),this.dirty=!1}catch{}}setBotStatus(e,t,s,n,i,r){this.data[e]||(this.data[e]={name:t,bots:{}}),t&&(this.data[e].name=t),this.data[e].bots[s]={username:i,agentId:n,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(([,s])=>s.status!=="left"&&s.status!=="kicked").map(([s,n])=>({accountId:s,agentId:n.agentId,username:n.username})):[]}hasGroup(e){return!!this.data[e]}},Ie=class{offsets={};filePath;dirty=!1;saveTimer;constructor(e){this.filePath=Qe(e,".agentx/telegram/offsets.json");try{Se(this.filePath)&&(this.offsets=JSON.parse(Ve(this.filePath,"utf-8")))}catch{this.offsets={}}}get(e){return this.offsets[e]||0}set(e,t){this.offsets[e]!==t&&(this.offsets[e]=t,this.dirty=!0,this.saveTimer||(this.saveTimer=setTimeout(()=>this.flush(),500)))}flush(){if(this.saveTimer&&(clearTimeout(this.saveTimer),this.saveTimer=void 0),!!this.dirty)try{let e=Ye(this.filePath);Se(e)||Xe(e,{recursive:!0}),qe(this.filePath,JSON.stringify(this.offsets,null,2)),this.dirty=!1}catch{}}},Q=class{name="telegram";accounts;offsetStore;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 Me(process.cwd()),this.offsetStore=new Ie(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[s,n]=e[t];this.log(`Starting polling for account "${s}" (${t+1}/${e.length})`);try{await this.apiCall(n.token,"deleteWebhook",{drop_pending_updates:!1}).catch(()=>{});let i=await this.apiCall(n.token,"getMe"),r=i.result?.id,c=i.result?.username;this.log(`Bot @${c} ready (account: ${s})`),r&&this.botInfo.set(s,{userId:r,username:c||s}),this.pollLoop(s,n)}catch(i){this.log(`Failed to verify bot for account "${s}": ${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,this.offsetStore.flush();let e=Array.from(this.accounts.entries()).map(async([t,s])=>{try{await this.apiCall(s.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 s=this.getTokenForAccount(t);if(s)return s}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 s=4096,n=e.text.length>s?e.text.slice(0,s-3)+"...":e.text,i=e.parseMode==="markdown"||e.parseMode===void 0?Te(n):n,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=n):e.parseMode==="plain"&&(delete r.parse_mode,r.text=n);try{let c=await this.apiCall(t,"sendMessage",r);return String(c.result?.message_id||"")}catch(c){if(r.parse_mode){delete r.parse_mode,r.text=n;let o=await this.apiCall(t,"sendMessage",r);return String(o.result?.message_id||"")}throw c}}async editMessage(e,t,s,n,i){let r=this.resolveToken(e,i);if(!r)return!1;let c=4096,o=s.length>c?s.slice(0,c-3)+"...":s,a=n!=="html"&&n!=="plain"?Te(o):o,l={chat_id:e,message_id:parseInt(t,10),text:a,parse_mode:"HTML"};n==="html"?(l.parse_mode="HTML",l.text=o):n==="plain"&&(delete l.parse_mode,l.text=o);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=o;try{return await this.apiCall(r,"editMessageText",l),!0}catch{return!1}}return!1}}async react(e,t,s="\u{1F440}",n){let i=this.resolveToken(e,n);if(i)try{await this.apiCall(i,"setMessageReaction",{chat_id:e,message_id:parseInt(t,10),reaction:[{type:"emoji",emoji:s}]})}catch{}}async sendTyping(e,t){let s=this.resolveToken(e,t);if(s)try{await this.apiCall(s,"sendChatAction",{chat_id:e,action:"typing"})}catch{}}async pollLoop(e,t){let s=0;for(;this.polling;)try{let n=this.offsetStore.get(e)||0,r=(await this.apiCall(t.token,"getUpdates",{offset:n||void 0,timeout:30,allowed_updates:["message","my_chat_member"]})).result||[];for(let c of r){if(this.offsetStore.set(e,c.update_id+1),c.message&&this.handler){let o=c.message,a=o.text||o.caption||"",l,h=o.photo&&o.photo.length>0,g=!!o.voice,m=!!o.audio,p=!!o.video,f=!!o.document;if(h||g||m||p||f){let k,w="application/octet-stream";if(h?(k=o.photo[o.photo.length-1].file_id,w="image/jpeg",a||(a="[Photo attached \u2014 please describe what you see]")):g?(k=o.voice.file_id,w=o.voice.mime_type||"audio/ogg",a||(a="[Voice message \u2014 please transcribe and respond]")):m?(k=o.audio.file_id,w=o.audio.mime_type||"audio/mpeg",a||(a=`[Audio: ${o.audio.title||"audio file"}]`)):p?(k=o.video.file_id,w=o.video.mime_type||"video/mp4",a||(a="[Video attached]")):f&&(k=o.document.file_id,w=o.document.mime_type||"application/octet-stream",a||(a=`[Document: ${o.document.file_name||"file"}]`)),k)try{let A=(await this.apiCall(t.token,"getFile",{file_id:k})).result?.file_path;if(A){let T=`https://api.telegram.org/file/bot${t.token}/${A}`,M=await fetch(T);if(M.ok){let N=Buffer.from(await M.arrayBuffer()),_=w.split("/")[1]?.split(";")[0]||"bin",{mkdirSync:F,writeFileSync:W}=await import("fs"),{randomUUID:pe}=await import("crypto"),{resolve:C,join:R}=await import("path"),P=C(process.cwd(),".agentx/media/telegram");F(P,{recursive:!0});let E=o.document?.file_name||`${pe()}.${_}`,O=R(P,E);W(O,N),l={path:O,type:w,fileName:E}}}}catch(S){this.log(`Media download failed: ${S.message}`)}}if(!a)continue;let y=o.chat.type!=="private",b=String(o.chat.id),$;y&&(this.groupStore.hasGroup(b)||await this.seedGroupMembership(b,o.chat.title),$=await this.getChannelMeta(b));let v={id:String(o.message_id),channel:"telegram",accountId:e,sender:{id:String(o.from.id),name:[o.from.first_name,o.from.last_name].filter(Boolean).join(" "),username:o.from.username,isBot:o.from.is_bot===!0},group:y?{id:b,name:o.chat.title||""}:void 0,text:a,media:l,replyTo:o.reply_to_message?String(o.reply_to_message.message_id):void 0,replyToText:o.reply_to_message?o.reply_to_message.text||o.reply_to_message.caption||`[message from ${o.reply_to_message.from?.first_name||"unknown"}]`:void 0,timestamp:new Date(o.date*1e3),raw:c,channelMeta:$};this.chatAccountMap.set(String(o.chat.id),e),this.handler(v).catch(k=>{this.log(`Error handling message: ${k.message}`)})}if(c.my_chat_member){let o=c.my_chat_member,a=String(o.chat.id),l=o.chat.title,h=o.new_chat_member.status,g=o.new_chat_member.user.username||e;this.groupStore.setBotStatus(a,l,e,t.agentBinding,g,h),this.log(`Group membership: @${g} is now "${h}" in "${l||a}"`)}}s=0}catch(n){s++;let i=Math.min(5e3*Math.pow(2,s-1),6e4);this.log(`Poll error (${e}): ${n.message} [retry in ${i/1e3}s, errors: ${s}]`),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(s=>({id:s.agentId,name:s.username,handle:`@${s.username}`})),facts:[`${t.length} bot(s) verified in this group`]}}async seedGroupMembership(e,t){this.log(`Seeding group membership for "${t||e}"`);for(let[s,n]of this.accounts){let i=this.botInfo.get(s);if(i)try{let c=(await this.apiCall(n.token,"getChatMember",{chat_id:Number(e),user_id:i.userId})).result?.status;c&&this.groupStore.setBotStatus(e,t,s,n.agentBinding,i.username,c)}catch{this.groupStore.setBotStatus(e,t,s,n.agentBinding,i.username,"left")}}this.log(`Seeded: ${this.groupStore.getGroupBots(e).length} bot(s) in "${t||e}"`)}async apiCall(e,t,s){let n=`https://api.telegram.org/bot${e}/${t}`,i=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:s?JSON.stringify(s):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 Ze,writeFileSync as $t}from"fs";import{resolve as et,join as vt}from"path";import{randomUUID as Tt}from"crypto";var Y=class{name="whatsapp";sessionDir;defaultAgent;allowFrom;routes;handler;sock=null;reconnectAttempts=0;generation=0;sentMessageIds=new Set;log;onQR;onStatus;constructor(e,t=console.error.bind(console,"[whatsapp]")){this.sessionDir=et(e.sessionDir),this.defaultAgent=e.defaultAgent,this.allowFrom=e.allowFrom,this.routes=e.routes||[],this.onQR=e.onQR,this.onStatus=e.onStatus,this.log=t}resolveAgent(e,t,s){for(let n of this.routes){if(n.contact){let i=n.contact.replace(/\+/g,"");if(e.includes(i)||i.includes(e))return n.agent}if(n.group&&(t||s)){let i=n.group.toLowerCase();if(t?.toLowerCase().includes(i)||s?.toLowerCase().includes(i))return n.agent}}return this.defaultAgent}onMessage(e){this.handler=e}async start(){let e,t,s,n;try{n=await import("@whiskeysockets/baileys"),e=n.default||n.makeWASocket,t=n.useMultiFileAuthState,s=n.DisconnectReason}catch{this.log("WhatsApp requires @whiskeysockets/baileys. Install with:"),this.log(" npm install @whiskeysockets/baileys");return}Ze(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:c}=await t(this.sessionDir),o;try{let{version:l}=await n.fetchLatestBaileysVersion();o=l,this.log(`WhatsApp Web version: ${l.join(".")}`)}catch{this.log("Could not fetch WA version, using default")}let a={level:"silent",trace:()=>{},debug:()=>{},info:()=>{},warn:()=>{},fatal:()=>{},error:(...l)=>this.log("WA error:",...l),child:()=>a};this.sock=e({auth:{creds:r.creds,keys:n.makeCacheableSignalKeyStore?n.makeCacheableSignalKeyStore(r.keys,a):r.keys},...o?{version:o}:{},logger:a,printQRInTerminal:!1,browser:["agentx","server","1.0"],syncFullHistory:!1,markOnlineOnConnect:!1}),this.sock.ev.on("creds.update",c),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:m}=l;if(i===this.generation){if(m){this.log("Scan QR code with WhatsApp to connect:");try{this.onQR?.(m)}catch{}try{let{default:p}=await import("qrcode-terminal");p.generate(m,{small:!0})}catch{this.log(`QR: ${m}`),this.log("Install qrcode-terminal for visual QR: npm install qrcode-terminal")}}if(h)try{this.onStatus?.(h,g?.error?.message)}catch{}if(h==="open")try{this.onQR?.(null)}catch{}if(h==="close"){let p=g?.error?.output?.statusCode,f=g?.error?.output?.payload?.message||"";if(this.log(`WhatsApp connection closed (status: ${p}, reason: ${f})`),p===s?.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 u=Math.min(1e4*Math.pow(2,this.reconnectAttempts-1),12e4);this.log(`Reconnecting in ${u/1e3}s (attempt ${this.reconnectAttempts}/5)...`),setTimeout(()=>this.start(),u)}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),m=!!(h.message?.conversation||h.message?.extendedTextMessage?.text);if(this.log(`WA msg: from=${g} fromMe=${h.key.fromMe} hasText=${m} 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 C=this.sock?.user,R=h.key.remoteJid||"",P=C?.id?.replace(/:.*/,"")||"",E=C?.lid?.replace(/:.*/,"")||"",O=R.replace(/:.*/,"").replace(/@.*/,"");if(!(O===P||O===E))continue}let p=h.message?.conversation||h.message?.extendedTextMessage?.text||h.message?.imageMessage?.caption||h.message?.videoMessage?.caption||"",f=h.message||{},u=!!f.imageMessage,y=!!f.audioMessage,b=!!f.videoMessage,$=!!f.documentMessage,v=!!f.stickerMessage,k=u||y||b||$||v;if(k&&!p&&(u?p="[Image attached \u2014 please describe what you see]":y?p="[Voice message attached \u2014 please transcribe and respond]":b?p="[Video attached]":$?p=`[Document: ${f.documentMessage?.fileName||"file"}]`:v&&(p="[Sticker]")),!p)continue;let w=h.key.remoteJid||"",S=w.endsWith("@g.us"),A=w.replace(/@.*$/,""),M=(S?h.key.participant||"":w).replace(/@.*$/,"");if(this.allowFrom?.length&&!h.key.fromMe&&!this.allowFrom.some(R=>{let P=R.replace(/\+/g,"");return M.includes(P)||A.includes(P)}))continue;let N=h.key.fromMe?"me":h.pushName||M,_;if(S&&this.sock)try{_=(await this.sock.groupMetadata(w)).subject}catch{}let F=this.resolveAgent(M,_,S?w:void 0);if(!F){this.log(`No route for ${S?`group ${_||w}`:M}, skipping`);continue}let W;if(k&&this.sock)try{let R=await(await import("@whiskeysockets/baileys")).downloadMediaMessage(h,"buffer",{},{reuploadRequest:this.sock.updateMediaMessage,logger:this.sock.logger});if(R){let P=f.imageMessage?.mimetype||f.audioMessage?.mimetype||"audio/ogg",E=P.split("/")[1]?.split(";")[0]||"bin",O=et(this.sessionDir,"../media/inbound");Ze(O,{recursive:!0});let ue=f.documentMessage?.fileName||`${Tt()}.${E}`,me=vt(O,ue);$t(me,R),W={path:me,type:P,fileName:ue},this.log(`WA media saved: ${P} -> ${me}`)}}catch(C){this.log(`WA media download failed: ${C.message}`)}let pe={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:N,username:M},group:S?{id:w,name:_||w}:void 0,text:p,media:W,replyTo:h.message?.extendedTextMessage?.contextInfo?.stanzaId,timestamp:new Date((h.messageTimestamp||0)*1e3),raw:h,resolvedAgent:F};this.handler(pe).catch(C=>{this.log(`Error handling message: ${C.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 n=(await this.sock.sendMessage(t,{text:e.text}))?.key?.id||"";return n&&this.sentMessageIds.add(n),n}catch(s){return this.log(`Send error: ${s.message}`),""}}async editMessage(e,t,s){if(!this.sock)return!1;let n=e.includes("@")?e:`${e}@s.whatsapp.net`;try{return await this.sock.sendMessage(n,{text:s,edit:{remoteJid:n,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,s="\u{1F440}"){if(!this.sock)return;let n=e.includes("@")?e:`${e}@s.whatsapp.net`;try{await this.sock.sendMessage(n,{react:{text:s,key:{remoteJid:n,id:t}}})}catch{}}};at();import{createServer as xt}from"http";import{writeFileSync as _t,existsSync as it,unlinkSync as Rt,mkdirSync as Ot,watch as Dt}from"fs";import{resolve as Re,dirname as jt}from"path";var Z=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:s}=e;this.client=new t({intents:[s.Guilds,s.GuildMessages,s.MessageContent,s.DirectMessages]}),this.client.on("ready",()=>{this.log(`Discord connected as ${this.client.user?.tag}`)}),this.client.on("messageCreate",async n=>{if(!this.handler||n.author.bot)return;let i=n.mentions.users.has(this.client.user?.id),r=!n.guild;if(!i&&!r)return;let c=n.content;if(this.client.user&&(c=c.replace(new RegExp(`<@!?${this.client.user.id}>`,"g"),"").trim()),!c)return;let o={id:n.id,channel:"discord",accountId:"default",sender:{id:n.author.id,name:n.author.displayName||n.author.username,username:n.author.username},group:n.guild?{id:n.channelId,name:n.channel?.name||n.channelId}:void 0,text:c,replyTo:n.reference?.messageId,timestamp:n.createdAt,raw:n};this.handler(o).catch(a=>{this.log(`Error handling message: ${a.message}`)})});try{await this.client.login(this.token)}catch(n){this.log(`Discord login failed: ${n.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,s){if(!this.client)return!1;try{let n=await this.client.channels.fetch(e);return n?.isTextBased()?(await(await n.messages.fetch(t)).edit(s),!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,s="\u{1F440}"){if(this.client)try{let n=await this.client.channels.fetch(e);if(!n?.isTextBased())return;await(await n.messages.fetch(t)).react(s)}catch{}}};var ee=class{name="slack";botToken;appToken;agentBinding;handler;socket=null;web=null;botUserId;log;constructor(e,t=console.error.bind(console,"[slack]")){this.botToken=e.botToken,this.appToken=e.appToken,this.agentBinding=e.agentBinding,this.log=t}onMessage(e){this.handler=e}async start(){let e,t;try{e=await import("@slack/socket-mode"),t=await import("@slack/web-api")}catch{this.log("Slack requires @slack/socket-mode and @slack/web-api. Install with:"),this.log(" npm install @slack/socket-mode @slack/web-api");return}let{SocketModeClient:s}=e,{WebClient:n}=t;this.web=new n(this.botToken);try{let r=await this.web.auth.test();this.botUserId=r.user_id,this.log(`Slack connected as @${r.user} (${this.botUserId}) in team ${r.team}`)}catch(r){this.log(`Slack auth.test failed: ${r.message}`);return}this.socket=new s({appToken:this.appToken});let i=async(r,c)=>{try{await c()}catch{}if(!this.handler)return;let o=r.event;if(!o||typeof o.text!="string"||o.bot_id||o.user===this.botUserId||o.subtype==="bot_message")return;let a=o.channel_type==="im",l=this.botUserId&&o.text.includes(`<@${this.botUserId}>`);if(!a&&!l)return;let h=o.text;if(this.botUserId&&(h=h.replace(new RegExp(`<@${this.botUserId}>`,"g"),"").trim()),!h)return;let g=o.user||"slack-user";try{let p=await this.web.users.info({user:o.user});g=p?.user?.profile?.display_name||p?.user?.real_name||g}catch{}let m={id:o.client_msg_id||o.ts,channel:"slack",accountId:"default",sender:{id:o.user,name:g,username:g},group:a?void 0:{id:o.channel,name:o.channel},text:h,replyTo:o.thread_ts&&o.thread_ts!==o.ts?o.thread_ts:void 0,timestamp:o.ts?new Date(parseFloat(o.ts)*1e3):new Date,raw:o};try{await this.handler(m)}catch(p){this.log(`Error handling message: ${p.message}`)}};this.socket.on("app_mention",i),this.socket.on("message",i),this.socket.on("error",r=>this.log(`Slack socket error: ${r?.message||r}`));try{await this.socket.start()}catch(r){this.log(`Slack socket start failed: ${r.message}`)}}async stop(){if(this.socket){try{await this.socket.disconnect()}catch{}this.socket=null}this.web=null}async send(e){if(!this.web)return"";try{return(await this.web.chat.postMessage({channel:e.chatId,text:e.text,thread_ts:e.replyTo,mrkdwn:e.parseMode!=="plain"})).ts}catch(t){return this.log(`Send error: ${t.message}`),""}}async editMessage(e,t,s){if(!this.web)return!1;try{return await this.web.chat.update({channel:e,ts:t,text:s}),!0}catch(n){return this.log(`Edit error: ${n.message}`),!1}}async react(e,t,s="eyes"){if(this.web)try{let n=s.replace(/^:|:$/g,"");await this.web.reactions.add({channel:e,timestamp:t,name:n})}catch{}}};import{createServer as St}from"http";var te=class{name="gitlab";config;handler;server;botUsername;botUsernames=new Set;sentNoteIds=new Set;usernameToAgent=new Map;agentToUsername=new Map;log;hooks;reactForwarder;sendNoteForwarder;logTimeForwarder;constructor(e,t=console.error.bind(console,"[gitlab]"),s){this.config=e,this.log=t,this.hooks=s}setReactForwarder(e){this.reactForwarder=e}setSendNoteForwarder(e){this.sendNoteForwarder=e}setLogTimeForwarder(e){this.logTimeForwarder=e}onMessage(e){this.handler=e}async start(){let e=[];this.config.token&&e.push({label:"global",token:this.config.token});for(let s of this.config.agentMappings||[])s.token&&e.push({label:s.agentId,token:s.token});let t=await Promise.allSettled(e.map(async({label:s,token:n})=>{let i=await fetch(`${this.config.host}/api/v4/user`,{headers:{"PRIVATE-TOKEN":n}});if(!i.ok)throw new Error(`${i.status}`);let r=await i.json();return{label:s,username:r.username}}));for(let s of t)if(s.status==="fulfilled"){let{label:n,username:i}=s.value;this.botUsernames.add(i),n==="global"?(this.botUsername=i,this.log(`Global bot user: ${i}`)):(this.usernameToAgent.set(i.toLowerCase(),n),this.agentToUsername.set(n,i.toLowerCase()),this.log(`Agent "${n}" -> GitLab user @${i}`))}for(let s of this.config.agentMappings||[]){for(let n of s.gitlabUsernames)this.botUsernames.add(n),(!this.usernameToAgent.has(n.toLowerCase())||s.node)&&this.usernameToAgent.set(n.toLowerCase(),s.agentId);!this.agentToUsername.has(s.agentId)&&s.gitlabUsernames.length>0&&this.agentToUsername.set(s.agentId,s.gitlabUsernames[0].toLowerCase())}this.log(`Bot users (${this.botUsernames.size}): ${[...this.botUsernames].join(", ")}`),this.log(`Username->Agent map: ${[...this.usernameToAgent.entries()].map(([s,n])=>`@${s}->${n}`).join(", ")}`),this.server=St(async(s,n)=>{s.method==="POST"?await this.handleWebhook(s,n):(n.writeHead(200,{"Content-Type":"text/plain"}),n.end("GitLab webhook endpoint. POST events here."))}),this.server.on("error",s=>{s.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: ${s.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 s=t.pop(),n=t.pop(),i=t.join(":"),r=encodeURIComponent(i),c;switch(n){case"issue":c=`${this.config.host}/api/v4/projects/${r}/issues/${s}/notes`;break;case"merge_request":c=`${this.config.host}/api/v4/projects/${r}/merge_requests/${s}/notes`;break;default:return this.log(`Unsupported noteable type: ${n}`),""}let o=this.config.agentMappings?.find(h=>h.agentId===e.agentId),a=this.getAgentToken(e.agentId);if(!a&&o?.node&&this.sendNoteForwarder)try{let h=`${e.text}
39
-
40
- <!-- agentx:${e.agentId||"unknown"} -->`,g=await this.sendNoteForwarder(o.node,i,n,s,e.agentId||"",h);return g&&this.sentNoteIds.add(g),g}catch(h){return this.log(`GitLab send forward to "${o.node}" failed: ${h.message} \u2014 skipping (would post as group bot)`),""}let l=a||this.config.token;j.webhook("gitlab","send",`agentId="${e.agentId}" token=${a?"per-agent":"GLOBAL("+this.botUsername+")"}`);try{let h=await fetch(c,{method:"POST",headers:{"Content-Type":"application/json","PRIVATE-TOKEN":l},body:JSON.stringify({body:`${e.text}
41
-
42
- <!-- agentx:${e.agentId||"unknown"} -->`})});if(!h.ok){let p=await h.text();return this.log(`GitLab API error: ${h.status} ${p}`),""}let g=await h.json(),m=String(g.id||"");return m&&this.sentNoteIds.add(m),m}catch(h){return this.log(`GitLab send error: ${h.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 n=await this.readBody(e),i=n.object_kind||n.event_type||"unknown";switch(this.log(`Event: ${i} from ${n.project?.path_with_namespace||"unknown"}`),i){case"note":await this.handleNote(n,t);break;case"issue":await this.handleIssue(n,t);break;case"merge_request":await this.handleMR(n,t);break;case"pipeline":await this.handlePipeline(n,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 s=e.object_attributes.note,n=e.project.path_with_namespace,i=e.user,r=String(e.object_attributes.id),c=s.match(/<!-- agentx:(\S+) -->/);if(c){let w=c[1],S=s.match(/@(\w[\w.-]*)/g)?.map(T=>T.slice(1).replace(/[.]+$/,""))||[];if(!S.some(T=>{let M=this.usernameToAgent.get(T.toLowerCase());return M&&M!==w})){this.log(`AgentX comment from ${w}, no cross-agent mention, skipping (note ${r})`),t.writeHead(200),t.end("ok");return}this.log(`Bot-to-bot handoff: ${w} \u2192 ${S.join(", ")} (note ${r})`)}if(this.sentNoteIds.has(r)){this.sentNoteIds.delete(r),t.writeHead(200),t.end("ok");return}let o=s.match(/@(\w[\w.-]*)/g)?.map(w=>w.slice(1).replace(/[.]+$/,""))||[];if(o.length===0){this.log(`No @mention in note ${r}, skipping`),t.writeHead(200),t.end("ok");return}if(this.isBotUser(i.username)&&!c){this.log(`Bot comment from ${i.username} without signature, skipping (note ${r})`),t.writeHead(200),t.end("ok");return}let a="",l="",h="";e.issue?(a="issue",l=String(e.issue.iid),h=e.issue.title):e.merge_request&&(a="merge_request",l=String(e.merge_request.iid),h=e.merge_request.title),j.webhook("gitlab","handleNote",`noteId=${r} mentions=[${o.join(",")}] user=${i.username}`);let g=this.resolveAgentFromMention(s);if(j.webhook("gitlab","resolve",`agentId=${g??"NONE"}`),!g){this.log(`[handleNote] @mentions in note ${r} are not agents (${o.join(", ")}), skipping`),t.writeHead(200),t.end("ok");return}let m=g,p=this.config.agentMappings?.find(w=>w.agentId===m),f=p?.token,u=p?.node;j.webhook("gitlab","token",`agent="${m}" mapping=${p?"found":"MISSING"} hasToken=${!!f} node=${u??"local"}`),this.reactToNote(n,a,l,e.object_attributes.id,f,u,m).catch(()=>{});let y=`${n}:${a}:${l}`,b=await this.getChannelMeta(y),$=s.replace(/\n*<!-- agentx:\S+ -->/g,""),v=await this.downloadNoteImages($,n,f||this.config.token),k={id:String(e.object_attributes.id),channel:"gitlab",accountId:"default",sender:{id:y,name:i.name,username:i.username},text:`[GitLab ${a} #${l}: ${h}]
43
- ${i.name} commented:
44
- ${$}`,timestamp:new Date,raw:e,resolvedAgent:m,preferNode:p?.node,channelMeta:b?{...b,issue:{type:a,iid:l,title:h}}:void 0,media:v};this.handler(k).catch(w=>{this.log(`Error handling note: ${w.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 s=e.object_attributes,n=e.project.path_with_namespace,i=this.resolveAgent(n),r,c=!1;if(this.hooks?.has("on:gitlab-issue"))try{let a=await this.hooks.execute("on:gitlab-issue",{event:"on:gitlab-issue",issueEvent:e,project:n,iid:s.iid,title:s.title,description:s.description||"",url:s.url,action:s.action,usernameToAgent:Object.fromEntries(this.usernameToAgent.entries()),agentMappings:this.config.agentMappings||[],defaultAgentId:i||null});a.blocked&&(c=!0);let l=a.modified?.dispatch;Array.isArray(l)&&l.length>0&&(r=l.filter(h=>h&&typeof h.agentId=="string"))}catch(a){this.log(`on:gitlab-issue hook error: ${a.message}`)}if(r&&r.length>0){for(let a of r){let l=this.config.agentMappings?.find(f=>f.agentId===a.agentId),h=`${n}:issue:${s.iid}`,g=await this.getChannelMeta(h),p={id:`issue-hook-${s.iid}-${a.agentId}${a.assignee?`-${a.assignee}`:""}-${s.action}`,channel:"gitlab",accountId:"default",sender:{id:h,name:e.user.name,username:e.user.username},text:a.prompt||`[GitLab Issue #${s.iid} ${s.action}]: ${s.title}
45
- ${s.description?.slice(0,500)||""}
46
- URL: ${s.url}`,timestamp:new Date,raw:e,resolvedAgent:a.agentId,preferNode:a.preferNode||l?.node,channelMeta:g?{...g,issue:{type:"issue",iid:String(s.iid),title:s.title}}:void 0};this.log(`Issue #${s.iid} hook dispatch -> agent "${a.agentId}"${a.preferNode||l?.node?` (remote: ${a.preferNode||l?.node})`:""}`),this.handler(p).catch(f=>this.log(`Error handling hook dispatch: ${f.message}`))}t.writeHead(200),t.end("ok");return}if(c){this.log(`Issue #${s.iid}: on:gitlab-issue hook suppressed default dispatch`),t.writeHead(200),t.end("ok");return}let o={id:`issue-${s.iid}-${s.action}`,channel:"gitlab",accountId:"default",sender:{id:`${n}:issue:${s.iid}`,name:e.user.name,username:e.user.username},text:`[GitLab Issue #${s.iid} ${s.action}]: ${s.title}
47
- ${s.description?.slice(0,500)||""}
48
- URL: ${s.url}`,timestamp:new Date,raw:e,resolvedAgent:i};this.handler(o).catch(a=>this.log(`Error handling issue: ${a.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 s=e.object_attributes,n=e.project.path_with_namespace,i=this.resolveAgent(n),r={id:`mr-${s.iid}-${s.action}`,channel:"gitlab",accountId:"default",sender:{id:`${n}:merge_request:${s.iid}`,name:e.user.name,username:e.user.username},text:`[GitLab MR !${s.iid} ${s.action}]: ${s.title}
49
- Branch: ${s.source_branch} -> ${s.target_branch}
50
- ${s.description?.slice(0,500)||""}
51
- URL: ${s.url}`,timestamp:new Date,raw:e,resolvedAgent:i};this.handler(r).catch(c=>this.log(`Error handling MR: ${c.message}`)),t.writeHead(200),t.end("ok")}async handlePipeline(e,t){let s=e.object_attributes,n=e.project.path_with_namespace;if(["success","failed","canceled"].includes(s.status)&&this.hooks?.has("on:gitlab-pipeline")&&this.hooks.execute("on:gitlab-pipeline",{event:"on:gitlab-pipeline",pipelineId:s.id,status:s.status,ref:s.ref,duration:s.duration,project:n,projectId:e.project?.id,raw:e}).catch(o=>this.log(`on:gitlab-pipeline hook error: ${o.message}`)),!this.handler||s.status!=="failed"){t.writeHead(200),t.end("ok");return}let r=this.resolveAgent(n),c={id:`pipeline-${s.id}`,channel:"gitlab",accountId:"default",sender:{id:`${n}:pipeline:${s.id}`,name:e.user.name,username:e.user.username},text:`[GitLab Pipeline FAILED] Project: ${n}
52
- Ref: ${s.ref}
53
- Duration: ${s.duration}s
54
- Please investigate the failure.`,timestamp:new Date,raw:e,resolvedAgent:r};this.handler(c).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(s=>s.slice(1).replace(/[.]+$/,"").toLowerCase())||[];if(t.length!==0)for(let s of t){let n=this.usernameToAgent.get(s);if(n)return this.log(`Mention @${s} -> agent "${n}" (resolved from token)`),n}}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(":"),s=t[0],n=t[1],i=t[2],r=[];for(let o of this.config.routes)(o.project===s||o.project==="*")&&r.push({id:o.agent,name:o.agent});for(let o of this.config.agentMappings||[])r.some(a=>a.id===o.agentId)||r.push({id:o.agentId,name:o.agentId});return{channel:"gitlab",agents:r,project:s,issue:n&&i?{type:n,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(s=>s.agentId===e)?.token}getTokenForMentionedAgent(e){if(!this.config.agentMappings?.length)return;let t=e.match(/@(\w[\w.-]*)/g)?.map(s=>s.slice(1).replace(/[.]+$/,"").toLowerCase())||[];for(let s of this.config.agentMappings)if(s.token&&s.gitlabUsernames.some(n=>t.includes(n.toLowerCase())))return s.token}async reactToNote(e,t,s,n,i,r,c){if(!i&&r&&c&&this.reactForwarder){this.log(`Forwarding \u{1F440} reaction for "${c}" to mesh peer "${r}"`),await this.reactForwarder(r,e,t,s,n,c);return}if(!i){this.log(`No token for reaction on note ${n} (agent "${c}") \u2014 skipping`);return}let o=encodeURIComponent(e),a;switch(t){case"issue":a=`${this.config.host}/api/v4/projects/${o}/issues/${s}/notes/${n}/award_emoji`;break;case"merge_request":a=`${this.config.host}/api/v4/projects/${o}/merge_requests/${s}/notes/${n}/award_emoji`;break;default:return}try{let l=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json","PRIVATE-TOKEN":i},body:JSON.stringify({name:"eyes"})});l.ok||this.log(`Reaction failed on note ${n}: ${l.status}`)}catch(l){this.log(`Failed to react to note ${n}: ${l.message}`)}}async logTimeSpent(e,t,s){let n=e.split(":");if(n.length<3)return;let i=n.pop(),r=n.pop(),c=n.join(":"),o=Math.max(60,Math.round(t/1e3)),a=Math.floor(o/3600),l=Math.ceil(o%3600/60),h=a>0?`${a}h${l>0?` ${l}m`:""}`:`${l}m`,g=encodeURIComponent(c),m=r==="merge_request"?"merge_requests":"issues",p=`${this.config.host}/api/v4/projects/${g}/${m}/${i}/add_spent_time`,f=this.config.agentMappings?.find(b=>b.agentId===s),u=this.getAgentToken(s);if(!u&&f?.node&&this.logTimeForwarder){try{await this.logTimeForwarder(f.node,c,r,i,s||"",t),this.log(`Time logged (via peer "${f.node}"): ${h} on ${c} ${m}/${i} (${s})`)}catch(b){this.log(`Time log forward to "${f.node}" failed: ${b.message} \u2014 skipping (would log as group bot)`)}return}let y=u||this.config.token;try{let b=await fetch(p,{method:"POST",headers:{"Content-Type":"application/json","PRIVATE-TOKEN":y},body:JSON.stringify({duration:h})});if(b.ok)this.log(`Time logged: ${h} on ${c} ${r} #${i} (${s||"global"})`);else{let $=await b.text();this.log(`Time log failed (${b.status}): ${$.slice(0,100)}`)}}catch(b){this.log(`Time log error: ${b.message}`)}}async downloadNoteImages(e,t,s){let n=/!\[[^\]]*\]\(([^)]+\.(?:png|jpg|jpeg|gif|webp|svg))\)/gi,i=[...e.matchAll(n)];if(i.length===0)return;let r=i[0][1],c;if(r.startsWith("http"))c=r;else{let o=encodeURIComponent(t);c=`${this.config.host}/${t}${r}`}try{let o=await fetch(c,{headers:{"PRIVATE-TOKEN":s}});if(!o.ok){this.log(`Failed to download image: ${o.status} ${c}`);return}let a=Buffer.from(await o.arrayBuffer()),l=o.headers.get("content-type")||"image/png",h=l.split("/")[1]?.split(";")[0]||"png",{mkdirSync:g,writeFileSync:m}=await import("fs"),{resolve:p}=await import("path"),{randomUUID:f}=await import("crypto"),u=p(process.cwd(),".agentx/media/gitlab");g(u,{recursive:!0});let y=`${f().slice(0,8)}.${h}`,b=p(u,y);return m(b,a),this.log(`Downloaded GitLab image: ${b} (${a.length} bytes)`),{path:b,type:l,fileName:r.split("/").pop()||y}}catch(o){this.log(`Image download error: ${o.message}`);return}}async readBody(e){return new Promise(t=>{let s="";e.on("data",n=>s+=n.toString()),e.on("end",()=>{try{t(s?JSON.parse(s):{})}catch{t({raw:s})}}),e.on("error",()=>t({}))})}};var U=class{module;minLevel;constructor(e,t="info"){this.module=e,this.minLevel=t}child(e){return new U(`${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 s=e.map(n=>typeof n=="string"?n:JSON.stringify(n)).join(" ").replace(/^\[agentx\]\s*/,"");s&&this.info(s)}}emit(e,t,s){if(U.levelOrder[e]<U.levelOrder[this.minLevel])return;let n={time:new Date().toISOString(),level:e,module:this.module,msg:t,...s},i=e==="error"?"ERROR":e==="warn"?"WARN":"",r=`[${this.module}]`,c=i?`${r} ${i}: ${t}`:`${r} ${t}`;console.error(c)}},H=U;J(H,"levelOrder",{debug:0,info:1,warn:2,error:3});var se=class{registry;config;log;constructor(e,t={},s=console.error.bind(console,"[webhook]")){this.registry=e,this.config=t,this.log=s}async handle(e,t,s){let n=s.replace(/^\/webhook\/?/,"").split("/").filter(Boolean),i=n[0],r=n[1];if(!i){this.sendJson(t,400,{error:"Missing agent ID. Use /webhook/:agentId"});return}let c=await this.readBody(e),o=r||this.detectSource(e.headers),a=this.buildSummary(o,c,e.headers);this.log(`Webhook [${o}] -> ${i}: ${a.slice(0,100)}`);try{let l=await this.registry.execute({message:a,agentId:i,context:{channel:`webhook:${o}`,sender:`webhook:${o}`}});this.sendJson(t,l.error?500:200,{ok:!l.error,agent:i,source:o,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,s){let n=[`[Webhook from ${e}]`];switch(e){case"gitlab":{let r=s["x-gitlab-event"]||t.object_kind||"event",c=t.project?.path_with_namespace||"",o=t.user?.name||t.user_username||"";if(n.push(`Event: ${r}`),c&&n.push(`Project: ${c}`),o&&n.push(`User: ${o}`),t.ref&&n.push(`Ref: ${t.ref}`),t.commits&&Array.isArray(t.commits)){n.push(`Commits: ${t.commits.length}`);for(let l of t.commits.slice(0,3))n.push(` - ${l.message?.split(`
55
- `)[0]||"no message"} (${l.author?.name||""})`)}let a=t.object_attributes;if(a?.title&&(n.push(`Title: ${a.title}`),n.push(`State: ${a.state||""}`),n.push(`Action: ${a.action||""}`),a.source_branch&&n.push(`Branch: ${a.source_branch} -> ${a.target_branch}`),a.url&&n.push(`URL: ${a.url}`)),a?.iid&&!a?.source_branch&&(n.push(`Issue #${a.iid}: ${a.title||""}`),a.description&&n.push(`Description: ${a.description.slice(0,200)}`)),t.object_kind==="pipeline"){let l=t.object_attributes;n.push(`Pipeline: ${l?.status||""} (${l?.ref||""})`),n.push(`Duration: ${l?.duration||0}s`)}break}case"github":{let r=s["x-github-event"]||"event",c=t.repository?.full_name||"",o=t.sender?.login||"";if(n.push(`Event: ${r}`),c&&n.push(`Repository: ${c}`),o&&n.push(`Sender: ${o}`),t.ref&&n.push(`Ref: ${t.ref}`),t.commits&&Array.isArray(t.commits))for(let h of t.commits.slice(0,3))n.push(` - ${h.message?.split(`
56
- `)[0]||""} (${h.author?.name||""})`);let a=t.pull_request;a&&(n.push(`PR #${a.number}: ${a.title}`),n.push(`Action: ${t.action}`),n.push(`Branch: ${a.head?.ref} -> ${a.base?.ref}`));let l=t.issue;l&&(n.push(`Issue #${l.number}: ${l.title}`),n.push(`Action: ${t.action}`));break}case"stripe":{let r=t.type||"event",c=t.data?.object||{};n.push(`Event: ${r}`),c.amount&&n.push(`Amount: ${(c.amount/100).toFixed(2)} ${c.currency?.toUpperCase()||""}`),c.customer_email&&n.push(`Customer: ${c.customer_email}`),c.description&&n.push(`Description: ${c.description}`),c.status&&n.push(`Status: ${c.status}`);break}case"sentry":{let r=s["sentry-hook-resource"]||"event";n.push(`Resource: ${r}`);let c=t.data||t;c.error?.title&&n.push(`Error: ${c.error.title}`),c.error?.culprit&&n.push(`Culprit: ${c.error.culprit}`),c.error?.metadata?.value&&n.push(`Message: ${c.error.metadata.value}`),t.url&&n.push(`URL: ${t.url}`);break}default:n.push(`Headers: ${JSON.stringify(Object.keys(s).filter(r=>r.startsWith("x-")).slice(0,5))}`),n.push(`Payload keys: ${Object.keys(t).slice(0,10).join(", ")}`);let i=JSON.stringify(t).slice(0,500);n.push(`Body: ${i}`)}return n.join(`
57
- `)}async readBody(e){return new Promise(t=>{let s="";e.on("data",n=>s+=n.toString()),e.on("end",()=>{try{t(s?JSON.parse(s):{})}catch{t({raw:s})}}),e.on("error",()=>t({}))})}sendJson(e,t,s){e.writeHead(t,{"Content-Type":"application/json"}),e.end(JSON.stringify(s,null,2))}};var ne=class{cache=new Map;config;meshPeers=[];constructor(e){this.config=e}fingerprint="";build(e){e&&(this.meshPeers=e),this.cache.clear(),this.fingerprint=this.computeFingerprint();for(let t of Object.keys(this.config.agents))this.cache.set(t,this.renderForAgent(t))}computeFingerprint(){return[...this.meshPeers].sort((t,s)=>t.peer.localeCompare(s.peer)).map(t=>`${t.peer}:${t.skills.map(s=>s.id).sort().join(",")}`).join("|")}getForAgent(e){return this.cache.get(e)}refresh(e){this.meshPeers=e,!(this.computeFingerprint()===this.fingerprint&&this.cache.size>0)&&this.build()}renderForAgent(e){let t=["[Landscape]"],s=this.config.agents[e];t.push(`Node: ${this.config.node.name} (${this.config.node.id})`);let n=this.getPrimaryHandle(e),i=this.extractCapability(s),r=n?` (${n})`:"";t.push(`You: ${s.name}${r} \u2014 ${i}`),t.push("");let c=Object.entries(this.config.agents).filter(([g])=>g!==e);t.push(`Available agents on this node (${c.length} \u2014 not all may be in the current group):`);for(let[g,m]of c){let p=this.getPrimaryHandle(g),f=this.extractCapability(m),u=p?` (${p})`:"";t.push(`\u2022 ${m.name}${u} \u2014 ${f}`)}let o=this.meshPeers.filter(g=>g.skills.length>0);if(o.length){t.push("Remote:");let g=[...o].sort((m,p)=>m.peer.localeCompare(p.peer));for(let m of g){let p=[...m.skills].sort((f,u)=>f.id.localeCompare(u.id));for(let f of p)t.push(`\u2022 ${f.name} [${m.peer}] \u2014 ${f.description.slice(0,60)}`)}}t.push(""),t.push(this.renderChannels()),t.push(""),t.push("[Rules]");let a=[n,e].filter(Boolean).join(" / ");t.push(`- Only respond when YOU (${a}) 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.push(""),t.push("[Background Monitoring]"),t.push("You can watch things in the background while continuing to work:"),t.push("- Tail log files and react to errors as they appear"),t.push("- Poll CI/CD pipelines and report when status changes"),t.push("- Watch directories for file changes"),t.push("Use the Monitor tool for this \u2014 it runs a script in the background and streams output to you."),t.push(""),t.push("[Agent Teams]"),t.push("For complex tasks that benefit from parallel work, you can spawn an agent team:"),t.push("- Tell Claude Code to 'create an agent team' with specialized teammates"),t.push("- Each teammate works independently with its own context window"),t.push("- Teammates coordinate via shared task list and direct messaging"),t.push("- Best for: code review (security + performance + tests), debugging with competing hypotheses, multi-module features"),t.push("- Use teams when work can be parallelized. Use single session for sequential/simple tasks."),t.join(`
58
- `)}renderChannels(){let e=[],t=this.config.channels;if(t.telegram.enabled){let s=t.telegram.policy;e.push(`telegram(groups:${s.group}, DMs:${s.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(s=>s.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 ie=class{registry;memoryStore;states=new Map;log;constructor(e,t=console.error.bind(console,"[heartbeat]"),s){this.registry=e,this.log=t,this.memoryStore=s}register(e,t){if(!t.enabled)return;this.stop(e);let s={agentId:e,config:t,runCount:0,errors:0},n=(t.intervalMinutes||30)*60*1e3;s.timer=setInterval(()=>{this.runHeartbeat(s).catch(i=>{this.log(`[${e}] heartbeat error: ${i.message}`),s.errors++})},n),this.states.set(e,s),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:s}=e;this.log(`[${t}] heartbeat running...`),e.lastRun=new Date,e.runCount++;let n=s.channel||"heartbeat";try{let i=s.prompt;if(this.memoryStore){let c=this.memoryStore.buildRecallContext(t);c&&(i=`${c}
59
-
60
- ${i}`)}let r=await this.registry.execute({message:i,agentId:t,context:{channel:n,sender:"system:heartbeat",chatId:`heartbeat:${t}`}});r.error?(this.log(`[${t}] heartbeat error: ${r.error}`),e.errors++):this.log(`[${t}] heartbeat completed (${r.duration}ms, ${r.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 re=class{services=[];log;constructor(e,t=console.error.bind(console,"[services]")){this.log=t;for(let[s,n]of Object.entries(e)){let i={def:{...n,id:s},patterns:[]};for(let r of n.triggers)try{i.patterns.push({regex:new RegExp(r.pattern,"i"),channel:r.channel})}catch(c){this.log(`Invalid regex in service "${s}": ${r.pattern} \u2014 ${c.message}`)}i.patterns.length>0&&this.services.push(i)}this.log(`${this.services.length} service(s) loaded`)}match(e,t,s){for(let n of this.services){if(n.def.allowedContacts?.length){let i=t.replace(/[^0-9]/g,"");if(!n.def.allowedContacts.some(c=>{let o=c.replace(/[^0-9]/g,"");return i.includes(o)||o.includes(i)}))continue}for(let i of n.patterns){if(i.channel&&i.channel!==s)continue;let r=e.match(i.regex);if(r)return j.cat("agent",`Service match: "${n.def.name}" (trigger: ${i.regex.source})`),{service:n.def,trigger:i.regex.source,captured:r[0]}}}return null}async execute(e,t,s,n){this.log(`Executing service "${e.name}" -> agent "${e.agent}"`);try{let i=await t.execute({message:e.prompt,agentId:e.agent,context:{channel:s.channel,sender:s.sender,chatId:`service:${e.id}:${s.chatId}`}});if(i.error){this.log(`Service "${e.name}" failed: ${i.error}`),await n(`Service "${e.name}" encountered an error: ${i.error}`);return}i.content&&await n(i.content),this.log(`Service "${e.name}" completed in ${i.duration}ms`)}catch(i){this.log(`Service "${e.name}" threw: ${i.message}`),await n(`Service error: ${i.message}`)}}getScheduledServices(){return this.services.filter(e=>e.def.schedule).map(e=>e.def)}list(){return this.services.map(e=>({id:e.def.id,name:e.def.name,triggers:e.patterns.length,agent:e.def.agent,scheduled:!!e.def.schedule}))}};var oe=class{employees=new Map;constructor(e){for(let[t,s]of Object.entries(e.orgChart)){let n=e.roles[s.role];if(!n)throw new Error(`Org chart references unknown role "${s.role}" for agent "${t}"`);this.employees.set(t,{agentId:t,role:n,roleKey:s.role,reportsTo:s.reportsTo,schedule:s.schedule,utilizationTarget:s.utilizationTarget})}for(let t of this.employees.values())if(t.reportsTo&&!this.employees.has(t.reportsTo))throw new Error(`Agent "${t.agentId}" reports to unknown agent "${t.reportsTo}"`);for(let t of this.employees.values()){let s=new Set,n=t.agentId;for(;n;){if(s.has(n))throw new Error(`Reporting cycle detected involving "${t.agentId}"`);s.add(n),n=this.employees.get(n)?.reportsTo}}}get(e){return this.employees.get(e)}all(){return[...this.employees.values()]}reportsTo(e){return this.employees.get(e)?.reportsTo}directReports(e){return[...this.employees.values()].filter(t=>t.reportsTo===e).map(t=>t.agentId)}chainOfCommand(e){let t=[],s=e;for(;s&&!t.includes(s);)t.push(s),s=this.employees.get(s)?.reportsTo;return t}};var tt={sun:0,mon:1,tue:2,wed:3,thu:4,fri:5,sat:6};function Ae(d,e){let s=new Intl.DateTimeFormat("en-US",{timeZone:e,weekday:"short",hour:"2-digit",minute:"2-digit",hour12:!1}).formatToParts(d),n=(s.find(o=>o.type==="weekday")?.value||"sun").toLowerCase().slice(0,3),i=s.find(o=>o.type==="hour")?.value||"00",r=s.find(o=>o.type==="minute")?.value||"00";return{hour:parseInt(i,10)%24,minute:parseInt(r,10),dow:tt[n]??0}}function L(d){let[e,t]=d.split(":").map(Number);return e*60+t}function Ce(d,e){return d.days.some(t=>tt[t]===e)}function Mt(d,e,t){let{hour:s,minute:n,dow:i}=Ae(e,t);if(!Ce(d,i))return!1;let r=s*60+n,c=L(d.start),o=L(d.end);if(r<c||r>=o)return!1;if(d.lunch){let a=L(d.lunch.start),l=L(d.lunch.end);if(r>=a&&r<l)return!1}return!0}var ae=class{constructor(e,t){this.org=e;this.config=t}isOnClock(e,t=new Date){let s=this.org.get(e);return s?Mt(s.schedule,t,this.config.timezone):!1}isDayStart(e,t=new Date){let s=this.org.get(e);if(!s)return!1;let{hour:n,minute:i,dow:r}=Ae(t,this.config.timezone);if(!Ce(s.schedule,r))return!1;let c=L(s.schedule.start);return n*60+i===c}isDayEnd(e,t=new Date){let s=this.org.get(e);if(!s)return!1;let{hour:n,minute:i,dow:r}=Ae(t,this.config.timezone);if(!Ce(s.schedule,r))return!1;let c=L(s.schedule.end);return n*60+i===c}clockedInAgents(e=new Date){return this.org.all().filter(t=>this.isOnClock(t.agentId,e)).map(t=>t.agentId)}};var ce=class{constructor(e,t,s,n){this.router=e;this.org=t;this.config=s;this.log=n}async postToMain(e,t){let s=this.config.mainChannel;try{await this.router.sendOutbound({channel:s.channel,chatId:s.chatId,text:e,agentId:t,accountId:s.accountId})}catch(n){this.log(`[business] postToMain failed: ${n.message}`)}}async escalate(e,t,s){let n=this.org.chainOfCommand(e).slice(1),i=n.length?n.slice(0,1):[],r=`\u26A0\uFE0F **Escalation from ${e}** \u2014 ${t}
61
-
62
- ${s}`;if(!i.length){await this.postToMain(r,e);return}await this.postToMain(`${r}
63
-
64
- cc: ${i.map(c=>`@${c}`).join(" ")}`,e)}};import{writeFileSync as It,existsSync as st,mkdirSync as At,readFileSync as Ct}from"fs";import{resolve as Pe}from"path";function xe(){return new Date().toISOString().slice(0,10)}var le=class{constructor(e,t,s){this.org=e;this.schedule=t;this.log=s;this.dir=Pe(process.cwd(),".agentx/kpi"),st(this.dir)||At(this.dir,{recursive:!0}),this.stats=this.loadOrInit()}stats;dir;onClockTick=new Map;loadOrInit(){let e=Pe(this.dir,`${xe()}.json`);if(st(e))try{return JSON.parse(Ct(e,"utf-8"))}catch{}let t={};for(let s of this.org.all())t[s.agentId]={tasksCompleted:0,timeLoggedSeconds:0,onClockSeconds:0,blockers:0,utilization:0};return{date:xe(),perAgent:t}}tick(){let e=Date.now();for(let t of this.org.all()){if(!this.schedule.isOnClock(t.agentId)){this.onClockTick.delete(t.agentId);continue}let s=this.onClockTick.get(t.agentId);if(s){let n=Math.min(e-s,12e4)/1e3,i=this.stats.perAgent[t.agentId];i&&(i.onClockSeconds+=n)}this.onClockTick.set(t.agentId,e)}this.recomputeUtil(),this.persist()}recordTaskCompletion(e){let t=this.stats.perAgent[e.agentId];t&&(t.tasksCompleted+=1,t.timeLoggedSeconds+=e.durationSeconds,this.recomputeUtil(),this.persist())}recordBlocker(e){let t=this.stats.perAgent[e];t&&(t.blockers+=1,this.persist())}recomputeUtil(){for(let e of Object.values(this.stats.perAgent))e.utilization=e.onClockSeconds>0?Math.min(1,e.timeLoggedSeconds/e.onClockSeconds):0}persist(){this.stats.date!==xe()&&(this.flush(),this.stats=this.loadOrInit());try{It(Pe(this.dir,`${this.stats.date}.json`),JSON.stringify(this.stats,null,2))}catch(e){this.log(`[business] KPI persist failed: ${e.message}`)}}flush(){this.persist()}snapshot(){return JSON.parse(JSON.stringify(this.stats))}dailySummaryText(){let e=[`\u{1F4CA} **Daily report \u2014 ${this.stats.date}**`,""],t=0,s=0;for(let[i,r]of Object.entries(this.stats.perAgent)){t+=r.timeLoggedSeconds,s+=r.onClockSeconds;let c=(r.timeLoggedSeconds/3600).toFixed(1),o=(r.utilization*100).toFixed(0);e.push(`\u2022 **${i}** \u2014 ${r.tasksCompleted} tasks, ${c}h logged, ${o}% utilization${r.blockers?`, ${r.blockers} blocker(s)`:""}`)}e.push("");let n=s>0?(t/s*100).toFixed(0):"0";return e.push(`**Team:** ${(t/3600).toFixed(1)}h logged / ${(s/3600).toFixed(1)}h on-clock \u2192 ${n}% utilization`),e.join(`
65
- `)}};function I(d,e,t){d.statusCode=e,d.setHeader("Content-Type","application/json"),d.end(JSON.stringify(t))}async function he(d){return new Promise((e,t)=>{let s=[];d.on("data",n=>s.push(n)),d.on("end",()=>{let n=Buffer.concat(s).toString("utf-8");if(!n)return e({});try{e(JSON.parse(n))}catch(i){t(i)}}),d.on("error",t)})}async function nt(d,e,t,s){if(d==="GET /business/status"){let n=new Date,i=s.schedule.clockedInAgents(n);return I(t,200,{time:n.toISOString(),onClock:i,employees:s.org.all().map(r=>({agentId:r.agentId,role:r.role.title,reportsTo:r.reportsTo,onClock:i.includes(r.agentId)})),kpi:s.kpi.snapshot()}),!0}if(d.startsWith("GET /business/work")){let i=new URL(e.url||"/","http://x").searchParams.get("agent");if(!i)return I(t,400,{error:"?agent= required"}),!0;try{let r=await s.workSource.listOpen(i);I(t,200,{items:r})}catch(r){I(t,500,{error:r.message})}return!0}if(d==="POST /business/claim"){let n=await he(e),{agent:i,itemId:r}=n;if(!i||!r)return I(t,400,{error:"agent and itemId required"}),!0;try{await s.workSource.claim(i,r),I(t,200,{ok:!0})}catch(c){I(t,500,{error:c.message})}return!0}if(d==="POST /business/report"){let n=await he(e);if(!n.itemId||!n.status)return I(t,400,{error:"itemId and status required"}),!0;if(!["in-progress","done","blocked"].includes(n.status))return I(t,400,{error:"status must be in-progress | done | blocked"}),!0;try{await s.workSource.report(n.itemId,{status:n.status,note:n.note,timeSeconds:n.timeSeconds,blocker:n.blocker}),I(t,200,{ok:!0})}catch(i){I(t,500,{error:i.message})}return!0}if(d==="POST /business/escalate"){let n=await he(e);return!n.agent||!n.subject?(I(t,400,{error:"agent and subject required"}),!0):(await s.reporter.escalate(n.agent,n.subject,n.detail||""),s.kpi.recordBlocker(n.agent),I(t,200,{ok:!0}),!0)}if(d==="POST /business/post"){let n=await he(e);return n.text?(await s.reporter.postToMain(n.text,n.agent),I(t,200,{ok:!0}),!0):(I(t,400,{error:"text required"}),!0)}return!1}function _e(d){return`## Business Layer Tools
66
-
67
- During business hours you'll receive lifecycle prompts ([STANDUP], [WORK], [WRAP]).
68
- Use these HTTP endpoints on the local daemon to act on them.
69
-
70
- - **List your work:**
71
- \`curl -s ${d}/business/work?agent=<your-id>\`
72
- - **Claim an item** (optional \u2014 some sources no-op):
73
- \`curl -s -X POST ${d}/business/claim -d '{"agent":"<your-id>","itemId":"<id>"}'\`
74
- - **Report progress or completion:**
75
- \`curl -s -X POST ${d}/business/report \\
76
- -d '{"itemId":"<id>","status":"done","timeSeconds":1800,"note":"shipped"}'\`
77
- status: \`in-progress\` | \`done\` | \`blocked\`
78
- - **Escalate a blocker** up the org chart:
79
- \`curl -s -X POST ${d}/business/escalate \\
80
- -d '{"agent":"<your-id>","subject":"short","detail":"why it's stuck"}'\`
81
- - **Post to the main channel** (daily plans, reports):
82
- \`curl -s -X POST ${d}/business/post \\
83
- -d '{"agent":"<your-id>","text":"your update"}'\`
84
-
85
- Your org chart, role, and schedule are fixed in agentx.json under \`business.orgChart\`.
86
- `}var Pt=6e4,ge=class{constructor(e,t,s,n,i,r,c,o,a){this.config=e;this.org=t;this.schedule=s;this.registry=n;this.reporter=i;this.kpi=r;this.workSource=c;this.daemonBase=o;this.log=a;for(let l of t.all())this.day.set(l.agentId,{})}timer;day=new Map;currentDate=new Date().toISOString().slice(0,10);runningTask=new Set;start(){this.log("[business] day cycle starting"),this.tick().catch(e=>this.log(`[business] tick error: ${e.message}`)),this.timer=setInterval(()=>{this.tick().catch(e=>this.log(`[business] tick error: ${e.message}`))},Pt)}stop(){this.timer&&clearInterval(this.timer),this.timer=void 0,this.kpi.flush()}async tick(){let e=new Date,t=e.toISOString().slice(0,10);if(t!==this.currentDate){this.currentDate=t;for(let s of this.day.keys())this.day.set(s,{})}this.kpi.tick();for(let s of this.org.all()){let n=this.day.get(s.agentId)||{};if(this.day.set(s.agentId,n),!this.runningTask.has(s.agentId)){if(this.schedule.isDayStart(s.agentId,e)&&!n.standupFiredAt){n.standupFiredAt=Date.now(),this.fireStandup(s.agentId).catch(i=>this.log(`[business] standup err: ${i.message}`));continue}if(this.schedule.isDayEnd(s.agentId,e)&&!n.wrapFiredAt){n.wrapFiredAt=Date.now(),this.fireWrap(s.agentId).catch(i=>this.log(`[business] wrap err: ${i.message}`));continue}this.schedule.isOnClock(s.agentId,e)&&(n.lastWorkTickAt?(Date.now()-n.lastWorkTickAt)/6e4:1/0)>=this.config.workTickMinutes&&(n.lastWorkTickAt=Date.now(),this.fireWorkTick(s.agentId).catch(r=>this.log(`[business] work-tick err: ${r.message}`)))}}}roleContext(e){let t=this.org.get(e);if(!t)return"";let s=t.reportsTo?`reporting to ${t.reportsTo}`:"(no manager \u2014 top of org chart)",n=t.role.responsibilities.length?`
87
- Responsibilities:
88
- ${t.role.responsibilities.map(r=>`- ${r}`).join(`
89
- `)}`:"",i=t.role.sopPath?`
90
- SOP: @${t.role.sopPath}`:"";return`You are ${e}, role: ${t.role.title} ${s}.${n}${i}
91
-
92
- ${_e(this.daemonBase)}`}async runLifecycle(e,t){if(this.runningTask.has(e))return;this.runningTask.add(e);let s=Date.now();try{let n=await this.registry.execute({message:t,agentId:e,context:{channel:"business",chatId:`day-cycle:${this.currentDate}:${e}`}}),i=Math.round((n.duration??Date.now()-s)/1e3);return n.error||this.kpi.recordTaskCompletion({agentId:e,durationSeconds:i,success:!0,channel:"business"}),n.content}finally{this.runningTask.delete(e)}}async fireStandup(e){this.log(`[business] STANDUP \u2192 ${e}`);let s=`${this.roleContext(e)}
93
-
94
- [STANDUP] It's the start of your work day.
95
- 1. Call GET /business/work?agent=${e} to see your open work items.
96
- 2. Pick your top 3 for today, in priority order.
97
- 3. Post your plan to the main channel via POST /business/post \u2014 keep it to 4-6 short lines.
98
- 4. Flag any blockers via POST /business/escalate.`;if(await this.runLifecycle(e,s)){let i=this.day.get(e)||{};i.hadWorkToday=!0,this.day.set(e,i)}}async fireWorkTick(e){let t=await this.workSource.listOpen(e).catch(()=>[]);if(!t.length)return;this.log(`[business] WORK-TICK \u2192 ${e} (${t.length} open)`);let n=`${this.roleContext(e)}
99
-
100
- [WORK] Continue your highest-priority open task.
101
- - Call GET /business/work?agent=${e} to see what's assigned to you.
102
- - If you complete one, POST /business/report with status:done and the time you spent.
103
- - If blocked, POST /business/escalate with a concise blocker description.
104
- - Then move on to the next item. Keep working until you hit a real stopping point \u2014 don't idle.`;if(await this.runLifecycle(e,n)){let r=this.day.get(e)||{};r.hadWorkToday=!0,this.day.set(e,r)}}async fireWrap(e){if(!(this.day.get(e)||{}).hadWorkToday){this.log(`[business] WRAP skipped for ${e} (no work done today)`);return}this.log(`[business] WRAP \u2192 ${e}`);let n=`${this.roleContext(e)}
105
-
106
- [WRAP] End of work day. Post a concise daily report to the main channel via POST /business/post:
107
- - Tasks closed today
108
- - Time logged (total)
109
- - Carry-overs to tomorrow
110
- - Any outstanding blockers
111
- Keep it under 8 lines.`;await this.runLifecycle(e,n)}};var de=class{org;schedule;workSource;reporter;kpi;dayCycle;constructor(e,t,s,n,i){this.org=new oe(e),this.schedule=new ae(this.org,e),this.workSource=Ge(e,t,i),this.reporter=new ce(n,this.org,e,i),this.kpi=new le(this.org,this.schedule,i);let[r,c]=t.node.bind.split(":"),o=`http://${r==="0.0.0.0"?"127.0.0.1":r}:${c||"18800"}`;this.dayCycle=new ge(e,this.org,this.schedule,s,this.reporter,this.kpi,this.workSource,o,i)}start(){this.dayCycle.start()}stop(){this.dayCycle.stop()}recordTaskCompletion(e,t,s,n){this.kpi.recordTaskCompletion({agentId:e,durationSeconds:t,success:s,channel:n})}async handleHttp(e,t,s){let n={workSource:this.workSource,org:this.org,schedule:this.schedule,reporter:this.reporter,kpi:this.kpi};return nt(e,t,s,n)}summary(){let e=this.schedule.clockedInAgents().length;return`Business layer: ${this.org.all().length} employees, ${e} on-clock now`}};var rt=class{config;registry;router;cron;mesh;hooks;landscape;heartbeat;business;httpServer;webhooks;log;sseClients=new Set;configPath;configWatcher;reloadTimer;constructor(e){let s=new H("agentx").asConsoleLog();this.log=(...r)=>{s(...r);let c=r.map(o=>typeof o=="string"?o:JSON.stringify(o)).join(" ");this.broadcastSSE("log",c)},this.log("Loading configuration..."),this.configPath=e,this.config=fe(e);let n=Ne(this.config);for(let r of n)this.log(` \u26A0 ${r}`);let[,i]=this.config.node.bind.split(":");Be(this.config.agents,i||"19900",this.log),this.hooks=new De,je(process.cwd(),this.hooks),this.landscape=new ne(this.config),this.registry=new Le(this.config,this.log),Ee(this.registry),this.registry.setLandscape(this.landscape),this.heartbeat=new ie(this.registry,this.log);for(let[r,c]of Object.entries(this.config.agents))c.heartbeat?.enabled&&this.heartbeat.register(r,c.heartbeat);if(this.router=new G(this.registry,this.config,this.hooks,this.log),this.webhooks=new se(this.registry,{},this.log),Object.keys(this.config.services).length>0){let r=new re(this.config.services,this.log);this.router.setServiceMatcher(r)}if(this.cron=new ye(this.config,this.registry,this.hooks,this.log),this.cron.setNotifyCallback(async(r,c,o,a)=>{let l=`Cron "${r}" failed (${a}x)
112
- Agent: ${c}
113
- Error: ${o.slice(0,300)}`;this.log(`[CRON ALERT] ${l}`),this.broadcastSSE("cron-failure",JSON.stringify({jobId:r,agent:c,error:o,consecutiveErrors:a}));let h=this.config.crons[r];if(h?.notify)try{await this.router.sendOutbound({channel:h.notify.channel,chatId:h.notify.chatId,text:`\u{1F534} **Cron "${r}" failed** (${a}x)
114
- ${o.slice(0,300)}`,agentId:c,accountId:h.notify.accountId})}catch(g){this.log(`[CRON ALERT] notify send failed: ${g.message}`)}}),this.config.mesh.enabled&&(this.mesh=new K(this.config,this.log),this.router.setMesh(this.mesh)),this.config.business?.enabled)try{this.business=new de(this.config.business,this.config,this.registry,this.router,this.log),this.router.setBusiness(this.business)}catch(r){this.log(`[business] initialization failed: ${r.message}`)}}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(),this.business&&(this.business.start(),this.log(` ${this.business.summary()}`)),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 c=r.enabled?"enabled":"disabled";this.log(` ${r.id} [${c}] \u2192 ${r.agent} (${r.schedule})`)}}if(this.mesh){this.log(""),this.log(" Mesh Peers:");for(let r of this.mesh.directory()){let c=r.healthy?"\u2713":"\u2717";this.log(` ${c} ${r.peer} (${r.peerUrl})`)}}process.env.AGENTX_AUTO_RELOAD!=="false"&&this.startConfigWatcher();try{let r=this.registry.pruneTaskHistory();r>0&&this.log(` Pruned ${r} old task-history folder(s)`)}catch{}try{let r=this.registry.hydrateLastSummariesFromDisk();r>0&&this.log(` Loaded last-activity summaries for ${r} agent(s)`)}catch{}this.log(""),this.log(" Ready."),this.log("");let s=Re(process.cwd(),".agentx/daemon.pid");Ot(jt(s),{recursive:!0}),_t(s,String(process.pid)),this.log(` PID: ${process.pid} (${s})`),process.on("uncaughtException",r=>{this.log(`UNCAUGHT EXCEPTION: ${r.message}`),this.log(r.stack||"")}),process.on("unhandledRejection",r=>{this.log(`UNHANDLED REJECTION: ${r}`)});let n=!1,i=async r=>{n||(n=!0,this.log(`
115
- 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.router.flushPersistence?.()}catch(t){this.log(` Router flush 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.business&&(this.log(" Stopping business layer..."),this.business.stop())}catch{}try{this.mesh&&(this.log(" Stopping mesh..."),await this.mesh.stop())}catch{}if(this.midnightTimer&&clearTimeout(this.midnightTimer),this.reloadTimer&&clearTimeout(this.reloadTimer),this.configWatcher)try{this.configWatcher.close()}catch{}this.httpServer&&this.httpServer.close();try{let t=Re(process.cwd(),".agentx/daemon.pid");it(t)&&Rt(t)}catch{}this.log(` Shutdown complete (${Date.now()-e}ms)`),process.exit(0)}midnightTimer;startConfigWatcher(){let e=this.configPath||Re(process.cwd(),"agentx.json");if(it(e))try{this.configWatcher=Dt(e,{persistent:!1},t=>{t==="change"&&(this.reloadTimer&&clearTimeout(this.reloadTimer),this.reloadTimer=setTimeout(()=>{this.reload().catch(s=>this.log(`[reload] failed: ${s?.message||s}`))},500))}),this.log(` Watching ${e} for config changes`)}catch(t){this.log(` Config watcher failed to start: ${t.message}`)}}async reload(){let e;try{e=fe(this.configPath)}catch(i){return this.log(`[reload] config invalid, keeping previous: ${i.message}`),{applied:[],restartRequired:[],error:i.message}}let t=[],s=[];if(JSON.stringify(this.config.crons)!==JSON.stringify(e.crons))try{await this.cron.stop(),this.cron=new ye(e,this.registry,this.hooks,this.log),this.cron.setNotifyCallback(async(i,r,c,o)=>{this.log(`[CRON ALERT] Cron "${i}" failed (${o}x) \u2014 ${c.slice(0,200)}`),this.broadcastSSE("cron-failure",JSON.stringify({jobId:i,agent:r,error:c,consecutiveErrors:o}));let a=e.crons[i];if(a?.notify)try{await this.router.sendOutbound({channel:a.notify.channel,chatId:a.notify.chatId,text:`Cron "${i}" failed (${o}x)
116
- ${c.slice(0,300)}`,agentId:r,accountId:a.notify.accountId})}catch(l){this.log(`[CRON ALERT] notify send failed: ${l.message}`)}}),await this.cron.start(),t.push("crons")}catch(i){this.log(`[reload] cron reload failed: ${i.message}`)}let n=["agents","channels","mesh","providers","node","services"];for(let i of n)JSON.stringify(this.config[i])!==JSON.stringify(e[i])&&s.push(String(i));return this.config=e,t.length&&this.log(`[reload] applied: ${t.join(", ")}`),s.length?(this.log(`[reload] restart required to apply changes in: ${s.join(", ")}`),this.broadcastSSE("reload-partial",JSON.stringify({applied:t,restartRequired:s}))):t.length&&this.broadcastSSE("reload-complete",JSON.stringify({applied:t})),{applied:t,restartRequired:s}}scheduleMidnightHook(){let e=()=>{let t=new Date,s=t.toLocaleString("en-US",{timeZone:"Africa/Tunis"}),n=new Date(s),i=new Date(n);i.setDate(i.getDate()+1),i.setHours(0,0,5,0);let r=n.getTime()-t.getTime(),c=i.getTime()-r-t.getTime();this.log(` Cost tracking: next run in ${Math.round(c/6e4)}min`),this.midnightTimer=setTimeout(async()=>{await this.runDailyCostReport(),e()},Math.max(c,6e4))};e()}async runDailyCostReport(){let e=new Date(Date.now()-864e5).toISOString().slice(0,10),t=this.registry.getTokenTracker(),s=t.generateDailyReport(e);if(!s||s.totalTasks===0){this.log(` Cost tracking: no usage for ${e}, skipping`);return}t.appendToTokenCosts(s),this.log(` Cost tracking: ${e} \u2014 ${s.totalTasks} tasks, $${s.totalCost.toFixed(4)} (top: ${s.topAgent} $${s.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 Q(e,this.log);this.router.addChannel(t),this.log(" Telegram: enabled")}}if(this.config.channels.whatsapp.enabled){let{setWhatsAppQR:e,setWhatsAppStatus:t}=await import("./whatsapp-state-6D6FHHD2.js"),s=new Y({sessionDir:this.config.channels.whatsapp.sessionDir,defaultAgent:this.config.channels.whatsapp.defaultAgent,allowFrom:this.config.channels.whatsapp.allowFrom,routes:this.config.channels.whatsapp.routes,onQR:e,onStatus:t},this.log);this.router.addChannel(s),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 Z({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.slack?.enabled&&this.config.channels.slack.botToken&&this.config.channels.slack.appToken){let e=new ee({botToken:this.config.channels.slack.botToken,appToken:this.config.channels.slack.appToken,agentBinding:this.config.channels.slack.agentBinding},this.log);this.router.addChannel(e),this.log(" Slack: enabled")}else this.config.channels.slack?.enabled&&this.log(" Slack: enabled in config but missing botToken/appToken \u2014 skipped");if(this.config.channels.gitlab?.enabled&&this.config.channels.gitlab.token){let e=new te({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.hooks);this.mesh&&(e.setReactForwarder(async(t,s,n,i,r,c)=>{let o=this.mesh.directory().find(l=>l.peer===t&&l.healthy);if(!o){this.log(`[gitlab] react forward: peer "${t}" not found or unhealthy`);return}let a=`${o.peerUrl}/gitlab/react`;try{let l=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({project:s,noteableType:n,noteableIid:i,noteId:r,agentId:c})}),h=await l.text().catch(()=>"");this.log(`[gitlab] react forward -> ${a} : ${l.status} ${h.slice(0,200)}`)}catch(l){this.log(`[gitlab] react forward FAILED -> ${a} : ${l.message}`)}}),e.setSendNoteForwarder(async(t,s,n,i,r,c)=>{let o=this.mesh.directory().find(l=>l.peer===t&&l.healthy);if(!o)return this.log(`[gitlab] send-note forward: peer "${t}" not found or unhealthy`),"";let a=`${o.peerUrl}/gitlab/send-note`;try{let l=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({project:s,noteableType:n,noteableIid:i,agentId:r,text:c})}),h=await l.json().catch(()=>({}));return this.log(`[gitlab] send-note forward -> ${a} : ${l.status} noteId=${h.noteId||"?"}`),h.noteId||""}catch(l){return this.log(`[gitlab] send-note forward FAILED -> ${a} : ${l.message}`),""}}),e.setLogTimeForwarder(async(t,s,n,i,r,c)=>{let o=this.mesh.directory().find(l=>l.peer===t&&l.healthy);if(!o){this.log(`[gitlab] log-time forward: peer "${t}" not found or unhealthy`);return}let a=`${o.peerUrl}/gitlab/log-time`;try{let l=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({project:s,noteableType:n,noteableIid:i,agentId:r,durationMs:c})}),h=await l.text().catch(()=>"");this.log(`[gitlab] log-time forward -> ${a} : ${l.status} ${h.slice(0,160)}`)}catch(l){this.log(`[gitlab] log-time forward FAILED -> ${a} : ${l.message}`)}})),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(":"),s=parseInt(t||"18800",10);this.httpServer=xt(async(n,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"),n.method==="OPTIONS"){i.writeHead(204),i.end();return}await this.handleHttp(n,i)}),this.httpServer.on("error",n=>{n.code==="EADDRINUSE"?(this.log(` ERROR: Port ${s} is already in use. Retrying in 5s...`),setTimeout(()=>{this.httpServer?.close(),this.httpServer?.listen(s,e||"0.0.0.0")},5e3)):this.log(` HTTP error: ${n.message}`)}),this.httpServer.listen(s,e||"0.0.0.0",()=>{this.log(` HTTP API: http://${e||"0.0.0.0"}:${s}`)})}broadcastSSE(e,t){if(this.sseClients.size===0)return;let s=`event: ${e}
117
- data: ${JSON.stringify({time:new Date().toISOString(),message:t})}
118
-
119
- `;for(let n of this.sseClients)try{n.write(s)}catch{this.sseClients.delete(n)}}handleSSE(e,t){t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","Access-Control-Allow-Origin":"*"});let s=this.registry.list(),n=s.filter(i=>i.active>0);t.write(`event: status
120
- data: ${JSON.stringify({node:this.config.node.name,agents:s.length,active:n.map(i=>({id:i.id,name:i.name,tasks:i.active})),mesh:this.mesh?.directory().map(i=>({peer:i.peer,healthy:i.healthy}))||[]})}
121
-
122
- `),this.sseClients.add(t),e.on("close",()=>this.sseClients.delete(t))}handleTaskStream(e,t,s,n){t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","Access-Control-Allow-Origin":"*","X-Accel-Buffering":"no"});let i=!1,r=(a,l)=>{if(!i)try{t.write(`event: ${a}
123
- data: ${JSON.stringify(l)}
124
-
125
- `)}catch{i=!0}},c=this.registry.subscribeToTaskOutput(n,a=>r("chunk",{text:a}));if(!c){r("error",{message:"task not found or already evicted"}),t.end();return}if(r("start",{taskId:n,initial:c.initial,done:c.done}),c.done){r("end",{reason:"already finished"}),t.end();return}let o=setInterval(()=>{if(!i)try{t.write(`: ping
126
-
127
- `)}catch{}},15e3);e.on("close",()=>{i=!0,clearInterval(o),c.unsubscribe()})}async handleHttp(e,t){let s=new URL(e.url||"/",`http://${e.headers.host||"localhost"}`),n=s.pathname;try{if(e.method==="GET"&&n==="/events"){this.handleSSE(e,t);return}if(e.method==="POST"&&n.startsWith("/webhook/")){await this.webhooks.handle(e,t,n);return}let i=e.method==="GET"&&n.match(/^\/agents\/([^/]+)\/tasks\/([^/]+)\/stream$/);if(i){this.handleTaskStream(e,t,i[1],i[2]);return}let r=e.method==="GET"&&n.match(/^\/agents\/([^/]+)\/tasks$/);if(r){let o=Math.max(1,Math.min(500,parseInt(s.searchParams.get("limit")||"50",10)||50));this.json(t,200,this.registry.listTaskHistory(r[1],o));return}let c=e.method==="GET"&&n.match(/^\/agents\/([^/]+)\/tasks\/([^/]+)$/);if(c){let o=this.registry.getTaskRecord(c[1],c[2]);if(!o){this.json(t,404,{error:"not found"});return}this.json(t,200,o);return}if(e.method==="POST"&&(n==="/v1/chat/completions"||n.match(/^\/llm\/[^/]+\/v1\/chat\/completions$/))){await this.handleOpenAICompat(e,t,n);return}if(this.business&&n.startsWith("/business")&&await this.business.handleHttp(`${e.method} ${n}`,e,t))return;switch(`${e.method} ${n}`){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(o=>({id:o.id,enabled:o.enabled,nextRun:o.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 /whatsapp/state":{let{getWhatsAppState:o}=await import("./whatsapp-state-6D6FHHD2.js");this.json(t,200,o());break}case"GET /whatsapp/qr.svg":{let{getWhatsAppState:o}=await import("./whatsapp-state-6D6FHHD2.js"),a=o();if(!a.qr){t.writeHead(204,{"Cache-Control":"no-store"}),t.end();break}try{let l=await import("qrcode"),g=await(l.default||l).toString(a.qr,{type:"svg",errorCorrectionLevel:"L",margin:1,color:{dark:"#000",light:"#fff"}});t.writeHead(200,{"Content-Type":"image/svg+xml; charset=utf-8","Cache-Control":"no-store"}),t.end(g)}catch(l){t.writeHead(503,{"Content-Type":"text/plain; charset=utf-8"}),t.end("qrcode package not installed or failed: "+(l?.message||"unknown"))}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:o,getDebugLogs:a}=await import("./debug-3MEUO2P2.js");this.json(t,200,{...o(),recentLogs:a(50)});break}case"POST /debug/on":{let{setDebug:o}=await import("./debug-3MEUO2P2.js"),a=s.searchParams.get("categories")?.split(",")||["all"];o(!0,a),this.log(`Debug enabled: ${a.join(", ")}`),this.json(t,200,{enabled:!0,categories:a});break}case"POST /debug/off":{let{setDebug:o}=await import("./debug-3MEUO2P2.js");o(!1),this.log("Debug disabled"),this.json(t,200,{enabled:!1});break}case"POST /gitlab/react":{let o=await D(e),{project:a,noteableType:l,noteableIid:h,noteId:g,agentId:m}=o,f=(this.config.channels.gitlab?.agentMappings||[]).find(v=>v.agentId===m),u=f?.token||this.config.channels.gitlab?.token,y=this.config.channels.gitlab?.host;if(this.log(`[gitlab/react] agent="${m}" project="${a}" mappingFound=${!!f} mappingToken=${!!f?.token} globalToken=${!!this.config.channels.gitlab?.token}(len=${(this.config.channels.gitlab?.token||"").length}) host=${y||"MISSING"}`),!u||!y){this.json(t,404,{error:"no gitlab token for agent",debug:{agentId:m,mappingFound:!!f,hasMappingToken:!!f?.token,hasGlobalToken:!!this.config.channels.gitlab?.token,hasHost:!!y}});break}let b=encodeURIComponent(a),$=l==="issue"?`${y}/api/v4/projects/${b}/issues/${h}/notes/${g}/award_emoji`:`${y}/api/v4/projects/${b}/merge_requests/${h}/notes/${g}/award_emoji`;try{let v=await fetch($,{method:"POST",headers:{"Content-Type":"application/json","PRIVATE-TOKEN":u},body:JSON.stringify({name:"eyes"})}),k=await v.text().catch(()=>"");this.log(`[gitlab/react] -> POST ${$} : ${v.status} ${k.slice(0,120)}`),this.json(t,200,{ok:v.ok,status:v.status,gitlabResponse:k.slice(0,200)})}catch(v){this.log(`[gitlab/react] FETCH ERROR: ${v.message}`),this.json(t,500,{error:v.message})}break}case"POST /gitlab/send-note":{let o=await D(e),{project:a,noteableType:l,noteableIid:h,agentId:g,text:m}=o,u=(this.config.channels.gitlab?.agentMappings||[]).find(v=>v.agentId===g)?.token||this.config.channels.gitlab?.token,y=this.config.channels.gitlab?.host;if(!u||!y){this.json(t,404,{error:"no gitlab token for agent",debug:{agentId:g,hasToken:!!u,hasHost:!!y}});break}let b=encodeURIComponent(a),$=l==="issue"?`${y}/api/v4/projects/${b}/issues/${h}/notes`:`${y}/api/v4/projects/${b}/merge_requests/${h}/notes`;try{let v=await fetch($,{method:"POST",headers:{"Content-Type":"application/json","PRIVATE-TOKEN":u},body:JSON.stringify({body:m})}),k=await v.text().catch(()=>""),w="";try{w=String(JSON.parse(k).id||"")}catch{}this.log(`[gitlab/send-note] agent="${g}" -> POST ${$} : ${v.status} noteId=${w||"?"}`),this.json(t,200,{ok:v.ok,status:v.status,noteId:w})}catch(v){this.log(`[gitlab/send-note] FETCH ERROR: ${v.message}`),this.json(t,500,{error:v.message})}break}case"POST /gitlab/log-time":{let o=await D(e),{project:a,noteableType:l,noteableIid:h,agentId:g,durationMs:m}=o,u=(this.config.channels.gitlab?.agentMappings||[]).find(T=>T.agentId===g)?.token||this.config.channels.gitlab?.token,y=this.config.channels.gitlab?.host;if(!u||!y){this.json(t,404,{error:"no gitlab token for agent"});break}let b=Math.max(60,Math.round(Number(m)/1e3)),$=Math.floor(b/3600),v=Math.ceil(b%3600/60),k=$>0?`${$}h${v>0?` ${v}m`:""}`:`${v}m`,w=encodeURIComponent(a),A=`${y}/api/v4/projects/${w}/${l==="merge_request"?"merge_requests":"issues"}/${h}/add_spent_time`;try{let T=await fetch(A,{method:"POST",headers:{"Content-Type":"application/json","PRIVATE-TOKEN":u},body:JSON.stringify({duration:k})});this.log(`[gitlab/log-time] agent="${g}" duration=${k} -> POST ${A} : ${T.status}`),this.json(t,200,{ok:T.ok,status:T.status,duration:k})}catch(T){this.log(`[gitlab/log-time] FETCH ERROR: ${T.message}`),this.json(t,500,{error:T.message})}break}case"POST /reload":{try{let o=await this.reload();this.json(t,200,{ok:!0,...o})}catch(o){this.json(t,500,{ok:!1,error:o?.message||String(o)})}break}case"POST /send":{let o=await D(e);if(!o.channel||!o.chatId||!o.text){this.json(t,400,{error:"Required: channel, chatId, text"});return}try{let a=await this.router.sendOutbound({channel:o.channel,chatId:o.chatId,text:o.text,replyTo:o.replyTo,parseMode:o.parseMode,agentId:o.agentId,accountId:o.accountId});this.json(t,200,{ok:!0,messageId:a||null})}catch(a){this.json(t,400,{error:a.message})}break}case"GET /channels":this.json(t,200,this.router.getChannelNames());break;case"GET /services":this.json(t,200,this.router.serviceMatcher?.list()||[]);break;case"POST /task":{let o=await D(e),a=o.agent||this.config.node.defaultAgent;if(!a||!o.message){this.json(t,400,{error:"Missing: message (and no defaultAgent configured)"});return}let l=await this.registry.execute({agentId:a,message:o.message,context:o.context},()=>{});this.json(t,l.error?500:200,l);break}case"POST /ask":case"GET /ask":{let o=new URL(e.url||"/",`http://${e.headers.host||"localhost"}`),a=o.searchParams.get("q")||o.searchParams.get("message")||"";if(e.method==="POST"){let p=await D(e);a=p.message||p.q||a}let l=this.config.node.defaultAgent;if(!l){this.json(t,400,{error:"No defaultAgent configured in node config"});return}if(!a){this.json(t,400,{error:"Missing message (pass as ?q=... or {message:...})"});return}let h=`[VOICE MODE \u2014 Your response will be spoken aloud by a TTS engine. Keep it to 2-3 short sentences. Use plain language, no markdown, no code blocks, no bullet points, no URLs. Speak conversationally.]
128
-
129
- ${a}`,g=await this.registry.execute({agentId:l,message:h,context:{channel:"voice",sender:"Siri"}}),m=Et(g.content);this.json(t,g.error?500:200,{text:m,full:g.content,error:g.error,duration:g.duration});break}case"POST /mesh/task":{let o=await D(e);if(!o.peer||!o.message){this.json(t,400,{error:"Missing: peer, message"});return}if(!this.mesh){this.json(t,400,{error:"Mesh not enabled"});return}let a=await this.mesh.sendTask(o.peer,o.message,o.agent);this.json(t,200,{response:a});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(o=>({id:o.id,name:o.name,description:`Agent "${o.name}" (${o.tier})`,tags:[o.tier]})),defaultInputModes:["text"],defaultOutputModes:["text"]});break;case"GET /wiki/agents":{let o=this.registry.getWikiHub();this.json(t,200,{nodeId:this.config.node.id,agents:o.summary()});break}case"GET /wiki/entries":{let o=this.registry.getWikiHub(),a=s.searchParams.get("agent")||void 0,l=s.searchParams.get("after")||void 0,h=a?o.getAgentEntries(a):o.getSharedStore().listEntries({after:l});this.json(t,200,{nodeId:this.config.node.id,count:h.length,entries:h.map(g=>({id:g.id,date:g.date,agentId:g.agentId,source:g.source,sourceContext:g.sourceContext,content:g.content}))});break}case"GET /wiki/articles":{let o=this.registry.getWikiHub(),a=s.searchParams.get("agent");if(!a){this.json(t,400,{error:"?agent= required"});break}let h=o.getAgentWiki(a).rebuildIndex();this.json(t,200,{nodeId:this.config.node.id,agentId:a,articles:h.articles});break}case"GET /wiki/article":{let o=this.registry.getWikiHub(),a=s.searchParams.get("agent"),l=s.searchParams.get("path");if(!a||!l){this.json(t,400,{error:"?agent= and ?path= required"});break}let g=o.getAgentWiki(a).readArticle(l);if(!g){this.json(t,404,{error:"Article not found"});break}this.json(t,200,{nodeId:this.config.node.id,agentId:a,path:g.path,title:g.meta.title,tags:g.meta.tags,owner:g.meta.owner,created:g.meta.created,lastUpdated:g.meta.lastUpdated,sources:g.meta.sources,content:g.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","POST /reload \u2014 re-read agentx.json (hot-swaps crons)","GET /.well-known/agent-card.json"]})}}catch(i){this.json(t,500,{error:i.message})}}async handleOpenAICompat(e,t,s){let n=await D(e),i=s.match(/^\/llm\/([^/]+)\//),r=Object.keys(this.config.agents)[0]||"default",c=i?.[1]||n.model||r,o=n.messages||[],a=[...o].reverse().find(m=>m.role==="user");if(!a?.content){this.json(t,400,{error:{message:"No user message found",type:"invalid_request_error"}});return}let l=o.slice(0,-1).map(m=>`${m.role==="user"?"User":"Assistant"}: ${m.content.slice(0,200)}`),h=l.length>0?`[Conversation]
130
- ${l.slice(-10).join(`
131
- `)}
132
-
133
- `:"";if(n.stream===!0){t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"});let m=`chatcmpl-${Date.now().toString(36)}`,p=await this.registry.execute({agentId:c,message:h+a.content,context:{channel:"api",sender:"openai-compat"}}),f=p.error||p.content||"",u={id:m,object:"chat.completion.chunk",created:Math.floor(Date.now()/1e3),model:c,choices:[{index:0,delta:{role:"assistant",content:f},finish_reason:"stop"}]};t.write(`data: ${JSON.stringify(u)}
134
-
135
- `),t.write(`data: [DONE]
136
-
137
- `),t.end()}else{let m=await this.registry.execute({agentId:c,message:h+a.content,context:{channel:"api",sender:"openai-compat"}}),p=m.error||m.content||"",f=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:c,choices:[{index:0,message:{role:"assistant",content:p},finish_reason:"stop"}],usage:{prompt_tokens:Math.ceil(a.content.length/4),completion_tokens:f,total_tokens:Math.ceil(a.content.length/4)+f}})}}json(e,t,s){e.writeHead(t,{"Content-Type":"application/json"}),e.end(JSON.stringify(s,null,2))}};function Et(d){let e=d;return e=e.replace(/```[\s\S]*?```/g,". "),e=e.replace(/`[^`]*`/g,""),e=e.replace(/\*\*(.*?)\*\*/g,"$1"),e=e.replace(/__(.*?)__/g,"$1"),e=e.replace(/\*([^*]+)\*/g,"$1"),e=e.replace(/_([^_]+)_/g,"$1"),e=e.replace(/~~(.*?)~~/g,"$1"),e=e.replace(/\[([^\]]+)\]\([^)]+\)/g,"$1"),e=e.replace(/!\[([^\]]*)\]\([^)]+\)/g,"image$1 "),e=e.replace(/^#{1,6}\s+/gm,""),e=e.replace(/^[\s]*[-*+]\s+/gm,". "),e=e.replace(/^[\s]*\d+\.\s+/gm,". "),e=e.replace(/^[-=_]{3,}$/gm,""),e=e.replace(/^>\s*/gm,""),e=e.replace(/https?:\/\/\S+/g,"link"),e=e.replace(/\/\w+(\/\w+)+/g,t=>t.split("/").filter(Boolean).join(" slash ")),e=e.replace(/&/g," and "),e=e.replace(/@/g," at "),e=e.replace(/=>/g," returns "),e=e.replace(/->/g," to "),e=e.replace(/\|/g," or "),e=e.replace(/\be\.g\./gi,"for example"),e=e.replace(/\bi\.e\./gi,"that is"),e=e.replace(/\betc\./gi,"etcetera"),e=e.replace(/\bvs\.?\b/gi,"versus"),e=e.replace(/\n{2,}/g,". "),e=e.replace(/\n/g," "),e=e.replace(/\s+/g," "),e=e.replace(/\.\s*\./g,"."),e=e.replace(/\s+([.,!?])/g,"$1"),e=e.trim().replace(/^[.,:;]+\s*/,""),e.length>800&&(e=e.slice(0,800).replace(/\s+\S*$/,"")+"..."),e}async function D(d){return new Promise((e,t)=>{let s="";d.on("data",n=>s+=n.toString()),d.on("end",()=>{try{e(s?JSON.parse(s):{})}catch{e({})}}),d.on("error",t)})}import Oe from"path";import ot from"fs-extra";function hn(){let d=[];try{d.push(Oe.resolve(__dirname,"..","package.json")),d.push(Oe.resolve(__dirname,"package.json"))}catch{}d.push(Oe.resolve(process.cwd(),"package.json"));for(let e of d)try{if(ot.existsSync(e)){let t=ot.readJSONSync(e);if(t&&t.name)return t}}catch{}return{name:"agentix-cli",version:"0.0.0"}}export{z as a,K as b,G as c,Q as d,Y as e,rt as f,hn as g};
138
- //# sourceMappingURL=chunk-WVNSWBCU.js.map