agentix-cli 0.16.0 → 0.18.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 (173) hide show
  1. package/dist/agent-RDVGL2TW.js +2 -0
  2. package/dist/audit-Z47AQFFD.js +3 -0
  3. package/dist/audit-Z47AQFFD.js.map +1 -0
  4. package/dist/board-dashboard-KX2CNOSY.js +7282 -0
  5. package/dist/board-dashboard-KX2CNOSY.js.map +1 -0
  6. package/dist/chunk-34NG7UDX.js +11 -0
  7. package/dist/chunk-34NG7UDX.js.map +1 -0
  8. package/dist/{chunk-6MKAXEE5.js → chunk-4ITQP3XL.js} +2 -2
  9. package/dist/{chunk-6MKAXEE5.js.map → chunk-4ITQP3XL.js.map} +1 -1
  10. package/dist/chunk-53NRX2NA.js +2 -0
  11. package/dist/chunk-53NRX2NA.js.map +1 -0
  12. package/dist/chunk-6PPIFDOQ.js +1742 -0
  13. package/dist/chunk-6PPIFDOQ.js.map +1 -0
  14. package/dist/chunk-AFRHKA7B.js +2 -0
  15. package/dist/chunk-AFRHKA7B.js.map +1 -0
  16. package/dist/chunk-ASSJ4Z6Z.js +164 -0
  17. package/dist/chunk-ASSJ4Z6Z.js.map +1 -0
  18. package/dist/chunk-BLUXL6HR.js +854 -0
  19. package/dist/chunk-BLUXL6HR.js.map +1 -0
  20. package/dist/chunk-BTAEYL3P.js +72 -0
  21. package/dist/chunk-BTAEYL3P.js.map +1 -0
  22. package/dist/chunk-CDNWCYF7.js +2 -0
  23. package/dist/chunk-CDNWCYF7.js.map +1 -0
  24. package/dist/chunk-DJEZ2HVU.js +5 -0
  25. package/dist/chunk-DJEZ2HVU.js.map +1 -0
  26. package/dist/{chunk-FLOGK2U7.js → chunk-DSCMCVTV.js} +3 -3
  27. package/dist/chunk-DSCMCVTV.js.map +1 -0
  28. package/dist/chunk-EW27SCCC.js +2 -0
  29. package/dist/chunk-EW27SCCC.js.map +1 -0
  30. package/dist/chunk-HPDHNIA3.js +2 -0
  31. package/dist/chunk-IM376SD6.js +13 -0
  32. package/dist/chunk-IM376SD6.js.map +1 -0
  33. package/dist/chunk-J4WKMKTP.js +5 -0
  34. package/dist/{chunk-YI2T5QYB.js.map → chunk-J4WKMKTP.js.map} +1 -1
  35. package/dist/chunk-JGXE2YNV.js +46 -0
  36. package/dist/chunk-JGXE2YNV.js.map +1 -0
  37. package/dist/chunk-JXEPJ3YD.js +118 -0
  38. package/dist/chunk-JXEPJ3YD.js.map +1 -0
  39. package/dist/chunk-LY7JGDV7.js +6 -0
  40. package/dist/chunk-LY7JGDV7.js.map +1 -0
  41. package/dist/chunk-N67JZAXU.js +2 -0
  42. package/dist/chunk-N67JZAXU.js.map +1 -0
  43. package/dist/chunk-SR3X25TN.js +3 -0
  44. package/dist/chunk-SR3X25TN.js.map +1 -0
  45. package/dist/{chunk-7MBTMCKY.js → chunk-TRQSP67X.js} +2 -2
  46. package/dist/chunk-U364GNQX.js +719 -0
  47. package/dist/chunk-U364GNQX.js.map +1 -0
  48. package/dist/chunk-VJUTQAPJ.js +2 -0
  49. package/dist/chunk-VJUTQAPJ.js.map +1 -0
  50. package/dist/{chunk-HI24KAEY.js → chunk-WJXU5F4Z.js} +3 -4
  51. package/dist/{chunk-HI24KAEY.js.map → chunk-WJXU5F4Z.js.map} +1 -1
  52. package/dist/{chunk-QZP65I2H.js → chunk-ZE2YFX4K.js} +2 -2
  53. package/dist/cli.js +1 -320
  54. package/dist/cli.js.map +1 -1
  55. package/dist/{compaction-JBZZ7I7Y.js → compaction-S5H6DTOR.js} +6 -6
  56. package/dist/compaction-S5H6DTOR.js.map +1 -0
  57. package/dist/config-UOHEG4BW.js +2 -0
  58. package/dist/config-mutator-KSEBWKJH.js +2 -0
  59. package/dist/context-planner-2RGHJHSO.js +17 -0
  60. package/dist/context-planner-2RGHJHSO.js.map +1 -0
  61. package/dist/debug-TGAP7E7W.js +2 -0
  62. package/dist/discover-OATU7SHI.js +4 -0
  63. package/dist/discover-OATU7SHI.js.map +1 -0
  64. package/dist/editor-chat-FUBQLMAQ.js +137 -0
  65. package/dist/editor-chat-FUBQLMAQ.js.map +1 -0
  66. package/dist/heal-YP7QI7D3.js +2 -0
  67. package/dist/inbox-O5UB3YQN.js +315 -0
  68. package/dist/inbox-O5UB3YQN.js.map +1 -0
  69. package/dist/index.d.ts +3390 -1814
  70. package/dist/index.js +23 -53
  71. package/dist/index.js.map +1 -1
  72. package/dist/ingest-whatsapp-C3O5UPBX.js +6 -0
  73. package/dist/ingest-whatsapp-C3O5UPBX.js.map +1 -0
  74. package/dist/loader-BGWA7IRY.js +2 -0
  75. package/dist/loader-LBHKSXBK.js +2 -0
  76. package/dist/memory-extract-KKPKAOPH.js +2 -0
  77. package/dist/processes-7W27G3J3.js +256 -0
  78. package/dist/processes-7W27G3J3.js.map +1 -0
  79. package/dist/program-2MDELQLX.js +2 -0
  80. package/dist/providers-WCQSKTNP.js +2 -0
  81. package/dist/query-FZAL7AQD.js +2 -0
  82. package/dist/recipes-LZ56TA3X.js +2 -0
  83. package/dist/registry-ONJSILH4.js +2 -0
  84. package/dist/scheduler-XRM2RJIV.js +2 -0
  85. package/dist/slack-D27BE37Q.js +3 -0
  86. package/dist/slack-D27BE37Q.js.map +1 -0
  87. package/dist/store-4UCO56K2.js +2 -0
  88. package/dist/store-4UCO56K2.js.map +1 -0
  89. package/dist/{subagent-2SJHSSJS.js → subagent-EPOT3QDM.js} +2 -2
  90. package/dist/task-store-3NUWLYRY.js +2 -0
  91. package/dist/task-store-3NUWLYRY.js.map +1 -0
  92. package/dist/telegram-UWV67KWG.js +3 -0
  93. package/dist/telegram-UWV67KWG.js.map +1 -0
  94. package/dist/timers-QB4KYNAE.js +2 -0
  95. package/dist/timers-QB4KYNAE.js.map +1 -0
  96. package/dist/types-ECMEC5BZ.js +2 -0
  97. package/dist/types-ECMEC5BZ.js.map +1 -0
  98. package/dist/types-TTB6DJUE.js +2 -0
  99. package/dist/types-TTB6DJUE.js.map +1 -0
  100. package/dist/usage-dashboard-FQGPPABD.js +803 -0
  101. package/dist/usage-dashboard-FQGPPABD.js.map +1 -0
  102. package/dist/web/workflow-editor.global.js +1450 -0
  103. package/dist/web/workflow-editor.global.js.map +1 -0
  104. package/dist/whatsapp-KJ2NRKKQ.js +3 -0
  105. package/dist/whatsapp-KJ2NRKKQ.js.map +1 -0
  106. package/dist/{whatsapp-state-6D6FHHD2.js → whatsapp-state-3ZVQ6MQD.js} +2 -2
  107. package/dist/{wiki-UDPYFJBL.js → wiki-OUYKM7MB.js} +2 -2
  108. package/dist/wiki-OUYKM7MB.js.map +1 -0
  109. package/dist/workspace-setup-6GDBEORT.js +2 -0
  110. package/dist/workspace-setup-6GDBEORT.js.map +1 -0
  111. package/package.json +19 -2
  112. package/dist/agent-BCDPSPKU.js +0 -2
  113. package/dist/board-dashboard-LA7WW56M.js +0 -5668
  114. package/dist/board-dashboard-LA7WW56M.js.map +0 -1
  115. package/dist/chunk-6U2XOQHU.js +0 -7
  116. package/dist/chunk-6U2XOQHU.js.map +0 -1
  117. package/dist/chunk-7FJQGD6U.js +0 -6
  118. package/dist/chunk-7FJQGD6U.js.map +0 -1
  119. package/dist/chunk-ABDP7TDL.js +0 -71
  120. package/dist/chunk-ABDP7TDL.js.map +0 -1
  121. package/dist/chunk-CXY2RYNI.js +0 -11
  122. package/dist/chunk-CXY2RYNI.js.map +0 -1
  123. package/dist/chunk-FIOB3MEX.js +0 -2
  124. package/dist/chunk-FLOGK2U7.js.map +0 -1
  125. package/dist/chunk-HVC6FJNP.js +0 -108
  126. package/dist/chunk-HVC6FJNP.js.map +0 -1
  127. package/dist/chunk-K7BE4E35.js +0 -138
  128. package/dist/chunk-K7BE4E35.js.map +0 -1
  129. package/dist/chunk-KXJSLLSQ.js +0 -199
  130. package/dist/chunk-KXJSLLSQ.js.map +0 -1
  131. package/dist/chunk-KXZMYLHQ.js +0 -46
  132. package/dist/chunk-KXZMYLHQ.js.map +0 -1
  133. package/dist/chunk-M7HKBG3V.js +0 -2
  134. package/dist/chunk-M7HKBG3V.js.map +0 -1
  135. package/dist/chunk-MCJUAE6V.js +0 -6
  136. package/dist/chunk-MCJUAE6V.js.map +0 -1
  137. package/dist/chunk-RB7265LO.js +0 -12
  138. package/dist/chunk-RB7265LO.js.map +0 -1
  139. package/dist/chunk-YI2T5QYB.js +0 -5
  140. package/dist/compaction-JBZZ7I7Y.js.map +0 -1
  141. package/dist/config-LN6JEHMF.js +0 -2
  142. package/dist/config-mutator-24TH7PHM.js +0 -2
  143. package/dist/debug-3MEUO2P2.js +0 -2
  144. package/dist/heal-WKY5I4KB.js +0 -2
  145. package/dist/loader-PTCXLJEX.js +0 -2
  146. package/dist/memory-extract-AD7KMRPC.js +0 -2
  147. package/dist/providers-VUIWO3ZK.js +0 -2
  148. package/dist/query-PKGYE5HR.js +0 -2
  149. package/dist/registry-ASC2QS5K.js +0 -2
  150. package/dist/scheduler-A3TQOCKT.js +0 -2
  151. package/dist/types-OIFU3KSQ.js +0 -2
  152. package/dist/usage-dashboard-QX3CF6KK.js +0 -404
  153. package/dist/usage-dashboard-QX3CF6KK.js.map +0 -1
  154. package/dist/workspace-setup-ZWTT42XT.js +0 -2
  155. /package/dist/{agent-BCDPSPKU.js.map → agent-RDVGL2TW.js.map} +0 -0
  156. /package/dist/{chunk-FIOB3MEX.js.map → chunk-HPDHNIA3.js.map} +0 -0
  157. /package/dist/{chunk-7MBTMCKY.js.map → chunk-TRQSP67X.js.map} +0 -0
  158. /package/dist/{chunk-QZP65I2H.js.map → chunk-ZE2YFX4K.js.map} +0 -0
  159. /package/dist/{config-LN6JEHMF.js.map → config-UOHEG4BW.js.map} +0 -0
  160. /package/dist/{config-mutator-24TH7PHM.js.map → config-mutator-KSEBWKJH.js.map} +0 -0
  161. /package/dist/{debug-3MEUO2P2.js.map → debug-TGAP7E7W.js.map} +0 -0
  162. /package/dist/{heal-WKY5I4KB.js.map → heal-YP7QI7D3.js.map} +0 -0
  163. /package/dist/{loader-PTCXLJEX.js.map → loader-BGWA7IRY.js.map} +0 -0
  164. /package/dist/{memory-extract-AD7KMRPC.js.map → loader-LBHKSXBK.js.map} +0 -0
  165. /package/dist/{providers-VUIWO3ZK.js.map → memory-extract-KKPKAOPH.js.map} +0 -0
  166. /package/dist/{query-PKGYE5HR.js.map → program-2MDELQLX.js.map} +0 -0
  167. /package/dist/{registry-ASC2QS5K.js.map → providers-WCQSKTNP.js.map} +0 -0
  168. /package/dist/{scheduler-A3TQOCKT.js.map → query-FZAL7AQD.js.map} +0 -0
  169. /package/dist/{types-OIFU3KSQ.js.map → recipes-LZ56TA3X.js.map} +0 -0
  170. /package/dist/{wiki-UDPYFJBL.js.map → registry-ONJSILH4.js.map} +0 -0
  171. /package/dist/{workspace-setup-ZWTT42XT.js.map → scheduler-XRM2RJIV.js.map} +0 -0
  172. /package/dist/{subagent-2SJHSSJS.js.map → subagent-EPOT3QDM.js.map} +0 -0
  173. /package/dist/{whatsapp-state-6D6FHHD2.js.map → whatsapp-state-3ZVQ6MQD.js.map} +0 -0
@@ -1,138 +0,0 @@
1
- import{b as ye}from"./chunk-FLOGK2U7.js";import{b as Fe}from"./chunk-QZP65I2H.js";import{b as He}from"./chunk-6U2XOQHU.js";import{e as Le,g as Ne}from"./chunk-ABDP7TDL.js";import{a as Ee}from"./chunk-5F3J55LG.js";import{f as De,g as je}from"./chunk-RB7265LO.js";import{f as fe,g as Be,i as ct}from"./chunk-7FJQGD6U.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 n=await this.rpc("tasks/send",{id:`task-${Date.now().toString(36)}`,message:{role:"user",parts:[{type:"text",text:e}]},metadata:t});if(n.error)throw new Error(`A2A error: ${n.error.message}`);return n.result}async*sendTaskStream(e,t){let n=JSON.stringify({jsonrpc:"2.0",id:1,method:"tasks/sendSubscribe",params:{id:`task-${Date.now().toString(36)}`,message:{role:"user",parts:[{type:"text",text:e}]},metadata:t}}),s=await fetch(this.baseUrl,{method:"POST",headers:{...this.headers(),"Content-Type":"application/json",Accept:"text/event-stream"},body:n});if(!s.ok||!s.body)throw new Error(`A2A stream error: ${s.status}`);let i=s.body.getReader(),r=new TextDecoder,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 n=await fetch(this.baseUrl,{method:"POST",headers:{...this.headers(),"Content-Type":"application/json"},body:JSON.stringify({jsonrpc:"2.0",id:Date.now(),method:e,params:t})});if(!n.ok)throw new Error(`A2A HTTP error: ${n.status}`);return n.json()}headers(){let e={};return this.token&&(e.Authorization=`Bearer ${this.token}`),e}};var K=class{peers=new Map;healthTimer;config;log;constructor(e,t=console.error.bind(console,"[mesh]")){this.config=e,this.log=t;for(let n of e.mesh.peers)this.peers.set(n.name,{peer:n,client:new z(n.url,n.token),healthy:!1,agents:[]})}async start(){this.log(`Mesh starting with ${this.peers.size} peer(s)`),await this.discoverAll();let e=this.config.mesh.healthCheck.interval*1e3;this.healthTimer=setInterval(()=>this.discoverAll(),e)}async stop(){this.healthTimer&&clearInterval(this.healthTimer)}async discoverAll(){let e=await Promise.allSettled(Array.from(this.peers.entries()).map(([n,s])=>this.discoverPeer(n,s))),t=Array.from(this.peers.values()).filter(n=>n.healthy).length;this.log(`Discovery complete: ${t}/${this.peers.size} peers healthy`)}async discoverPeer(e,t){let n=this.config.mesh.healthCheck.timeout*1e3;try{let s=new AbortController,i=setTimeout(()=>s.abort(),n),r=await t.client.getAgentCard();clearTimeout(i),t.healthy=!0,t.lastCheck=new Date,t.agentCard=r,t.agents=r.skills||[],this.log(`Peer "${e}" healthy: ${r.name} (${t.agents.length} skills)`)}catch(s){t.healthy=!1,t.lastCheck=new Date,this.log(`Peer "${e}" unreachable: ${s.message}`)}}async sendTask(e,t,n){let s=this.peers.get(e);if(!s)throw new Error(`Unknown peer: ${e}`);if(!s.healthy)throw new Error(`Peer "${e}" is not healthy`);let i=n||s.agents[0]?.id;if(!i)throw new Error(`Peer "${e}" has no agents`);let r=`${s.peer.url}/task`,c={"Content-Type":"application/json"};s.peer.token&&(c.Authorization=`Bearer ${s.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(n=>n.id===e))return t}directory(){return Array.from(this.peers.entries()).map(([e,t])=>({peer:e,peerUrl:t.peer.url,healthy:t.healthy,skills:t.agents,lastCheck:t.lastCheck}))}};import{readFileSync as lt,writeFileSync as ht,existsSync as gt,mkdirSync as dt}from"fs";import{resolve as Ge}from"path";function B(d,e,t="\u2026"){if(!d)return"";if(d.length<=e)return d;let n=e-t.length;if(n<=0)return d.slice(0,e);let s=d.slice(0,n),i=s.lastIndexOf(`
3
- `);if(i>n*.6)return s.slice(0,i)+`
4
- `+t;let r=Math.max(s.lastIndexOf(" "),s.lastIndexOf(" "));return r>n*.6?s.slice(0,r)+t:s+t}function be(d,e,t){if(!d)return"";let s=d.split(`
5
- `).slice(0,e).join(`
6
- `);return B(s,t)}var pt=30,Ue=6e3,ut=1500,mt=6e4;function ft(d){let e=new Date(d),t=e.getUTCHours().toString().padStart(2,"0"),n=Math.floor(e.getUTCMinutes()/15)*15;return`${t}:${n.toString().padStart(2,"0")}`}var V=class{dir;cache=new Map;constructor(e=Ge(process.cwd(),".agentx/groups")){this.dir=e,dt(this.dir,{recursive:!0})}add(e,t,n){let s=this.load(e),i=Date.now(),r=B(n,ut),c=s[s.length-1];if(!(c&&c.sender===t&&c.text===r&&i-c.timestamp<mt)){for(s.push({sender:t,text:r,timestamp:i});s.length>pt;)s.shift();this.cache.set(e,s),this.save(e,s)}}buildContext(e){let t=this.load(e);if(t.length<=1)return"";let n=["[Recent group conversation]"],s=0,i=null,r=[],c="",o=()=>{if(!i)return!0;let a=i.repeat>1?`${i.line} [\xD7${i.repeat}]`:i.line;if(s+a.length>Ue)return!1;if(r.push(a),s+=a.length,i.bucket!==c){let l=`\u2014 ${i.bucket} \u2014`;if(s+l.length>Ue)return i=null,!1;r.push(l),s+=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=ft(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--)n.splice(1,0,r[a]);return n.length<=1?"":(n.push("[End of conversation \u2014 respond to the latest message]"),n.join(`
7
- `))}getEntries(e){return[...this.load(e)]}filePath(e){let t=e.replace(/[^a-zA-Z0-9_-]/g,"_");return Ge(this.dir,`${t}.json`)}load(e){if(this.cache.has(e))return this.cache.get(e);let t=this.filePath(e);if(!gt(t))return this.cache.set(e,[]),[];try{let n=JSON.parse(lt(t,"utf-8")),s=Array.isArray(n)?n:[];return this.cache.set(e,s),s}catch{return this.cache.set(e,[]),[]}}save(e,t){try{ht(this.filePath(e),JSON.stringify(t))}catch{}}};var yt={minChars:80,maxChars:4e3,idleFlushMs:2e3,minIntervalMs:1500,humanPacing:!1},bt={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,n){let s=n?bt[n]:{};this.config={...yt,...s,...t},this.emitter=e}push(e){this.buffer+=e,this.idleTimer&&clearTimeout(this.idleTimer),this.updateCodeFenceState(e),this.shouldEmit()?this.emit():this.idleTimer=setTimeout(()=>{this.buffer.length>0&&this.emit()},this.config.idleFlushMs)}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),n=this.buffer.slice(0,t);if(this.buffer=this.buffer.slice(t),this.config.humanPacing){let s=800+Math.random()*1700;setTimeout(()=>this.emitImmediate(n),s)}else this.emitImmediate(n)}emitImmediate(e){this.lastEmitTime=Date.now(),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),n=e.lastIndexOf(`
8
-
9
- `,t);if(n>=this.config.minChars)return n+2;let s=e.lastIndexOf(`
10
- `,t);if(s>=this.config.minChars)return s+1;for(let i=t-1;i>=this.config.minChars;i--)if((e[i]==="."||e[i]==="!"||e[i]==="?")&&(i+1>=e.length||e[i+1]===" "||e[i+1]===`
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 n of t)this.fencePattern.test(n.trim())&&(this.insideCodeFence=!this.insideCodeFence)}};import{readFileSync as Je,writeFileSync as ze,existsSync as ke,mkdirSync as Ke,appendFileSync as We}from"fs";import{resolve as we,dirname as Ve}from"path";var $e=class{filePath;constructor(e){this.filePath=we(e,".agentx/router/inflight.jsonl"),Ke(Ve(this.filePath),{recursive:!0})}start(e){try{let t=JSON.stringify({type:"start",ts:Date.now(),...e})+`
13
- `;We(this.filePath,t)}catch{}}done(e){try{We(this.filePath,JSON.stringify({type:"done",id:e,ts:Date.now()})+`
14
- `)}catch{}}loadUnfinished(){if(!ke(this.filePath))return[];try{let e=Je(this.filePath,"utf-8"),t=new Map;for(let n of e.split(`
15
- `))if(n.trim())try{let s=JSON.parse(n);s.type==="start"?t.set(s.id,s):s.type==="done"&&t.delete(s.id)}catch{}return[...t.values()]}catch{return[]}}compact(){try{let e=this.loadUnfinished(),t=e.map(n=>JSON.stringify(n)).join(`
16
- `)+(e.length?`
17
- `:"");ze(this.filePath,t)}catch{}}};var kt=4e3,ve=class{registry;config;channels=new Map;hooks;mesh;serviceMatcher;business;groupLog;handoverStore;log;getHandoverStore(){return this.handoverStore}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,n,s=console.error.bind(console,"[router]")){this.registry=e,this.config=t,this.hooks=n,this.log=s,this.groupLog=new V,this.handoverStore=new Ee({log:(...i)=>s("[handover]",...i)}),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(Je(e,"utf-8")),n=Date.now(),s=0;for(let[i,r]of Object.entries(t))typeof r=="number"&&n-r<this.MESSAGE_ID_TTL_MS&&(this.recentMessageIds.set(i,r),s++);s>0&&this.log(`Router dedup: restored ${s} 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 n=we(process.cwd(),this.DEDUP_STORE_PATH),s=Ve(n);ke(s)||Ke(s,{recursive:!0}),ze(n,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 n=e.accountId;return t.name==="telegram"&&!n&&e.agentId&&(n=this.getAccountForAgent(e.agentId)),this.log(`Outbound [${e.channel}] -> ${e.chatId}: ${e.text.slice(0,80)}`),t.name==="telegram"&&n?this.adapterSend(t,{...e,accountId:n}):t.send(e)}getChannelNames(){return[...this.channels.keys()]}async startAll(){for(let[e,t]of this.channels)this.log(`Starting channel: ${e}`),await t.start();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 n=this.channels.get(t.channel);if(!n){this.log(`Inflight replay: adapter "${t.channel}" not available, skipping task ${t.id}`),this.inflight.done(t.id);continue}let s={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(n,s,{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}`,n=Date.now();if(this.recentMessageIds.size>=this.MESSAGE_ID_MAX_ENTRIES)for(let[i,r]of this.recentMessageIds)n-r>this.MESSAGE_ID_TTL_MS&&this.recentMessageIds.delete(i);let s=this.recentMessageIds.get(t);return s&&n-s<this.MESSAGE_ID_TTL_MS?!0:(this.recentMessageIds.set(t,n),this.scheduleDedupSave(),!1)}async handleMessage(e,t,n={}){if(!n.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 s=this.resolveAgent(t);if(!s)return;if(t.group&&t.channel==="telegram"){let r=this.getAccountForAgent(s);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:s,preferNode:t.preferNode});try{return await this.processResolvedMessage(e,t,s,i)}finally{this.inflight.done(t.id)}}async processResolvedMessage(e,t,n,s){if(t.preferNode){this.log(`preferNode="${t.preferNode}" \u2014 forcing mesh routing for agent "${n}"`),await this.handleViaMeshByPeer(e,t,n,t.preferNode)||this.log(`Mesh peer "${t.preferNode}" not found or unhealthy for agent "${n}"`);return}let i=this.registry.getAgent(n);if(!i){await this.handleViaMeshByAgentId(e,t,n)||this.log(`Agent "${n}" not found locally or on any mesh peer`);return}let r=i.name||n,c=this.getAccountForAgent(n)||t.accountId;this.log(`Routing [${t.channel}/${t.sender.name}] -> "${r}": ${t.text.slice(0,80)}`),this.adapterReact(e,s,t.id,"\u{1F440}",c);let o=this.startTypingLoop(e,s,c),a=typeof e.editMessage=="function",l,h="",g=a?new q(async k=>{if(h+=k,l)try{await this.adapterEdit(e,s,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:s,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(s):"",f=p?`${p}
20
-
21
- ${t.sender.name}: ${t.text}`:t.text,u=await this.registry.execute({message:f,agentId:n,context:{channel:t.channel,sender:t.sender.name,senderId:t.sender.id,senderUsername:t.sender.username,group:t.group?.name,chatId:s,mediaPath:t.media?.path,mediaType:t.media?.type,replyToText:t.replyToText,channelMeta:t.channelMeta}},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,s,l,k,"plain",c):await this.adapterSend(e,{channel:t.channel,chatId:s,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:n});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?.(n)||(y=`> **${r}** (${n})
22
-
23
- ${y}`)),t.channel==="whatsapp"&&y&&(y=`*${r}*
24
-
25
- ${y}`);let b;y&&(l?(await this.adapterEdit(e,s,l,y,void 0,c),b=l):b=await this.adapterSend(e,{channel:t.channel,chatId:s,text:y,replyTo:t.id,accountId:c,agentId:n})),t.group&&y&&this.groupLog.add(s,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:n,accountId:$.destination.accountId}).catch(_=>{this.log(`Task notification failed: ${_.message}`)})}}if(this.business&&u.duration&&this.business.recordTaskCompletion(n,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(s,u.duration,n).catch(w=>{this.log(`GitLab time tracking failed: ${w.message}`)})}y&&b&&["telegram","whatsapp","discord","slack"].includes(t.channel)&&this.handleBotToBotChain(e,t,n,y,b,0).catch(k=>{this.log(`Bot-to-bot error: ${k.message}`)})}async handleBotToBotChain(e,t,n,s,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(n);for(let[o,a]of Object.entries(this.config.agents)){if(o===n)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=>s.toLowerCase().includes(f.toLowerCase())))continue;this.log(`Bot-to-bot [${r+1}]: "${n}" -> "${o}"`);let g=t.group?.id||t.sender.id,m=this.getAccountForAgent(o),p=this.getAccountForAgent(n);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
- [${n} said]: ${s}`:s,y=await this.registry.execute({message:u,agentId:o,context:{channel:t.channel,sender:`agent:${n}`,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,n,s,i,r){return e.name==="telegram"&&r?e.editMessage(t,n,s,i,r):e.editMessage?.(t,n,s,i)??!1}adapterReact(e,t,n,s,i){e.name==="telegram"&&i?e.react(t,n,s,i):e.react?.(t,n,s)}startTypingLoop(e,t,n){let s=()=>{e.name==="telegram"&&n?e.sendTyping(t,n):e.sendTyping?.(t)};return s(),setInterval(s,kt)}async handleViaMesh(e,t){if(!this.mesh)return!1;let n=t.text.toLowerCase(),s=this.mesh.directory();for(let i of s)if(i.healthy){for(let r of i.skills)if(n.includes(r.id.toLowerCase())||n.includes(r.name.toLowerCase())){this.log(`Mesh routing [${t.channel}/${t.sender.name}] -> peer "${i.peer}" agent "${r.id}"`);let 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,n){if(!this.mesh)return!1;let s=this.mesh.directory();for(let i of s){if(!i.healthy)continue;let r=i.skills.find(l=>l.id===n);if(!r)continue;this.log(`Mesh routing by agentId [${t.channel}/${t.sender.name}] -> peer "${i.peer}" agent "${n}"`);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,n);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 ${n}: ${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,n,s){if(!this.mesh)return!1;let r=this.mesh.directory().find(g=>g.peer===s);if(!r||!r.healthy)return!1;let c=r.skills.find(g=>g.id===n)??{name:n,id:n};this.log(`Mesh routing by preferNode [${t.channel}/${t.sender.name}] -> peer "${s}" agent "${n}"`);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(s,t.text,n),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,n).catch(f=>{this.log(`GitLab time tracking (mesh) failed: ${f.message}`)})}return!0}catch(g){return clearInterval(l),this.log(`Mesh routing error for ${s}/${n}: ${g.message}`),await this.adapterSend(e,{channel:t.channel,chatId:o,text:`Error from ${s}/${c.name}: ${g.message}`,replyTo:t.id,parseMode:"plain",accountId:a}),!0}}getAccountForAgent(e){for(let[t,n]of Object.entries(this.config.channels.telegram.accounts))if(n.agentBinding===e)return t}resolveAgent(e){if(e.channel==="gitlab")return e.resolvedAgent?(this.log(`GitLab agent resolved by adapter: ${e.resolvedAgent}`),e.resolvedAgent):void 0;let t=e.group?.id??e.sender.id,n=this.handoverStore.get(e.channel,t,e.accountId);if(n)return this.log(`[handover] ${e.channel}:${t}${e.accountId?`:${e.accountId}`:""} -> ${n.toAgent} (handover)`),n.toAgent;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 s=e.sender.isBot===!0;if(e.channel==="telegram"&&this.config.channels.telegram.policy.group==="mention-required"){let c=this.registry.findByMention(e.text,{atMentionsOnly:s});return c||void 0}let i=this.registry.findByMention(e.text,{atMentionsOnly:s});if(i)return i;if(!s)return e.channel==="telegram"?this.config.channels.telegram.accounts[e.accountId]?.agentBinding:this.config.channels.whatsapp.defaultAgent}},F=ve;J(F,"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
- `),n=[],s=!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(s){s=!1;let u=X(r.join(`
35
- `)),y=i?`// ${i}
36
- `:"";n.push(`<pre><code>${y}${u}</code></pre>`),i="";continue}else{s=!0,i=g.trimStart().slice(3).trim(),r=[];continue}if(s){r.push(g);continue}if(c&&!g.trimStart().startsWith(">")&&(n.push("</blockquote>"),c=!1),a&&!g.trim().startsWith("|")&&(a=!1,o=[]),g.trimStart().startsWith("> ")){let u=g.replace(/^>\s*/,"");c||(n.push("<blockquote>"),c=!0),n.push(x(u));continue}let m=g.match(/^(#{1,6})\s+(.+)$/);if(m){n.push(""),n.push(`<b>${x(m[2])}</b>`);continue}if(/^[-*_]{3,}\s*$/.test(g.trim())){n.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])}`)}n.push(`\u2022 ${y.join(" \u2014 ")}`)}else n.push(`\u2022 ${x(u[0])}`);continue}let p=g.match(/^(\s*)[-*+]\s+(.+)$/);if(p){let u=p[1].length>0?" ":"";n.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";n.push(`${u}${y}. ${x(f[2])}`);continue}if(!g.trim()){n.push("");continue}n.push(x(g))}s&&n.push(`<pre><code>${X(r.join(`
37
- `))}</code></pre>`),c&&n.push("</blockquote>");let l=n.join(`
38
- `).trim();return l=$t(l),l}function x(d){let e=X(d);return e=e.replace(/`([^`]+)`/g,"<code>$1</code>"),e=e.replace(/\[([^\]]+)\]\(([^)]+)\)/g,(t,n,s)=>`<a href="${X(s)}">${n}</a>`),e=e.replace(/\*\*\*(.+?)\*\*\*/g,"<b><i>$1</i></b>"),e=e.replace(/\*\*(.+?)\*\*/g,"<b>$1</b>"),e=e.replace(/(?<!\*)\*([^*]+?)\*(?!\*)/g,"<i>$1</i>"),e=e.replace(/~~(.+?)~~/g,"<s>$1</s>"),e=e.replace(/\|\|(.+?)\|\|/g,"<tg-spoiler>$1</tg-spoiler>"),e}var wt=/(?<=\w)\.(ts|js|py|rs|go|rb|cs|sh|md|yml|yaml|toml|json|env|css|html|xml|sql|tf|hcl)(?=[\s,;:)\]}<]|$)/gi;function $t(d){let e=d.split(/(<\/?(?:code|pre|a)[^>]*>)/gi),t=!1;return e.map(n=>/<(?:code|pre|a)\b/i.test(n)?(t=!0,n):/<\/(?:code|pre|a)>/i.test(n)?(t=!1,n):t?n:n.replace(wt,"<code>.$1</code>")).join("")}import{readFileSync as qe,writeFileSync as Xe,mkdirSync as Qe,existsSync as Se}from"fs";import{resolve as Ye,dirname as Ze}from"path";var Me=class{data={};filePath;dirty=!1;constructor(e){this.filePath=Ye(e,".agentx/telegram/groups.json"),this.load()}load(){try{Se(this.filePath)&&(this.data=JSON.parse(qe(this.filePath,"utf-8")))}catch{this.data={}}}save(){try{Qe(Ze(this.filePath),{recursive:!0}),Xe(this.filePath,JSON.stringify(this.data,null,2)),this.dirty=!1}catch{}}setBotStatus(e,t,n,s,i,r){this.data[e]||(this.data[e]={name:t,bots:{}}),t&&(this.data[e].name=t),this.data[e].bots[n]={username:i,agentId:s,status:r,updatedAt:new Date().toISOString()},this.dirty=!0,this.save()}getGroupBots(e){let t=this.data[e];return t?Object.entries(t.bots).filter(([,n])=>n.status!=="left"&&n.status!=="kicked").map(([n,s])=>({accountId:n,agentId:s.agentId,username:s.username})):[]}hasGroup(e){return!!this.data[e]}},Ie=class{offsets={};filePath;dirty=!1;saveTimer;constructor(e){this.filePath=Ye(e,".agentx/telegram/offsets.json");try{Se(this.filePath)&&(this.offsets=JSON.parse(qe(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=Ze(this.filePath);Se(e)||Qe(e,{recursive:!0}),Xe(this.filePath,JSON.stringify(this.offsets,null,2)),this.dirty=!1}catch{}}},Q=class{name="telegram";accounts;offsetStore;handler;polling=!1;log;globalAllowFrom;constructor(e,t={},n=console.error.bind(console,"[telegram]")){this.accounts=new Map(Object.entries(e)),this.globalAllowFrom=t.policy?.allowFrom,this.log=n,this.groupStore=new Me(process.cwd()),this.offsetStore=new Ie(process.cwd())}effectiveAllowFrom(e){let t=this.accounts.get(e);return t?.allowFrom!==void 0?t.allowFrom:this.globalAllowFrom}isAllowed(e,t,n,s){let i=this.effectiveAllowFrom(e);if(!i||i.length===0)return!1;let r=t!=null?String(t):"",c=n!=null?String(n):"",o=s?.toLowerCase();for(let a of i)if(a&&(a===r||a===c||a.startsWith("@")&&o&&a.slice(1).toLowerCase()===o))return!0;return!1}onMessage(e){this.handler=e}async start(){this.polling=!0;let e=Array.from(this.accounts.entries());this.log(`${e.length} Telegram account(s) to start`);for(let t=0;t<e.length;t++){let[n,s]=e[t];this.log(`Starting polling for account "${n}" (${t+1}/${e.length})`);try{await this.apiCall(s.token,"deleteWebhook",{drop_pending_updates:!1}).catch(()=>{});let i=await this.apiCall(s.token,"getMe"),r=i.result?.id,c=i.result?.username;this.log(`Bot @${c} ready (account: ${n})`),r&&this.botInfo.set(n,{userId:r,username:c||n}),this.pollLoop(n,s)}catch(i){this.log(`Failed to verify bot for account "${n}": ${i.message}`)}t<e.length-1&&await new Promise(i=>setTimeout(i,300))}this.log(`All ${e.length} Telegram account(s) started`)}async stop(){this.polling=!1,this.offsetStore.flush();let e=Array.from(this.accounts.entries()).map(async([t,n])=>{try{await this.apiCall(n.token,"getUpdates",{offset:-1,timeout:0})}catch{}});await Promise.allSettled(e)}getTokenForAccount(e){return this.accounts.get(e)?.token}getDefaultToken(){let[,e]=Array.from(this.accounts.entries())[0];return e?.token}resolveToken(e,t){if(t){let n=this.getTokenForAccount(t);if(n)return n}return this.chatAccountMap.get(e)?this.getTokenForAccount(this.chatAccountMap.get(e)):this.getDefaultToken()}chatAccountMap=new Map;botInfo=new Map;groupStore;async send(e){let t=this.resolveToken(e.chatId,e.accountId);if(!t)return this.log("No telegram token found for sending"),"";let n=4096,s=e.text.length>n?e.text.slice(0,n-3)+"...":e.text,i=e.parseMode==="markdown"||e.parseMode===void 0?Te(s):s,r={chat_id:e.chatId,text:i,parse_mode:"HTML"};e.replyTo&&(r.reply_to_message_id=parseInt(e.replyTo,10)),e.parseMode==="html"?(r.parse_mode="HTML",r.text=s):e.parseMode==="plain"&&(delete r.parse_mode,r.text=s);try{let 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=s;let o=await this.apiCall(t,"sendMessage",r);return String(o.result?.message_id||"")}throw c}}async editMessage(e,t,n,s,i){let r=this.resolveToken(e,i);if(!r)return!1;let c=4096,o=n.length>c?n.slice(0,c-3)+"...":n,a=s!=="html"&&s!=="plain"?Te(o):o,l={chat_id:e,message_id:parseInt(t,10),text:a,parse_mode:"HTML"};s==="html"?(l.parse_mode="HTML",l.text=o):s==="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,n="\u{1F440}",s){let i=this.resolveToken(e,s);if(i)try{await this.apiCall(i,"setMessageReaction",{chat_id:e,message_id:parseInt(t,10),reaction:[{type:"emoji",emoji:n}]})}catch{}}async sendTyping(e,t){let n=this.resolveToken(e,t);if(n)try{await this.apiCall(n,"sendChatAction",{chat_id:e,action:"typing"})}catch{}}async pollLoop(e,t){let n=0;for(;this.polling;)try{let s=this.offsetStore.get(e)||0,r=(await this.apiCall(t.token,"getUpdates",{offset:s||void 0,timeout:30,allowed_updates:["message","my_chat_member"]})).result||[];for(let 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:U,writeFileSync:W}=await import("fs"),{randomUUID:pe}=await import("crypto"),{resolve:C,join:R}=await import("path"),P=C(process.cwd(),".agentx/media/telegram");U(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;if(!this.isAllowed(e,o.from?.id,o.chat?.id,o.from?.username)){this.log(`[telegram/${e}] dropped message from ${o.from?.id}${o.from?.username?` (@${o.from.username})`:""} in chat ${o.chat?.id} \u2014 not in allowlist`);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}"`)}}n=0}catch(s){n++;let i=Math.min(5e3*Math.pow(2,n-1),6e4);this.log(`Poll error (${e}): ${s.message} [retry in ${i/1e3}s, errors: ${n}]`),await new Promise(r=>setTimeout(r,i))}}async getChannelMeta(e){let t=this.groupStore.getGroupBots(e);if(t.length)return{channel:"telegram",agents:t.map(n=>({id:n.agentId,name:n.username,handle:`@${n.username}`})),facts:[`${t.length} bot(s) verified in this group`]}}async seedGroupMembership(e,t){this.log(`Seeding group membership for "${t||e}"`);for(let[n,s]of this.accounts){let i=this.botInfo.get(n);if(i)try{let c=(await this.apiCall(s.token,"getChatMember",{chat_id:Number(e),user_id:i.userId})).result?.status;c&&this.groupStore.setBotStatus(e,t,n,s.agentBinding,i.username,c)}catch{this.groupStore.setBotStatus(e,t,n,s.agentBinding,i.username,"left")}}this.log(`Seeded: ${this.groupStore.getGroupBots(e).length} bot(s) in "${t||e}"`)}async apiCall(e,t,n){let s=`https://api.telegram.org/bot${e}/${t}`,i=await fetch(s,{method:"POST",headers:{"Content-Type":"application/json"},body:n?JSON.stringify(n):void 0});if(!i.ok){let r=await i.text();throw new Error(`Telegram API error: ${i.status} ${r}`)}return i.json()}};import{mkdirSync as et,writeFileSync as vt}from"fs";import{resolve as tt,join as Tt}from"path";import{randomUUID as St}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=tt(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,n){for(let s of this.routes){if(s.contact){let i=s.contact.replace(/\+/g,"");if(e.includes(i)||i.includes(e))return s.agent}if(s.group&&(t||n)){let i=s.group.toLowerCase();if(t?.toLowerCase().includes(i)||n?.toLowerCase().includes(i))return s.agent}}return this.defaultAgent}onMessage(e){this.handler=e}async start(){let e,t,n,s;try{s=await import("@whiskeysockets/baileys"),e=s.default||s.makeWASocket,t=s.useMultiFileAuthState,n=s.DisconnectReason}catch{this.log("WhatsApp requires @whiskeysockets/baileys. Install with:"),this.log(" npm install @whiskeysockets/baileys");return}et(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 s.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:s.makeCacheableSignalKeyStore?s.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===n?.loggedOut||p===401)this.log("Logged out. Delete session dir and restart to re-scan QR.");else if(p===440||p===408){if(this.reconnectAttempts++,this.reconnectAttempts>=5){this.log(`WhatsApp: giving up after ${this.reconnectAttempts} conflict attempts. Another session is active for this number \u2014 close it or re-scan QR.`);return}let 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 U=this.resolveAgent(M,_,S?w:void 0);if(!U){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=tt(this.sessionDir,"../media/inbound");et(O,{recursive:!0});let ue=f.documentMessage?.fileName||`${St()}.${E}`,me=Tt(O,ue);vt(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:U};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 s=(await this.sock.sendMessage(t,{text:e.text}))?.key?.id||"";return s&&this.sentMessageIds.add(s),s}catch(n){return this.log(`Send error: ${n.message}`),""}}async editMessage(e,t,n){if(!this.sock)return!1;let s=e.includes("@")?e:`${e}@s.whatsapp.net`;try{return await this.sock.sendMessage(s,{text:n,edit:{remoteJid:s,id:t,fromMe:!0}}),!0}catch{return!1}}async sendTyping(e){if(!this.sock)return;let t=e.includes("@")?e:`${e}@s.whatsapp.net`;try{await this.sock.sendPresenceUpdate("composing",t)}catch{}}async react(e,t,n="\u{1F440}"){if(!this.sock)return;let s=e.includes("@")?e:`${e}@s.whatsapp.net`;try{await this.sock.sendMessage(s,{react:{text:n,key:{remoteJid:s,id:t}}})}catch{}}};ct();import{createServer as _t}from"http";import{writeFileSync as Rt,existsSync as rt,unlinkSync as Ot,mkdirSync as Dt,watch as jt}from"fs";import{resolve as Re,dirname as Et}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:n}=e;this.client=new t({intents:[n.Guilds,n.GuildMessages,n.MessageContent,n.DirectMessages]}),this.client.on("ready",()=>{this.log(`Discord connected as ${this.client.user?.tag}`)}),this.client.on("messageCreate",async s=>{if(!this.handler||s.author.bot)return;let i=s.mentions.users.has(this.client.user?.id),r=!s.guild;if(!i&&!r)return;let c=s.content;if(this.client.user&&(c=c.replace(new RegExp(`<@!?${this.client.user.id}>`,"g"),"").trim()),!c)return;let o={id:s.id,channel:"discord",accountId:"default",sender:{id:s.author.id,name:s.author.displayName||s.author.username,username:s.author.username},group:s.guild?{id:s.channelId,name:s.channel?.name||s.channelId}:void 0,text:c,replyTo:s.reference?.messageId,timestamp:s.createdAt,raw:s};this.handler(o).catch(a=>{this.log(`Error handling message: ${a.message}`)})});try{await this.client.login(this.token)}catch(s){this.log(`Discord login failed: ${s.message}`)}}async stop(){this.client&&(this.client.destroy(),this.client=null)}async send(e){if(!this.client)return"";try{let t=await this.client.channels.fetch(e.chatId);return t?.isTextBased()?(await t.send({content:e.text,...e.replyTo?{reply:{messageReference:e.replyTo}}:{}})).id:""}catch(t){return this.log(`Send error: ${t.message}`),""}}async editMessage(e,t,n){if(!this.client)return!1;try{let s=await this.client.channels.fetch(e);return s?.isTextBased()?(await(await s.messages.fetch(t)).edit(n),!0):!1}catch{return!1}}async sendTyping(e){if(this.client)try{let t=await this.client.channels.fetch(e);t?.isTextBased()&&await t.sendTyping()}catch{}}async react(e,t,n="\u{1F440}"){if(this.client)try{let s=await this.client.channels.fetch(e);if(!s?.isTextBased())return;await(await s.messages.fetch(t)).react(n)}catch{}}};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:n}=e,{WebClient:s}=t;this.web=new s(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 n({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,n){if(!this.web)return!1;try{return await this.web.chat.update({channel:e,ts:t,text:n}),!0}catch(s){return this.log(`Edit error: ${s.message}`),!1}}async react(e,t,n="eyes"){if(this.web)try{let s=n.replace(/^:|:$/g,"");await this.web.reactions.add({channel:e,timestamp:t,name:s})}catch{}}};import{createServer as Mt}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]"),n){this.config=e,this.log=t,this.hooks=n}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 n of this.config.agentMappings||[])n.token&&e.push({label:n.agentId,token:n.token});let t=await Promise.allSettled(e.map(async({label:n,token:s})=>{let i=await fetch(`${this.config.host}/api/v4/user`,{headers:{"PRIVATE-TOKEN":s}});if(!i.ok)throw new Error(`${i.status}`);let r=await i.json();return{label:n,username:r.username}}));for(let n of t)if(n.status==="fulfilled"){let{label:s,username:i}=n.value;this.botUsernames.add(i),s==="global"?(this.botUsername=i,this.log(`Global bot user: ${i}`)):(this.usernameToAgent.set(i.toLowerCase(),s),this.agentToUsername.set(s,i.toLowerCase()),this.log(`Agent "${s}" -> GitLab user @${i}`))}for(let n of this.config.agentMappings||[]){for(let s of n.gitlabUsernames)this.botUsernames.add(s),(!this.usernameToAgent.has(s.toLowerCase())||n.node)&&this.usernameToAgent.set(s.toLowerCase(),n.agentId);!this.agentToUsername.has(n.agentId)&&n.gitlabUsernames.length>0&&this.agentToUsername.set(n.agentId,n.gitlabUsernames[0].toLowerCase())}this.log(`Bot users (${this.botUsernames.size}): ${[...this.botUsernames].join(", ")}`),this.log(`Username->Agent map: ${[...this.usernameToAgent.entries()].map(([n,s])=>`@${n}->${s}`).join(", ")}`),this.server=Mt(async(n,s)=>{n.method==="POST"?await this.handleWebhook(n,s):(s.writeHead(200,{"Content-Type":"text/plain"}),s.end("GitLab webhook endpoint. POST events here."))}),this.server.on("error",n=>{n.code==="EADDRINUSE"?(this.log(`GitLab webhook port ${this.config.webhookPort} in use, retrying in 5s...`),setTimeout(()=>{this.server?.close(),this.server?.listen(this.config.webhookPort)},5e3)):this.log(`GitLab webhook server error: ${n.message}`)}),this.server.listen(this.config.webhookPort,()=>{this.log(`GitLab webhook listening on :${this.config.webhookPort}`)})}async stop(){this.server&&this.server.close()}async send(e){let t=e.chatId.split(":");if(t.length<3)return this.log(`Invalid chatId for GitLab reply: ${e.chatId}`),"";let n=t.pop(),s=t.pop(),i=t.join(":"),r=encodeURIComponent(i),c;switch(s){case"issue":c=`${this.config.host}/api/v4/projects/${r}/issues/${n}/notes`;break;case"merge_request":c=`${this.config.host}/api/v4/projects/${r}/merge_requests/${n}/notes`;break;default:return this.log(`Unsupported noteable type: ${s}`),""}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,s,n,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 s=await this.readBody(e),i=s.object_kind||s.event_type||"unknown";switch(this.log(`Event: ${i} from ${s.project?.path_with_namespace||"unknown"}`),i){case"note":await this.handleNote(s,t);break;case"issue":await this.handleIssue(s,t);break;case"merge_request":await this.handleMR(s,t);break;case"pipeline":await this.handlePipeline(s,t);break;default:this.log(`Unhandled event: ${i}`),t.writeHead(200),t.end("ok")}}async handleNote(e,t){if(!this.handler){t.writeHead(200),t.end("ok");return}let n=e.object_attributes.note,s=e.project.path_with_namespace,i=e.user,r=String(e.object_attributes.id),c=n.match(/<!-- agentx:(\S+) -->/);if(c){let w=c[1],S=n.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=n.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(n);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(s,a,l,e.object_attributes.id,f,u,m).catch(()=>{});let y=`${s}:${a}:${l}`,b=await this.getChannelMeta(y),$=n.replace(/\n*<!-- agentx:\S+ -->/g,""),v=await this.downloadNoteImages($,s,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 n=e.object_attributes,s=e.project.path_with_namespace,i=this.resolveAgent(s),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:s,iid:n.iid,title:n.title,description:n.description||"",url:n.url,action:n.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=`${s}:issue:${n.iid}`,g=await this.getChannelMeta(h),p={id:`issue-hook-${n.iid}-${a.agentId}${a.assignee?`-${a.assignee}`:""}-${n.action}`,channel:"gitlab",accountId:"default",sender:{id:h,name:e.user.name,username:e.user.username},text:a.prompt||`[GitLab Issue #${n.iid} ${n.action}]: ${n.title}
45
- ${n.description?.slice(0,500)||""}
46
- URL: ${n.url}`,timestamp:new Date,raw:e,resolvedAgent:a.agentId,preferNode:a.preferNode||l?.node,channelMeta:g?{...g,issue:{type:"issue",iid:String(n.iid),title:n.title}}:void 0};this.log(`Issue #${n.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 #${n.iid}: on:gitlab-issue hook suppressed default dispatch`),t.writeHead(200),t.end("ok");return}let o={id:`issue-${n.iid}-${n.action}`,channel:"gitlab",accountId:"default",sender:{id:`${s}:issue:${n.iid}`,name:e.user.name,username:e.user.username},text:`[GitLab Issue #${n.iid} ${n.action}]: ${n.title}
47
- ${n.description?.slice(0,500)||""}
48
- URL: ${n.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 n=e.object_attributes,s=e.project.path_with_namespace,i=this.resolveAgent(s),r={id:`mr-${n.iid}-${n.action}`,channel:"gitlab",accountId:"default",sender:{id:`${s}:merge_request:${n.iid}`,name:e.user.name,username:e.user.username},text:`[GitLab MR !${n.iid} ${n.action}]: ${n.title}
49
- Branch: ${n.source_branch} -> ${n.target_branch}
50
- ${n.description?.slice(0,500)||""}
51
- URL: ${n.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 n=e.object_attributes,s=e.project.path_with_namespace;if(["success","failed","canceled"].includes(n.status)&&this.hooks?.has("on:gitlab-pipeline")&&this.hooks.execute("on:gitlab-pipeline",{event:"on:gitlab-pipeline",pipelineId:n.id,status:n.status,ref:n.ref,duration:n.duration,project:s,projectId:e.project?.id,raw:e}).catch(o=>this.log(`on:gitlab-pipeline hook error: ${o.message}`)),!this.handler||n.status!=="failed"){t.writeHead(200),t.end("ok");return}let r=this.resolveAgent(s),c={id:`pipeline-${n.id}`,channel:"gitlab",accountId:"default",sender:{id:`${s}:pipeline:${n.id}`,name:e.user.name,username:e.user.username},text:`[GitLab Pipeline FAILED] Project: ${s}
52
- Ref: ${n.ref}
53
- Duration: ${n.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(n=>n.slice(1).replace(/[.]+$/,"").toLowerCase())||[];if(t.length!==0)for(let n of t){let s=this.usernameToAgent.get(n);if(s)return this.log(`Mention @${n} -> agent "${s}" (resolved from token)`),s}}resolveAgent(e){for(let t of this.config.routes)if(t.project===e||t.project==="*")return t.agent}async getChannelMeta(e){let t=e.split(":"),n=t[0],s=t[1],i=t[2],r=[];for(let o of this.config.routes)(o.project===n||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:n,issue:s&&i?{type:s,iid:i,title:""}:void 0,facts:["This is a GitLab webhook event \u2014 respond as a GitLab comment","Do NOT use Telegram handles or delegate to other agents","Do NOT mention other agents by name \u2014 you are the only agent responding to this event","Stay in your role \u2014 do not act as or speak for other agents"]}}isBotUser(e){return this.botUsernames.has(e)}getAgentToken(e){return!e||!this.config.agentMappings?.length?void 0:this.config.agentMappings.find(n=>n.agentId===e)?.token}getTokenForMentionedAgent(e){if(!this.config.agentMappings?.length)return;let t=e.match(/@(\w[\w.-]*)/g)?.map(n=>n.slice(1).replace(/[.]+$/,"").toLowerCase())||[];for(let n of this.config.agentMappings)if(n.token&&n.gitlabUsernames.some(s=>t.includes(s.toLowerCase())))return n.token}async reactToNote(e,t,n,s,i,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,n,s,c);return}if(!i){this.log(`No token for reaction on note ${s} (agent "${c}") \u2014 skipping`);return}let o=encodeURIComponent(e),a;switch(t){case"issue":a=`${this.config.host}/api/v4/projects/${o}/issues/${n}/notes/${s}/award_emoji`;break;case"merge_request":a=`${this.config.host}/api/v4/projects/${o}/merge_requests/${n}/notes/${s}/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 ${s}: ${l.status}`)}catch(l){this.log(`Failed to react to note ${s}: ${l.message}`)}}async logTimeSpent(e,t,n){let s=e.split(":");if(s.length<3)return;let i=s.pop(),r=s.pop(),c=s.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===n),u=this.getAgentToken(n);if(!u&&f?.node&&this.logTimeForwarder){try{await this.logTimeForwarder(f.node,c,r,i,n||"",t),this.log(`Time logged (via peer "${f.node}"): ${h} on ${c} ${m}/${i} (${n})`)}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} (${n||"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,n){let s=/!\[[^\]]*\]\(([^)]+\.(?:png|jpg|jpeg|gif|webp|svg))\)/gi,i=[...e.matchAll(s)];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":n}});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 n="";e.on("data",s=>n+=s.toString()),e.on("end",()=>{try{t(n?JSON.parse(n):{})}catch{t({raw:n})}}),e.on("error",()=>t({}))})}};var G=class{module;minLevel;constructor(e,t="info"){this.module=e,this.minLevel=t}child(e){return new G(`${this.module}:${e}`,this.minLevel)}debug(e,t){this.emit("debug",e,t)}info(e,t){this.emit("info",e,t)}warn(e,t){this.emit("warn",e,t)}error(e,t){this.emit("error",e,t)}asConsoleLog(){return(...e)=>{let n=e.map(s=>typeof s=="string"?s:JSON.stringify(s)).join(" ").replace(/^\[agentx\]\s*/,"");n&&this.info(n)}}emit(e,t,n){if(G.levelOrder[e]<G.levelOrder[this.minLevel])return;let s={time:new Date().toISOString(),level:e,module:this.module,msg:t,...n},i=e==="error"?"ERROR":e==="warn"?"WARN":"",r=`[${this.module}]`,c=i?`${r} ${i}: ${t}`:`${r} ${t}`;console.error(c)}},H=G;J(H,"levelOrder",{debug:0,info:1,warn:2,error:3});var ne=class{registry;config;log;constructor(e,t={},n=console.error.bind(console,"[webhook]")){this.registry=e,this.config=t,this.log=n}async handle(e,t,n){let s=n.replace(/^\/webhook\/?/,"").split("/").filter(Boolean),i=s[0],r=s[1];if(!i){this.sendJson(t,400,{error:"Missing agent ID. Use /webhook/:agentId"});return}let 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,n){let s=[`[Webhook from ${e}]`];switch(e){case"gitlab":{let r=n["x-gitlab-event"]||t.object_kind||"event",c=t.project?.path_with_namespace||"",o=t.user?.name||t.user_username||"";if(s.push(`Event: ${r}`),c&&s.push(`Project: ${c}`),o&&s.push(`User: ${o}`),t.ref&&s.push(`Ref: ${t.ref}`),t.commits&&Array.isArray(t.commits)){s.push(`Commits: ${t.commits.length}`);for(let l of t.commits.slice(0,3))s.push(` - ${l.message?.split(`
55
- `)[0]||"no message"} (${l.author?.name||""})`)}let a=t.object_attributes;if(a?.title&&(s.push(`Title: ${a.title}`),s.push(`State: ${a.state||""}`),s.push(`Action: ${a.action||""}`),a.source_branch&&s.push(`Branch: ${a.source_branch} -> ${a.target_branch}`),a.url&&s.push(`URL: ${a.url}`)),a?.iid&&!a?.source_branch&&(s.push(`Issue #${a.iid}: ${a.title||""}`),a.description&&s.push(`Description: ${a.description.slice(0,200)}`)),t.object_kind==="pipeline"){let l=t.object_attributes;s.push(`Pipeline: ${l?.status||""} (${l?.ref||""})`),s.push(`Duration: ${l?.duration||0}s`)}break}case"github":{let r=n["x-github-event"]||"event",c=t.repository?.full_name||"",o=t.sender?.login||"";if(s.push(`Event: ${r}`),c&&s.push(`Repository: ${c}`),o&&s.push(`Sender: ${o}`),t.ref&&s.push(`Ref: ${t.ref}`),t.commits&&Array.isArray(t.commits))for(let h of t.commits.slice(0,3))s.push(` - ${h.message?.split(`
56
- `)[0]||""} (${h.author?.name||""})`);let a=t.pull_request;a&&(s.push(`PR #${a.number}: ${a.title}`),s.push(`Action: ${t.action}`),s.push(`Branch: ${a.head?.ref} -> ${a.base?.ref}`));let l=t.issue;l&&(s.push(`Issue #${l.number}: ${l.title}`),s.push(`Action: ${t.action}`));break}case"stripe":{let r=t.type||"event",c=t.data?.object||{};s.push(`Event: ${r}`),c.amount&&s.push(`Amount: ${(c.amount/100).toFixed(2)} ${c.currency?.toUpperCase()||""}`),c.customer_email&&s.push(`Customer: ${c.customer_email}`),c.description&&s.push(`Description: ${c.description}`),c.status&&s.push(`Status: ${c.status}`);break}case"sentry":{let r=n["sentry-hook-resource"]||"event";s.push(`Resource: ${r}`);let c=t.data||t;c.error?.title&&s.push(`Error: ${c.error.title}`),c.error?.culprit&&s.push(`Culprit: ${c.error.culprit}`),c.error?.metadata?.value&&s.push(`Message: ${c.error.metadata.value}`),t.url&&s.push(`URL: ${t.url}`);break}default:s.push(`Headers: ${JSON.stringify(Object.keys(n).filter(r=>r.startsWith("x-")).slice(0,5))}`),s.push(`Payload keys: ${Object.keys(t).slice(0,10).join(", ")}`);let i=JSON.stringify(t).slice(0,500);s.push(`Body: ${i}`)}return s.join(`
57
- `)}async readBody(e){return new Promise(t=>{let n="";e.on("data",s=>n+=s.toString()),e.on("end",()=>{try{t(n?JSON.parse(n):{})}catch{t({raw:n})}}),e.on("error",()=>t({}))})}sendJson(e,t,n){e.writeHead(t,{"Content-Type":"application/json"}),e.end(JSON.stringify(n,null,2))}};var se=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,n)=>t.peer.localeCompare(n.peer)).map(t=>`${t.peer}:${t.skills.map(n=>n.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]"],n=this.config.agents[e];t.push(`Node: ${this.config.node.name} (${this.config.node.id})`);let s=this.getPrimaryHandle(e),i=this.extractCapability(n),r=s?` (${s})`:"";t.push(`You: ${n.name}${r} \u2014 ${i}`),t.push("");let 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=[s,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 n=t.telegram.policy;e.push(`telegram(groups:${n.group}, DMs:${n.dm}, delegation:yes)`)}return t.gitlab?.enabled&&e.push("gitlab(webhooks, no delegation)"),t.whatsapp.enabled&&e.push("whatsapp(DMs only, no delegation)"),t.discord?.enabled&&e.push("discord(enabled)"),`Channels: ${e.join(", ")}`}getPrimaryHandle(e){let t=this.config.agents[e];if(t)return t.mentions.find(n=>n.startsWith("@"))}extractCapability(e){if(!e.systemPrompt)return e.name;let t=e.systemPrompt.split(/[.\n]/)[0]?.trim()||e.name;return t.length>80?t.slice(0,77)+"...":t}};var ie=class{registry;memoryStore;states=new Map;log;constructor(e,t=console.error.bind(console,"[heartbeat]"),n){this.registry=e,this.log=t,this.memoryStore=n}register(e,t){if(!t.enabled)return;this.stop(e);let n={agentId:e,config:t,runCount:0,errors:0},s=(t.intervalMinutes||30)*60*1e3;n.timer=setInterval(()=>{this.runHeartbeat(n).catch(i=>{this.log(`[${e}] heartbeat error: ${i.message}`),n.errors++})},s),this.states.set(e,n),this.log(`Registered heartbeat for "${e}" every ${t.intervalMinutes}min`)}stop(e){let t=this.states.get(e);t?.timer&&(clearInterval(t.timer),t.timer=void 0),this.states.delete(e)}stopAll(){for(let[e]of this.states)this.stop(e)}async runHeartbeat(e){let{agentId:t,config:n}=e;this.log(`[${t}] heartbeat running...`),e.lastRun=new Date,e.runCount++;let s=n.channel||"heartbeat";try{let i=n.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:s,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[n,s]of Object.entries(e)){let i={def:{...s,id:n},patterns:[]};for(let r of s.triggers)try{i.patterns.push({regex:new RegExp(r.pattern,"i"),channel:r.channel})}catch(c){this.log(`Invalid regex in service "${n}": ${r.pattern} \u2014 ${c.message}`)}i.patterns.length>0&&this.services.push(i)}this.log(`${this.services.length} service(s) loaded`)}match(e,t,n){for(let s of this.services){if(s.def.allowedContacts?.length){let i=t.replace(/[^0-9]/g,"");if(!s.def.allowedContacts.some(c=>{let o=c.replace(/[^0-9]/g,"");return i.includes(o)||o.includes(i)}))continue}for(let i of s.patterns){if(i.channel&&i.channel!==n)continue;let r=e.match(i.regex);if(r)return j.cat("agent",`Service match: "${s.def.name}" (trigger: ${i.regex.source})`),{service:s.def,trigger:i.regex.source,captured:r[0]}}}return null}async execute(e,t,n,s){this.log(`Executing service "${e.name}" -> agent "${e.agent}"`);try{let i=await t.execute({message:e.prompt,agentId:e.agent,context:{channel:n.channel,sender:n.sender,chatId:`service:${e.id}:${n.chatId}`}});if(i.error){this.log(`Service "${e.name}" failed: ${i.error}`),await s(`Service "${e.name}" encountered an error: ${i.error}`);return}i.content&&await s(i.content),this.log(`Service "${e.name}" completed in ${i.duration}ms`)}catch(i){this.log(`Service "${e.name}" threw: ${i.message}`),await s(`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,n]of Object.entries(e.orgChart)){let s=e.roles[n.role];if(!s)throw new Error(`Org chart references unknown role "${n.role}" for agent "${t}"`);this.employees.set(t,{agentId:t,role:s,roleKey:n.role,reportsTo:n.reportsTo,schedule:n.schedule,utilizationTarget:n.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 n=new Set,s=t.agentId;for(;s;){if(n.has(s))throw new Error(`Reporting cycle detected involving "${t.agentId}"`);n.add(s),s=this.employees.get(s)?.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=[],n=e;for(;n&&!t.includes(n);)t.push(n),n=this.employees.get(n)?.reportsTo;return t}};var nt={sun:0,mon:1,tue:2,wed:3,thu:4,fri:5,sat:6};function Ae(d,e){let n=new Intl.DateTimeFormat("en-US",{timeZone:e,weekday:"short",hour:"2-digit",minute:"2-digit",hour12:!1}).formatToParts(d),s=(n.find(o=>o.type==="weekday")?.value||"sun").toLowerCase().slice(0,3),i=n.find(o=>o.type==="hour")?.value||"00",r=n.find(o=>o.type==="minute")?.value||"00";return{hour:parseInt(i,10)%24,minute:parseInt(r,10),dow:nt[s]??0}}function L(d){let[e,t]=d.split(":").map(Number);return e*60+t}function Ce(d,e){return d.days.some(t=>nt[t]===e)}function It(d,e,t){let{hour:n,minute:s,dow:i}=Ae(e,t);if(!Ce(d,i))return!1;let r=n*60+s,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 n=this.org.get(e);return n?It(n.schedule,t,this.config.timezone):!1}isDayStart(e,t=new Date){let n=this.org.get(e);if(!n)return!1;let{hour:s,minute:i,dow:r}=Ae(t,this.config.timezone);if(!Ce(n.schedule,r))return!1;let c=L(n.schedule.start);return s*60+i===c}isDayEnd(e,t=new Date){let n=this.org.get(e);if(!n)return!1;let{hour:s,minute:i,dow:r}=Ae(t,this.config.timezone);if(!Ce(n.schedule,r))return!1;let c=L(n.schedule.end);return s*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,n,s){this.router=e;this.org=t;this.config=n;this.log=s}async postToMain(e,t){let n=this.config.mainChannel;try{await this.router.sendOutbound({channel:n.channel,chatId:n.chatId,text:e,agentId:t,accountId:n.accountId})}catch(s){this.log(`[business] postToMain failed: ${s.message}`)}}async escalate(e,t,n){let s=this.org.chainOfCommand(e).slice(1),i=s.length?s.slice(0,1):[],r=`\u26A0\uFE0F **Escalation from ${e}** \u2014 ${t}
61
-
62
- ${n}`;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 At,existsSync as st,mkdirSync as Ct,readFileSync as Pt}from"fs";import{resolve as Pe}from"path";function xe(){return new Date().toISOString().slice(0,10)}var le=class{constructor(e,t,n){this.org=e;this.schedule=t;this.log=n;this.dir=Pe(process.cwd(),".agentx/kpi"),st(this.dir)||Ct(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(Pt(e,"utf-8"))}catch{}let t={};for(let n of this.org.all())t[n.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 n=this.onClockTick.get(t.agentId);if(n){let s=Math.min(e-n,12e4)/1e3,i=this.stats.perAgent[t.agentId];i&&(i.onClockSeconds+=s)}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{At(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,n=0;for(let[i,r]of Object.entries(this.stats.perAgent)){t+=r.timeLoggedSeconds,n+=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 s=n>0?(t/n*100).toFixed(0):"0";return e.push(`**Team:** ${(t/3600).toFixed(1)}h logged / ${(n/3600).toFixed(1)}h on-clock \u2192 ${s}% 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 n=[];d.on("data",s=>n.push(s)),d.on("end",()=>{let s=Buffer.concat(n).toString("utf-8");if(!s)return e({});try{e(JSON.parse(s))}catch(i){t(i)}}),d.on("error",t)})}async function it(d,e,t,n){if(d==="GET /business/status"){let s=new Date,i=n.schedule.clockedInAgents(s);return I(t,200,{time:s.toISOString(),onClock:i,employees:n.org.all().map(r=>({agentId:r.agentId,role:r.role.title,reportsTo:r.reportsTo,onClock:i.includes(r.agentId)})),kpi:n.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 n.workSource.listOpen(i);I(t,200,{items:r})}catch(r){I(t,500,{error:r.message})}return!0}if(d==="POST /business/claim"){let s=await he(e),{agent:i,itemId:r}=s;if(!i||!r)return I(t,400,{error:"agent and itemId required"}),!0;try{await n.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 s=await he(e);if(!s.itemId||!s.status)return I(t,400,{error:"itemId and status required"}),!0;if(!["in-progress","done","blocked"].includes(s.status))return I(t,400,{error:"status must be in-progress | done | blocked"}),!0;try{await n.workSource.report(s.itemId,{status:s.status,note:s.note,timeSeconds:s.timeSeconds,blocker:s.blocker}),I(t,200,{ok:!0})}catch(i){I(t,500,{error:i.message})}return!0}if(d==="POST /business/escalate"){let s=await he(e);return!s.agent||!s.subject?(I(t,400,{error:"agent and subject required"}),!0):(await n.reporter.escalate(s.agent,s.subject,s.detail||""),n.kpi.recordBlocker(s.agent),I(t,200,{ok:!0}),!0)}if(d==="POST /business/post"){let s=await he(e);return s.text?(await n.reporter.postToMain(s.text,s.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 xt=6e4,ge=class{constructor(e,t,n,s,i,r,c,o,a){this.config=e;this.org=t;this.schedule=n;this.registry=s;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}`))},xt)}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 n of this.day.keys())this.day.set(n,{})}this.kpi.tick();for(let n of this.org.all()){let s=this.day.get(n.agentId)||{};if(this.day.set(n.agentId,s),!this.runningTask.has(n.agentId)){if(this.schedule.isDayStart(n.agentId,e)&&!s.standupFiredAt){s.standupFiredAt=Date.now(),this.fireStandup(n.agentId).catch(i=>this.log(`[business] standup err: ${i.message}`));continue}if(this.schedule.isDayEnd(n.agentId,e)&&!s.wrapFiredAt){s.wrapFiredAt=Date.now(),this.fireWrap(n.agentId).catch(i=>this.log(`[business] wrap err: ${i.message}`));continue}this.schedule.isOnClock(n.agentId,e)&&(s.lastWorkTickAt?(Date.now()-s.lastWorkTickAt)/6e4:1/0)>=this.config.workTickMinutes&&(s.lastWorkTickAt=Date.now(),this.fireWorkTick(n.agentId).catch(r=>this.log(`[business] work-tick err: ${r.message}`)))}}}roleContext(e){let t=this.org.get(e);if(!t)return"";let n=t.reportsTo?`reporting to ${t.reportsTo}`:"(no manager \u2014 top of org chart)",s=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} ${n}.${s}${i}
91
-
92
- ${_e(this.daemonBase)}`}async runLifecycle(e,t){if(this.runningTask.has(e))return;this.runningTask.add(e);let n=Date.now();try{let s=await this.registry.execute({message:t,agentId:e,context:{channel:"business",chatId:`day-cycle:${this.currentDate}:${e}`}}),i=Math.round((s.duration??Date.now()-n)/1e3);return s.error||this.kpi.recordTaskCompletion({agentId:e,durationSeconds:i,success:!0,channel:"business"}),s.content}finally{this.runningTask.delete(e)}}async fireStandup(e){this.log(`[business] STANDUP \u2192 ${e}`);let n=`${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,n)){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 s=`${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,s)){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 s=`${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,s)}};var de=class{org;schedule;workSource;reporter;kpi;dayCycle;constructor(e,t,n,s,i){this.org=new oe(e),this.schedule=new ae(this.org,e),this.workSource=He(e,t,i),this.reporter=new ce(s,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,n,this.reporter,this.kpi,this.workSource,o,i)}start(){this.dayCycle.start()}stop(){this.dayCycle.stop()}recordTaskCompletion(e,t,n,s){this.kpi.recordTaskCompletion({agentId:e,durationSeconds:t,success:n,channel:s})}async handleHttp(e,t,n){let s={workSource:this.workSource,org:this.org,schedule:this.schedule,reporter:this.reporter,kpi:this.kpi};return it(e,t,n,s)}summary(){let e=this.schedule.clockedInAgents().length;return`Business layer: ${this.org.all().length} employees, ${e} on-clock now`}};var ot=class{config;registry;router;cron;mesh;hooks;landscape;heartbeat;business;httpServer;webhooks;log;sseClients=new Set;configPath;configWatcher;reloadTimer;constructor(e){let n=new H("agentx").asConsoleLog();this.log=(...r)=>{n(...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 s=Be(this.config);for(let r of s)this.log(` \u26A0 ${r}`);let[,i]=this.config.node.bind.split(":");Fe(this.config.agents,i||"19900",this.log),this.hooks=new De,je(process.cwd(),this.hooks),this.landscape=new se(this.config),this.registry=new Ne(this.config,this.log),Le(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 F(this.registry,this.config,this.hooks,this.log),this.webhooks=new ne(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 n=Re(process.cwd(),".agentx/daemon.pid");Dt(Et(n),{recursive:!0}),Rt(n,String(process.pid)),this.log(` PID: ${process.pid} (${n})`),process.on("uncaughtException",r=>{this.log(`UNCAUGHT EXCEPTION: ${r.message}`),this.log(r.stack||"")}),process.on("unhandledRejection",r=>{this.log(`UNHANDLED REJECTION: ${r}`)});let s=!1,i=async r=>{s||(s=!0,this.log(`
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");rt(t)&&Ot(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(rt(e))try{this.configWatcher=jt(e,{persistent:!1},t=>{t==="change"&&(this.reloadTimer&&clearTimeout(this.reloadTimer),this.reloadTimer=setTimeout(()=>{this.reload().catch(n=>this.log(`[reload] failed: ${n?.message||n}`))},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=[],n=[];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 s=["agents","channels","mesh","providers","node","services"];for(let i of s)JSON.stringify(this.config[i])!==JSON.stringify(e[i])&&n.push(String(i));return this.config=e,t.length&&this.log(`[reload] applied: ${t.join(", ")}`),n.length?(this.log(`[reload] restart required to apply changes in: ${n.join(", ")}`),this.broadcastSSE("reload-partial",JSON.stringify({applied:t,restartRequired:n}))):t.length&&this.broadcastSSE("reload-complete",JSON.stringify({applied:t})),{applied:t,restartRequired:n}}scheduleMidnightHook(){let e=()=>{let t=new Date,n=t.toLocaleString("en-US",{timeZone:"Africa/Tunis"}),s=new Date(n),i=new Date(s);i.setDate(i.getDate()+1),i.setHours(0,0,5,0);let r=s.getTime()-t.getTime(),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(),n=t.generateDailyReport(e);if(!n||n.totalTasks===0){this.log(` Cost tracking: no usage for ${e}, skipping`);return}t.appendToTokenCosts(n),this.log(` Cost tracking: ${e} \u2014 ${n.totalTasks} tasks, $${n.totalCost.toFixed(4)} (top: ${n.topAgent} $${n.topCost.toFixed(4)})`)}async startChannels(){if(this.config.channels.telegram.enabled){let e=this.config.channels.telegram.accounts;if(Object.keys(e).length>0){let t=this.config.channels.telegram.policy,n=new Q(e,{policy:{allowFrom:t?.allowFrom}},this.log);this.router.addChannel(n);let s=t?.allowFrom?.length??0,i=Object.entries(e).filter(([,r])=>!r.allowFrom&&s===0).length;i>0?this.log(` Telegram: enabled \u2014 WARNING: ${i} account(s) have no allowFrom (global or per-account). All incoming messages will be DROPPED.`):this.log(` Telegram: enabled \u2014 global allowFrom entries: ${s}`)}}if(this.config.channels.whatsapp.enabled){let{setWhatsAppQR:e,setWhatsAppStatus:t}=await import("./whatsapp-state-6D6FHHD2.js"),n=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(n),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,n,s,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:n,noteableType:s,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,n,s,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:n,noteableType:s,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,n,s,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:n,noteableType:s,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(":"),n=parseInt(t||"18800",10);this.httpServer=_t(async(s,i)=>{if(i.setHeader("Access-Control-Allow-Origin","*"),i.setHeader("Access-Control-Allow-Methods","GET, POST, OPTIONS"),i.setHeader("Access-Control-Allow-Headers","Content-Type, Authorization"),s.method==="OPTIONS"){i.writeHead(204),i.end();return}await this.handleHttp(s,i)}),this.httpServer.on("error",s=>{s.code==="EADDRINUSE"?(this.log(` ERROR: Port ${n} is already in use. Retrying in 5s...`),setTimeout(()=>{this.httpServer?.close(),this.httpServer?.listen(n,e||"0.0.0.0")},5e3)):this.log(` HTTP error: ${s.message}`)}),this.httpServer.listen(n,e||"0.0.0.0",()=>{this.log(` HTTP API: http://${e||"0.0.0.0"}:${n}`)})}broadcastSSE(e,t){if(this.sseClients.size===0)return;let n=`event: ${e}
117
- data: ${JSON.stringify({time:new Date().toISOString(),message:t})}
118
-
119
- `;for(let s of this.sseClients)try{s.write(n)}catch{this.sseClients.delete(s)}}handleSSE(e,t){t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","Access-Control-Allow-Origin":"*"});let n=this.registry.list(),s=n.filter(i=>i.active>0);t.write(`event: status
120
- data: ${JSON.stringify({node:this.config.node.name,agents:n.length,active:s.map(i=>({id:i.id,name:i.name,tasks:i.active})),mesh:this.mesh?.directory().map(i=>({peer:i.peer,healthy:i.healthy}))||[]})}
121
-
122
- `),this.sseClients.add(t),e.on("close",()=>this.sseClients.delete(t))}handleTaskStream(e,t,n,s){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(s,a=>r("chunk",{text:a}));if(!c){r("error",{message:"task not found or already evicted"}),t.end();return}if(r("start",{taskId:s,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 n=new URL(e.url||"/",`http://${e.headers.host||"localhost"}`),s=n.pathname;try{if(e.method==="GET"&&s==="/events"){this.handleSSE(e,t);return}if(e.method==="POST"&&s.startsWith("/webhook/")){await this.webhooks.handle(e,t,s);return}let i=e.method==="GET"&&s.match(/^\/agents\/([^/]+)\/tasks\/([^/]+)\/stream$/);if(i){this.handleTaskStream(e,t,i[1],i[2]);return}let r=e.method==="GET"&&s.match(/^\/agents\/([^/]+)\/tasks$/);if(r){let o=Math.max(1,Math.min(500,parseInt(n.searchParams.get("limit")||"50",10)||50));this.json(t,200,this.registry.listTaskHistory(r[1],o));return}let c=e.method==="GET"&&s.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"&&(s==="/v1/chat/completions"||s.match(/^\/llm\/[^/]+\/v1\/chat\/completions$/))){await this.handleOpenAICompat(e,t,s);return}if(this.business&&s.startsWith("/business")&&await this.business.handleHttp(`${e.method} ${s}`,e,t))return;switch(`${e.method} ${s}`){case"GET /health":this.json(t,200,{status:"ok",node:this.config.node,uptime:process.uptime(),agents:this.registry.list(),crons:this.cron.list().map(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=n.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=Lt(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=n.searchParams.get("agent")||void 0,l=n.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=n.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=n.searchParams.get("agent"),l=n.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,n){let s=await D(e),i=n.match(/^\/llm\/([^/]+)\//),r=Object.keys(this.config.agents)[0]||"default",c=i?.[1]||s.model||r,o=s.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(s.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,n){e.writeHead(t,{"Content-Type":"application/json"}),e.end(JSON.stringify(n,null,2))}};function Lt(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 n="";d.on("data",s=>n+=s.toString()),d.on("end",()=>{try{e(n?JSON.parse(n):{})}catch{e({})}}),d.on("error",t)})}import Oe from"path";import at from"fs-extra";function ds(){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(at.existsSync(e)){let t=at.readJSONSync(e);if(t&&t.name)return t}}catch{}return{name:"agentix-cli",version:"0.0.0"}}export{z as a,K as b,F as c,Q as d,Y as e,ot as f,ds as g};
138
- //# sourceMappingURL=chunk-K7BE4E35.js.map