@toolpack-sdk/agents 2.1.1 → 2.3.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 (32) hide show
  1. package/README.md +72 -4
  2. package/dist/{base-agent-DPdK4Pnl.d.cts → base-agent-65162dq7.d.cts} +1 -1
  3. package/dist/{base-agent-nU8pr4nu.d.ts → base-agent-DzspMyaG.d.ts} +1 -1
  4. package/dist/capabilities/index.cjs +2 -2
  5. package/dist/capabilities/index.d.cts +3 -3
  6. package/dist/capabilities/index.d.ts +3 -3
  7. package/dist/capabilities/index.js +3 -3
  8. package/dist/channels/index.cjs +2 -2
  9. package/dist/channels/index.d.cts +2 -2
  10. package/dist/channels/index.d.ts +2 -2
  11. package/dist/channels/index.js +2 -2
  12. package/dist/eval-report-BUD6NRiP.d.cts +343 -0
  13. package/dist/eval-report-C6dSvR3Y.d.ts +343 -0
  14. package/dist/{index-Du6S0eG7.d.cts → index-BxUlu-qG.d.ts} +76 -2
  15. package/dist/{index-o8Lbzv5N.d.ts → index-DrigwC1A.d.cts} +76 -2
  16. package/dist/index.cjs +40 -26
  17. package/dist/index.d.cts +9 -8
  18. package/dist/index.d.ts +9 -8
  19. package/dist/index.js +40 -26
  20. package/dist/{intent-classifier-agent-DxyfJWcm.d.cts → intent-classifier-agent-D0rWtviD.d.cts} +2 -2
  21. package/dist/{intent-classifier-agent-0JZDlhpk.d.ts → intent-classifier-agent-mmNoAozf.d.ts} +2 -2
  22. package/dist/interceptors/index.cjs +1 -1
  23. package/dist/interceptors/index.d.cts +92 -5
  24. package/dist/interceptors/index.d.ts +92 -5
  25. package/dist/interceptors/index.js +1 -1
  26. package/dist/testing/index.cjs +16 -2
  27. package/dist/testing/index.d.cts +3 -2
  28. package/dist/testing/index.d.ts +3 -2
  29. package/dist/testing/index.js +16 -2
  30. package/dist/{types-TB6yypig.d.cts → types-C3eW-auY.d.cts} +6 -8
  31. package/dist/{types-TB6yypig.d.ts → types-C3eW-auY.d.ts} +6 -8
  32. package/package.json +18 -9
@@ -1,3 +1,3 @@
1
- "use strict";var M=Object.create;var C=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var E=Object.getOwnPropertyNames;var R=Object.getPrototypeOf,N=Object.prototype.hasOwnProperty;var O=(a,e)=>{for(var n in e)C(a,n,{get:e[n],enumerable:!0})},_=(a,e,n,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of E(e))!N.call(a,r)&&r!==n&&C(a,r,{get:()=>e[r],enumerable:!(t=D(e,r))||t.enumerable});return a};var p=(a,e,n)=>(n=a!=null?M(R(a)):{},_(e||!a||!a.__esModule?C(n,"default",{value:a,enumerable:!0}):n,a)),U=a=>_(C({},"__esModule",{value:!0}),a);var z={};O(z,{BaseChannel:()=>d,DiscordChannel:()=>I,EmailChannel:()=>T,SMSChannel:()=>P,ScheduledChannel:()=>S,SlackChannel:()=>v,TelegramChannel:()=>b,WebhookChannel:()=>y});module.exports=U(z);var d=class{name;_handler;onMessage(e){this._handler=e}async handleMessage(e){this._handler&&await this._handler(e)}};var k=require("crypto");var v=class extends d{isTriggerChannel=!1;config;server;participantCache=new Map;botUserId;allowedChannels;constructor(e){super(),this.config={port:3e3,...e},this.name=e.name;let n=e.channel;this.allowedChannels=n==null?null:Array.isArray(n)?n:[n]}listen(){typeof process<"u"&&import("http").then(e=>{this.server=e.createServer((n,t)=>{this.handleRequest(n,t)}),this.server.listen(this.config.port,()=>{console.log(`[SlackChannel] Listening on port ${this.config.port}`),this.runStartupCheck().catch(()=>{})})}).catch(e=>{console.error("[SlackChannel] Failed to start HTTP server:",e)})}async runStartupCheck(){try{let n=await(await fetch("https://slack.com/api/auth.test",{method:"POST",headers:{Authorization:`Bearer ${this.config.token}`,"Content-Type":"application/json"}})).json();n.ok?(this.botUserId=n.user_id,console.log(`[SlackChannel] Connected as @${n.user} (${n.user_id}) in workspace "${n.team}" \u2014 ${n.url}`)):console.warn(`[SlackChannel] auth.test failed: ${n.error}. Check your bot token.`)}catch(e){console.warn("[SlackChannel] Startup self-check failed (network error):",e)}}verifySignature(e,n){let t=e["x-slack-request-timestamp"],r=e["x-slack-signature"];if(!t||!r||Array.isArray(t)||Array.isArray(r))return!1;let i=parseInt(t,10),s=Math.floor(Date.now()/1e3);if(isNaN(i)||Math.abs(s-i)>300)return!1;let o=`v0:${t}:${n}`,c=`v0=${(0,k.createHmac)("sha256",this.config.signingSecret).update(o).digest("hex")}`;if(c.length!==r.length)return!1;try{return(0,k.timingSafeEqual)(Buffer.from(c),Buffer.from(r))}catch{return!1}}async send(e){let n=e.metadata?.threadTs??e.metadata?.thread_ts??e.metadata?.threadId,r=e.metadata?.channelId??(this.allowedChannels&&this.allowedChannels.length>0?this.allowedChannels[0]:void 0);if(!r)throw new Error("[SlackChannel] Cannot send: no channel configured and metadata.channelId is missing. Provide a target via SlackChannelConfig.channel or output.metadata.channelId.");let i={channel:r,text:e.output};n&&(i.thread_ts=n);let s=await fetch("https://slack.com/api/chat.postMessage",{method:"POST",headers:{Authorization:`Bearer ${this.config.token}`,"Content-Type":"application/json"},body:JSON.stringify(i)});if(!s.ok)throw new Error(`Failed to send Slack message: ${s.statusText}`);let o=await s.json();if(!o.ok)throw new Error(`Slack API error: ${o.error}`)}normalize(e){let n=e,r=(n.text||"").replace(/<(https?:\/\/[^|>]+)\|([^>]+)>/g,"$2 ($1)").replace(/<(https?:\/\/[^>]+)>/g,"$1").replace(/<!here>/g,"@here").replace(/<!channel>/g,"@channel").replace(/<!everyone>/g,"@everyone"),i=n.ts,s=n.thread_ts,o=s!==void 0&&s!==i,l=n.user,c=l?{kind:"user",id:l}:void 0,f=/<@([A-Z0-9]+)>/g,u=[],m;for(;(m=f.exec(r))!==null;)u.push(m[1]);let g=n.channel;return{message:r,conversationId:o?s:g||i||"",data:n,participant:c,context:{user:l,channel:g,team:n.team,channelType:n.channel_type,threadId:o?s:void 0,mentions:u.length>0?u:void 0,channelId:g,channelName:typeof this.config.channel=="string"?this.config.channel:g}}}async resolveParticipant(e){let n=e.participant?.id??e.context?.user;if(!n)return;let t=this.participantCache.get(n);if(t)return t;try{let r=await fetch(`https://slack.com/api/users.info?user=${encodeURIComponent(n)}`,{method:"GET",headers:{Authorization:`Bearer ${this.config.token}`}});if(!r.ok)return{kind:"user",id:n};let i=await r.json();if(!i.ok||!i.user){let l={kind:"user",id:n};return this.participantCache.set(n,l),l}let s=i.user.profile?.display_name||i.user.profile?.real_name||i.user.real_name||i.user.name||n,o={kind:"user",id:n,displayName:s,metadata:{slackUser:i.user}};return this.participantCache.set(n,o),o}catch{return{kind:"user",id:n}}}invalidateParticipant(e){this.participantCache.delete(e)}shouldProcessEvent(e){let n=e.type;if(n!=="message"&&n!=="app_mention")return!1;if(this.allowedChannels!==null){let s=e.channel_type;if(!(s==="im"||s==="mpim")){let l=e.channel;if(!l||!this.allowedChannels.includes(l))return!1}}let t=e.user;if(this.botUserId&&t===this.botUserId)return!1;let r=e.bot_id;if(!r)return!0;let i=this.config.blockedBotIds??[];if(i.includes(r)||t!==void 0&&i.includes(t))return!1;if(this.config.allowedBotIds!==void 0){let s=this.config.allowedBotIds;return s.includes(r)||t!==void 0&&s.includes(t)}return!0}handleRequest(e,n){if(e.method!=="POST"){n.writeHead(405),n.end("Method not allowed");return}let t="";e.on("data",r=>{t+=r.toString()}),e.on("end",()=>{if(!this.verifySignature(e.headers,t)){n.writeHead(401),n.end("Invalid signature");return}try{let r=JSON.parse(t);if(r.type==="url_verification"){n.writeHead(200,{"Content-Type":"application/json"}),n.end(JSON.stringify({challenge:r.challenge}));return}if(r.type==="event_callback"&&r.event){let i=r.event;if(this.shouldProcessEvent(i)){let s=this.normalize(i);this.handleMessage(s)}else i.type==="user_change"&&i.user&&this.invalidateParticipant(i.user.id);n.writeHead(200),n.end("OK");return}n.writeHead(200),n.end("OK")}catch(r){console.error("[SlackChannel] Error handling request:",r),n.writeHead(400),n.end("Bad request")}})}async stop(){if(this.server)return new Promise(e=>{this.server.close(e)})}};var y=class extends d{isTriggerChannel=!1;config;server;pendingResponses=new Map;constructor(e){super(),this.name=e.name,this.config={port:e.port??3e3,path:e.path??"/webhook"}}listen(){typeof process<"u"&&import("http").then(e=>{this.server=e.createServer((n,t)=>{this.handleRequest(n,t)}),this.server.listen(this.config.port,()=>{console.log(`[WebhookChannel] Listening on port ${this.config.port}${this.config.path}`)})}).catch(e=>{console.error("[WebhookChannel] Failed to start HTTP server:",e)})}async send(e){let n=e.metadata?.conversationId;if(n&&this.pendingResponses.has(n)){let t=this.pendingResponses.get(n);this.pendingResponses.delete(n),t.resolve({output:e.output,metadata:e.metadata})}}normalize(e){let n=e,t=n.headers||{},r=t["x-session-id"]||t["X-Session-Id"]||n.sessionId||n.conversationId||this.generateSessionId();return{message:n.message||n.text||"",intent:n.intent,conversationId:r,data:n,context:{headers:n.headers,method:n.method,sessionId:r}}}handleRequest(e,n){if(e.url!==this.config.path){n.writeHead(404),n.end("Not found");return}if(e.method!=="POST"){n.writeHead(405),n.end("Method not allowed");return}let t="";e.on("data",r=>{t+=r.toString()}),e.on("end",()=>{try{let r=JSON.parse(t),i=this.normalize(r),s=i.conversationId||this.generateSessionId(),o=new Promise((l,c)=>{this.pendingResponses.set(s,{resolve:l,reject:c}),setTimeout(()=>{this.pendingResponses.has(s)&&(this.pendingResponses.delete(s),c(new Error("Agent response timeout")))},3e4)});this.handleMessage({...i,conversationId:s,context:{...i.context,sessionId:s}}),o.then(l=>{n.writeHead(200,{"Content-Type":"application/json"}),n.end(JSON.stringify(l))}).catch(l=>{n.writeHead(500,{"Content-Type":"application/json"}),n.end(JSON.stringify({error:l.message}))})}catch(r){console.error("[WebhookChannel] Error handling request:",r),n.writeHead(400,{"Content-Type":"application/json"}),n.end(JSON.stringify({error:"Bad request"}))}})}generateSessionId(){return`webhook-${Date.now()}-${Math.random().toString(36).substring(2,9)}`}async stop(){if(this.server)return new Promise(e=>{this.server.close(e)})}};var x=require("cron-parser"),S=class extends d{isTriggerChannel=!0;config;timer;_stopped=!1;_generation=0;constructor(e){if(super(),!e.cron&&!e.store)throw new Error("ScheduledChannel: provide at least one of `cron` (static schedule) or `store` (dynamic scheduling).");if(e.cron)try{x.CronExpressionParser.parse(e.cron)}catch(n){throw new Error(`ScheduledChannel: invalid cron expression '${e.cron}': ${n.message}`)}if(e.store&&!e.name&&console.warn("[ScheduledChannel] A `store` was provided without a `name`. All store queries will be unscoped and will pick up jobs from every channel. Set `name` to scope this channel to its own jobs."),e.idlePollMs!==void 0&&e.idlePollMs<1e3)throw new Error(`ScheduledChannel: idlePollMs must be at least 1000ms (got ${e.idlePollMs}). Values below 1 second create a tight polling loop.`);this.config=e,this.name=e.name}listen(){this._generation++,this.timer&&(clearTimeout(this.timer),this.timer=void 0),this._stopped=!1,this.config.store?this._listenWithStore():this._listenStatic()}async stop(){this._stopped=!0,this.timer&&(clearTimeout(this.timer),this.timer=void 0)}async send(e){}normalize(e){let n=e,t=new Date,r=`${t.getFullYear()}-${t.getMonth()+1}-${t.getDate()}`;return{intent:n?.intent??this.config.intent,message:n?.message??this.config.message??`Scheduled task triggered at ${t.toISOString()}`,conversationId:`scheduled:${this.name??"default"}:${r}`,data:{...n?.payload??{},scheduled:!0,jobId:n?.id,cron:n?.cron??this.config.cron,timestamp:t.toISOString()}}}_listenStatic(){this._scheduleNextStatic(this._generation)}_scheduleNextStatic(e){if(this._stopped||e!==this._generation)return;let n=this._nextRunFromCron(this.config.cron),t=n.getTime()-Date.now();if(t<=0){this.timer=setTimeout(()=>this._scheduleNextStatic(e),0);return}console.log(`[ScheduledChannel:${this.name??"default"}] Next run: ${n.toISOString()}`),this.timer=setTimeout(async()=>{this._stopped||e!==this._generation||(await this._triggerStatic(),this._scheduleNextStatic(e))},t)}async _triggerStatic(){if(!this._handler){console.warn(`[ScheduledChannel:${this.name??"default"}] Cron fired but no message handler is registered. Call onMessage() before listen() to avoid silently losing triggers.`);return}let e=this.normalize(null);try{await this.handleMessage(e)}catch(n){console.error(`[ScheduledChannel:${this.name??"default"}] Error on trigger:`,n)}}_listenWithStore(){let e=this.config.store,n=this._generation;if(n===1){let r=e.resetStuck(this.name);r>0&&console.log(`[ScheduledChannel:${this.name??"default"}] Reset ${r} stuck 'running' job(s) to 'pending'.`)}if(this.config.cron){let{duplicate:r}=e.create({channelName:this.name,cron:this.config.cron,intent:this.config.intent,message:this.config.message});r||console.log(`[ScheduledChannel:${this.name??"default"}] Seeded static cron '${this.config.cron}' into store.`)}let t=e.getDue(Date.now(),this.name);t.length>0&&(console.log(`[ScheduledChannel:${this.name??"default"}] Recovering ${t.length} overdue job(s).`),Promise.allSettled(t.map(r=>this._triggerJob(r)))),this._scheduleNextFromStore(n)}_scheduleNextFromStore(e){if(this._stopped||e!==this._generation)return;let n=this.config.store,t=n.getNextPending(this.name);if(!t){let i=this.config.idlePollMs??3e4;this.timer=setTimeout(()=>this._scheduleNextFromStore(e),i);return}let r=Math.max(0,t.nextRunAt-Date.now());console.log(`[ScheduledChannel:${this.name??"default"}] Next store job at ${new Date(t.nextRunAt).toISOString()} (in ${Math.round(r/1e3)}s)`),this.timer=setTimeout(async()=>{if(this._stopped||e!==this._generation)return;let i=n.getDue(Date.now(),this.name);await Promise.allSettled(i.map(s=>this._triggerJob(s))),this._scheduleNextFromStore(e)},r)}async _triggerJob(e){let n=this.config.store;if(!this._handler){console.warn(`[ScheduledChannel:${this.name??"default"}] Job ${e.id} fired but no message handler is registered. Call onMessage() before listen() to avoid silently losing jobs.`),n.markFailed(e.id,"No message handler registered");return}n.markRunning(e.id);let t=this.normalize(e);try{await this.handleMessage(t),n.markCompleted(e.id)}catch(r){let i=r instanceof Error?r.message:String(r);console.error(`[ScheduledChannel:${this.name??"default"}] Job ${e.id} failed:`,r),n.markFailed(e.id,i)}}_nextRunFromCron(e){return x.CronExpressionParser.parse(e,{currentDate:new Date}).next().toDate()}};var b=class extends d{isTriggerChannel=!1;config;offset=0;pollingInterval;server;botUserId;botUsername;constructor(e){super(),this.name=e.name,this.config=e}listen(){this.runStartupCheck().catch(()=>{}),this.config.webhookUrl?this.startWebhook():this.startPolling()}async runStartupCheck(){try{let n=await(await fetch(`https://api.telegram.org/bot${this.config.token}/getMe`)).json();if(n.ok&&n.result){let t=n.result;this.botUserId=t.id!=null?String(t.id):void 0,this.botUsername=t.username,console.log(`[TelegramChannel] Connected as @${t.username} (id: ${t.id}, name: ${t.first_name})`)}else console.warn(`[TelegramChannel] getMe failed: ${n.description??"unknown error"}. Check your bot token.`)}catch(e){console.warn("[TelegramChannel] Startup self-check failed (network error):",e)}}async send(e){let n=e.metadata?.chatId;if(!n)throw new Error("Telegram send requires chatId in metadata");let t=await fetch(`https://api.telegram.org/bot${this.config.token}/sendMessage`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({chat_id:n,text:e.output,parse_mode:"Markdown"})});if(!t.ok)throw new Error(`Failed to send Telegram message: ${t.statusText}`);let r=await t.json();if(!r.ok)throw new Error(`Telegram API error: ${r.description}`)}normalize(e){let n=e,t=n.message||n.edited_message||{},r=t.text||"",i=t.chat||{},s=t.from||{},o=s.id!=null?String(s.id):void 0,l=s.first_name||s.username||o,c=o?{kind:"user",id:o,displayName:l??o}:void 0,f=t.entities??[],u=[];for(let w of f)if(w.type==="text_mention"&&w.user){let $=w.user;$.id!=null&&u.push(String($.id))}let m=i.type,g=i.id!=null?String(i.id):"";return{message:r,conversationId:g,data:n,participant:c,context:{chatId:i.id,userId:s.id,username:s.username,firstName:s.first_name,lastName:s.last_name,messageId:t.message_id,channelType:m,channelId:g,channelName:i.title,mentions:u.length>0?u:void 0}}}startPolling(){console.log("[TelegramChannel] Starting polling mode"),this.pollingInterval=setInterval(async()=>{try{await this.pollUpdates()}catch(e){console.error("[TelegramChannel] Polling error:",e)}},5e3)}async pollUpdates(){let e=`https://api.telegram.org/bot${this.config.token}/getUpdates?offset=${this.offset}&limit=100`,n=await fetch(e);if(!n.ok)throw new Error(`Telegram getUpdates failed: ${n.statusText}`);let t=await n.json();if(!t.ok)throw new Error("Telegram getUpdates returned not ok");for(let r of t.result){let i=r.update_id;i>=this.offset&&(this.offset=i+1);try{let s=this.normalize(r);await this.handleMessage(s)}catch(s){console.error("[TelegramChannel] Error processing update:",s)}}}startWebhook(){typeof process>"u"||(console.log("[TelegramChannel] Starting webhook mode"),import("http").then(e=>{this.server=e.createServer((r,i)=>{this.handleWebhookRequest(r,i)});let n=new URL(this.config.webhookUrl||"http://localhost:3000"),t=parseInt(n.port,10)||3e3;this.server.listen(t,()=>{console.log(`[TelegramChannel] Webhook server listening on port ${t}`)}),this.setWebhook()}).catch(e=>{console.error("[TelegramChannel] Failed to start webhook server:",e)}))}async setWebhook(){if(!this.config.webhookUrl)return;let e=await fetch(`https://api.telegram.org/bot${this.config.token}/setWebhook`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({url:this.config.webhookUrl})});if(!e.ok){console.error("[TelegramChannel] Failed to set webhook");return}let n=await e.json();n.ok?console.log("[TelegramChannel] Webhook set successfully"):console.error("[TelegramChannel] Failed to set webhook:",n.description)}handleWebhookRequest(e,n){if(e.method!=="POST"){n.writeHead(405),n.end("Method not allowed");return}let t="";e.on("data",r=>{t+=r.toString()}),e.on("end",()=>{try{let r=JSON.parse(t);this.handleMessage(this.normalize(r)).catch(i=>{console.error("[TelegramChannel] Error processing webhook:",i)}),n.writeHead(200),n.end("OK")}catch(r){console.error("[TelegramChannel] Error parsing webhook:",r),n.writeHead(400),n.end("Bad request")}})}async stop(){if(this.pollingInterval&&(clearInterval(this.pollingInterval),this.pollingInterval=void 0),this.server)return new Promise(e=>{this.server.close(e)});if(this.config.webhookUrl)try{await fetch(`https://api.telegram.org/bot${this.config.token}/deleteWebhook`,{method:"POST"})}catch(e){console.error("[TelegramChannel] Failed to delete webhook:",e)}}};var B=new Set([1,3]),A=/<@!?(\d+)>/g,F="https://discord.com/api/v10",I=class extends d{isTriggerChannel=!1;config;allowedChannelIds;botUserId;participantCache=new Map;client;constructor(e){super(),this.config=e,this.name=e.name,e.channelId==null?this.allowedChannelIds=new Set:Array.isArray(e.channelId)?this.allowedChannelIds=new Set(e.channelId):this.allowedChannelIds=new Set([e.channelId])}shouldProcessEvent(e){return!e.author||e.webhookId||this.botUserId&&e.author.id===this.botUserId||this.config.guildId&&e.guildId!==this.config.guildId||this.allowedChannelIds.size>0&&(!e.channelId||!this.allowedChannelIds.has(e.channelId))?!1:e.author.bot||e.author.system?this.config.blockedBotIds?.includes(e.author.id)?!1:this.config.allowedBotIds?this.config.allowedBotIds.includes(e.author.id):!1:!0}normalize(e){let n=e,t=n.channelId,r=(t??"")+(n.thread?.id?`:${n.thread.id}`:""),i=n.channel?.type,s=i!==void 0&&B.has(i),o=n.author?.id,l=n.author?.globalName??n.author?.username,c=[];if(n.content){A.lastIndex=0;let f;for(;(f=A.exec(n.content))!==null;)c.push(f[1])}return{message:n.content,conversationId:r,data:n,participant:o?{kind:"user",id:o,displayName:l??void 0}:void 0,context:{userId:o,username:n.author?.username,channelType:s?"dm":"channel",channelId:t,channelName:n.channel?.name,guildId:n.guildId,threadId:n.thread?.id,messageId:n.id,mentions:c.length>0?c:void 0,isMentioned:this.botUserId?c.includes(this.botUserId):void 0}}}async resolveParticipant(e){let n=e.participant?.id??e.context?.userId;if(!n)return;let t=this.participantCache.get(n);if(t)return t;try{let r=await fetch(`${F}/users/${n}`,{headers:{Authorization:`Bot ${this.config.token}`}});if(!r.ok)return console.warn(`[DiscordChannel] Failed to resolve user ${n}: HTTP ${r.status}`),{kind:"user",id:n};let i=await r.json(),s=i.global_name??i.username,o={kind:"user",id:i.id,displayName:s};return this.participantCache.set(n,o),o}catch(r){return console.warn(`[DiscordChannel] resolveParticipant error for ${n}:`,r),{kind:"user",id:n}}}invalidateParticipant(e){this.participantCache.delete(e)}async send(e){if(!this.client)throw new Error("[DiscordChannel] Client not initialized. Did you call listen()?");let n=e.metadata?.threadId;if(n){let i=await this.client.channels.fetch(n);if(i&&"send"in i)try{await i.send({content:e.output});return}catch(s){throw console.error("[DiscordChannel] Failed to send to thread:",s),new Error(`[DiscordChannel] Failed to send Discord message: ${s instanceof Error?s.message:String(s)}`)}console.warn(`[DiscordChannel] Thread ${n} not found or not sendable; falling back to channel send.`)}let t=e.metadata?.channelId??(this.allowedChannelIds.size>0?[...this.allowedChannelIds][0]:void 0);if(!t)throw new Error("[DiscordChannel] No channel ID available for sending. Configure channelId or pass output.metadata.channelId.");let r=await this.client.channels.fetch(t);if(!r||!("send"in r))throw new Error(`[DiscordChannel] Channel ${t} not found or is not a text channel`);try{await r.send({content:e.output})}catch(i){throw console.error("[DiscordChannel] Failed to send to channel:",i),new Error(`[DiscordChannel] Failed to send Discord message: ${i instanceof Error?i.message:String(i)}`)}}listen(){typeof process>"u"||import("discord.js").then(e=>{let{Client:n,GatewayIntentBits:t}=e;this.client=new n({intents:[t.Guilds,t.GuildMessages,t.MessageContent,t.DirectMessages]}),this.client.on("ready",()=>{let r=this.client.user;this.botUserId=r?.id;let i=this.allowedChannelIds.size===0?"all channels":[...this.allowedChannelIds].join(", "),s=this.config.guildId??"all guilds";console.log(`[DiscordChannel] Logged in as ${r?.tag??"unknown"} (botUserId=${this.botUserId??"unknown"}) | guild=${s} | channels=${i}`)}),this.client.on("messageCreate",r=>{let i=r;if(!this.shouldProcessEvent(i))return;let s=this.normalize(i);this.handleMessage(s)}),this.client.on("userUpdate",(r,i)=>{let s=i;s?.id&&this.invalidateParticipant(s.id)}),this.client.login(this.config.token).catch(r=>{console.error("[DiscordChannel] Failed to login to Discord:",r)})}).catch(e=>{console.error("[DiscordChannel] Failed to initialize Discord client:",e),console.error("[DiscordChannel] Make sure discord.js is installed: npm install discord.js")})}async stop(){this.client&&(await this.client.destroy(),this.client=void 0)}};var T=class extends d{isTriggerChannel=!0;config;transporter;constructor(e){super(),this.config=e,this.name=e.name}listen(){typeof process<"u"&&import("nodemailer").then(e=>{this.transporter=e.default.createTransport({host:this.config.smtp.host,port:this.config.smtp.port,secure:this.config.smtp.secure??this.config.smtp.port===465,auth:{user:this.config.smtp.auth.user,pass:this.config.smtp.auth.pass}}),console.log(`[EmailChannel] Email transporter initialized for ${this.config.from}`)}).catch(e=>{console.error("[EmailChannel] Failed to initialize nodemailer:",e),console.error("[EmailChannel] Make sure to install nodemailer: npm install nodemailer")})}async send(e){if(!this.transporter)throw new Error("Email transporter not initialized. Did you call listen()?");let n=Array.isArray(this.config.to)?this.config.to:[this.config.to],t=this.config.subject||"Message from Agent",r={from:this.config.from,to:n.join(", "),subject:t,text:e.output,html:this.formatAsHtml(e.output)};try{await this.transporter.sendMail(r)}catch(i){throw console.error("[EmailChannel] Failed to send email:",i),new Error(`Failed to send email: ${i instanceof Error?i.message:String(i)}`)}}normalize(e){throw new Error("EmailChannel is outbound-only. Use WebhookChannel with email webhook events for inbound email.")}formatAsHtml(e){return e.split(`
1
+ "use strict";var D=Object.create;var C=Object.defineProperty;var E=Object.getOwnPropertyDescriptor;var R=Object.getOwnPropertyNames;var O=Object.getPrototypeOf,N=Object.prototype.hasOwnProperty;var U=(a,e)=>{for(var n in e)C(a,n,{get:e[n],enumerable:!0})},A=(a,e,n,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of R(e))!N.call(a,i)&&i!==n&&C(a,i,{get:()=>e[i],enumerable:!(t=E(e,i))||t.enumerable});return a};var p=(a,e,n)=>(n=a!=null?D(O(a)):{},A(e||!a||!a.__esModule?C(n,"default",{value:a,enumerable:!0}):n,a)),B=a=>A(C({},"__esModule",{value:!0}),a);var j={};U(j,{BaseChannel:()=>d,DiscordChannel:()=>I,EmailChannel:()=>T,McpChannel:()=>x,SMSChannel:()=>P,ScheduledChannel:()=>S,SlackChannel:()=>v,TelegramChannel:()=>b,WebhookChannel:()=>k});module.exports=B(j);var d=class{name;_handler;onMessage(e){this._handler=e}async handleMessage(e){this._handler&&await this._handler(e)}};var y=require("crypto");var v=class extends d{isTriggerChannel=!1;config;server;participantCache=new Map;botUserId;allowedChannels;constructor(e){super(),this.config={port:3e3,...e},this.name=e.name;let n=e.channel;this.allowedChannels=n==null?null:Array.isArray(n)?n:[n]}listen(){typeof process<"u"&&import("http").then(e=>{this.server=e.createServer((n,t)=>{this.handleRequest(n,t)}),this.server.listen(this.config.port,()=>{console.log(`[SlackChannel] Listening on port ${this.config.port}`),this.runStartupCheck().catch(()=>{})})}).catch(e=>{console.error("[SlackChannel] Failed to start HTTP server:",e)})}async runStartupCheck(){try{let n=await(await fetch("https://slack.com/api/auth.test",{method:"POST",headers:{Authorization:`Bearer ${this.config.token}`,"Content-Type":"application/json"}})).json();n.ok?(this.botUserId=n.user_id,console.log(`[SlackChannel] Connected as @${n.user} (${n.user_id}) in workspace "${n.team}" \u2014 ${n.url}`)):console.warn(`[SlackChannel] auth.test failed: ${n.error}. Check your bot token.`)}catch(e){console.warn("[SlackChannel] Startup self-check failed (network error):",e)}}verifySignature(e,n){let t=e["x-slack-request-timestamp"],i=e["x-slack-signature"];if(!t||!i||Array.isArray(t)||Array.isArray(i))return!1;let r=parseInt(t,10),s=Math.floor(Date.now()/1e3);if(isNaN(r)||Math.abs(s-r)>300)return!1;let o=`v0:${t}:${n}`,c=`v0=${(0,y.createHmac)("sha256",this.config.signingSecret).update(o).digest("hex")}`;if(c.length!==i.length)return!1;try{return(0,y.timingSafeEqual)(Buffer.from(c),Buffer.from(i))}catch{return!1}}async send(e){let n=e.metadata?.threadTs??e.metadata?.thread_ts??e.metadata?.threadId,i=e.metadata?.channelId??(this.allowedChannels&&this.allowedChannels.length>0?this.allowedChannels[0]:void 0);if(!i)throw new Error("[SlackChannel] Cannot send: no channel configured and metadata.channelId is missing. Provide a target via SlackChannelConfig.channel or output.metadata.channelId.");let r={channel:i,text:e.output};n&&(r.thread_ts=n);let s=await fetch("https://slack.com/api/chat.postMessage",{method:"POST",headers:{Authorization:`Bearer ${this.config.token}`,"Content-Type":"application/json"},body:JSON.stringify(r)});if(!s.ok)throw new Error(`Failed to send Slack message: ${s.statusText}`);let o=await s.json();if(!o.ok)throw new Error(`Slack API error: ${o.error}`)}normalize(e){let n=e,i=(n.text||"").replace(/<(https?:\/\/[^|>]+)\|([^>]+)>/g,"$2 ($1)").replace(/<(https?:\/\/[^>]+)>/g,"$1").replace(/<!here>/g,"@here").replace(/<!channel>/g,"@channel").replace(/<!everyone>/g,"@everyone"),r=n.ts,s=n.thread_ts,o=s!==void 0&&s!==r,l=n.user,c=l?{kind:"user",id:l}:void 0,m=/<@([A-Z0-9]+)>/g,g=[],f;for(;(f=m.exec(i))!==null;)g.push(f[1]);let u=n.channel;return{message:i,conversationId:o?s:u||r||"",data:n,participant:c,context:{user:l,channel:u,team:n.team,channelType:n.channel_type,threadId:o?s:void 0,mentions:g.length>0?g:void 0,channelId:u,channelName:typeof this.config.channel=="string"?this.config.channel:u}}}async resolveParticipant(e){let n=e.participant?.id??e.context?.user;if(!n)return;let t=this.participantCache.get(n);if(t)return t;try{let i=await fetch(`https://slack.com/api/users.info?user=${encodeURIComponent(n)}`,{method:"GET",headers:{Authorization:`Bearer ${this.config.token}`}});if(!i.ok)return{kind:"user",id:n};let r=await i.json();if(!r.ok||!r.user){let l={kind:"user",id:n};return this.participantCache.set(n,l),l}let s=r.user.profile?.display_name||r.user.profile?.real_name||r.user.real_name||r.user.name||n,o={kind:"user",id:n,displayName:s,metadata:{slackUser:r.user}};return this.participantCache.set(n,o),o}catch{return{kind:"user",id:n}}}invalidateParticipant(e){this.participantCache.delete(e)}shouldProcessEvent(e){let n=e.type;if(n!=="message"&&n!=="app_mention")return!1;if(this.allowedChannels!==null){let s=e.channel_type;if(!(s==="im"||s==="mpim")){let l=e.channel;if(!l||!this.allowedChannels.includes(l))return!1}}let t=e.user;if(this.botUserId&&t===this.botUserId)return!1;let i=e.bot_id;if(!i)return!0;let r=this.config.blockedBotIds??[];if(r.includes(i)||t!==void 0&&r.includes(t))return!1;if(this.config.allowedBotIds!==void 0){let s=this.config.allowedBotIds;return s.includes(i)||t!==void 0&&s.includes(t)}return!0}handleRequest(e,n){if(e.method!=="POST"){n.writeHead(405),n.end("Method not allowed");return}let t="";e.on("data",i=>{t+=i.toString()}),e.on("end",()=>{if(!this.verifySignature(e.headers,t)){n.writeHead(401),n.end("Invalid signature");return}try{let i=JSON.parse(t);if(i.type==="url_verification"){n.writeHead(200,{"Content-Type":"application/json"}),n.end(JSON.stringify({challenge:i.challenge}));return}if(i.type==="event_callback"&&i.event){let r=i.event;if(this.shouldProcessEvent(r)){let s=this.normalize(r);this.handleMessage(s)}else r.type==="user_change"&&r.user&&this.invalidateParticipant(r.user.id);n.writeHead(200),n.end("OK");return}n.writeHead(200),n.end("OK")}catch(i){console.error("[SlackChannel] Error handling request:",i),n.writeHead(400),n.end("Bad request")}})}async stop(){if(this.server)return new Promise(e=>{this.server.close(e)})}};var k=class extends d{isTriggerChannel=!1;config;server;pendingResponses=new Map;constructor(e){super(),this.name=e.name,this.config={port:e.port??3e3,path:e.path??"/webhook"}}listen(){typeof process<"u"&&import("http").then(e=>{this.server=e.createServer((n,t)=>{this.handleRequest(n,t)}),this.server.listen(this.config.port,()=>{console.log(`[WebhookChannel] Listening on port ${this.config.port}${this.config.path}`)})}).catch(e=>{console.error("[WebhookChannel] Failed to start HTTP server:",e)})}async send(e){let n=e.metadata?.conversationId;if(n&&this.pendingResponses.has(n)){let t=this.pendingResponses.get(n);this.pendingResponses.delete(n),t.resolve({output:e.output,metadata:e.metadata})}}normalize(e){let n=e,t=n.headers||{},i=t["x-session-id"]||t["X-Session-Id"]||n.sessionId||n.conversationId||this.generateSessionId();return{message:n.message||n.text||"",intent:n.intent,conversationId:i,data:n,context:{headers:n.headers,method:n.method,sessionId:i}}}handleRequest(e,n){if(e.url!==this.config.path){n.writeHead(404),n.end("Not found");return}if(e.method!=="POST"){n.writeHead(405),n.end("Method not allowed");return}let t="";e.on("data",i=>{t+=i.toString()}),e.on("end",()=>{try{let i=JSON.parse(t),r=this.normalize(i),s=r.conversationId||this.generateSessionId(),o=new Promise((l,c)=>{this.pendingResponses.set(s,{resolve:l,reject:c}),setTimeout(()=>{this.pendingResponses.has(s)&&(this.pendingResponses.delete(s),c(new Error("Agent response timeout")))},3e4)});this.handleMessage({...r,conversationId:s,context:{...r.context,sessionId:s}}),o.then(l=>{n.writeHead(200,{"Content-Type":"application/json"}),n.end(JSON.stringify(l))}).catch(l=>{n.writeHead(500,{"Content-Type":"application/json"}),n.end(JSON.stringify({error:l.message}))})}catch(i){console.error("[WebhookChannel] Error handling request:",i),n.writeHead(400,{"Content-Type":"application/json"}),n.end(JSON.stringify({error:"Bad request"}))}})}generateSessionId(){return`webhook-${Date.now()}-${Math.random().toString(36).substring(2,9)}`}async stop(){if(this.server)return new Promise(e=>{this.server.close(e)})}};var _=require("cron-parser"),S=class extends d{isTriggerChannel=!0;config;timer;_stopped=!1;_generation=0;constructor(e){if(super(),!e.cron&&!e.store)throw new Error("ScheduledChannel: provide at least one of `cron` (static schedule) or `store` (dynamic scheduling).");if(e.cron)try{_.CronExpressionParser.parse(e.cron)}catch(n){throw new Error(`ScheduledChannel: invalid cron expression '${e.cron}': ${n.message}`)}if(e.store&&!e.name&&console.warn("[ScheduledChannel] A `store` was provided without a `name`. All store queries will be unscoped and will pick up jobs from every channel. Set `name` to scope this channel to its own jobs."),e.idlePollMs!==void 0&&e.idlePollMs<1e3)throw new Error(`ScheduledChannel: idlePollMs must be at least 1000ms (got ${e.idlePollMs}). Values below 1 second create a tight polling loop.`);this.config=e,this.name=e.name}listen(){this._generation++,this.timer&&(clearTimeout(this.timer),this.timer=void 0),this._stopped=!1,this.config.store?this._listenWithStore():this._listenStatic()}async stop(){this._stopped=!0,this.timer&&(clearTimeout(this.timer),this.timer=void 0)}async send(e){}normalize(e){let n=e,t=new Date,i=`${t.getFullYear()}-${t.getMonth()+1}-${t.getDate()}`;return{intent:n?.intent??this.config.intent,message:n?.message??this.config.message??`Scheduled task triggered at ${t.toISOString()}`,conversationId:`scheduled:${this.name??"default"}:${i}`,data:{...n?.payload??{},scheduled:!0,jobId:n?.id,cron:n?.cron??this.config.cron,timestamp:t.toISOString()}}}_listenStatic(){this._scheduleNextStatic(this._generation)}_scheduleNextStatic(e){if(this._stopped||e!==this._generation)return;let n=this._nextRunFromCron(this.config.cron),t=n.getTime()-Date.now();if(t<=0){this.timer=setTimeout(()=>this._scheduleNextStatic(e),0);return}console.log(`[ScheduledChannel:${this.name??"default"}] Next run: ${n.toISOString()}`),this.timer=setTimeout(async()=>{this._stopped||e!==this._generation||(await this._triggerStatic(),this._scheduleNextStatic(e))},t)}async _triggerStatic(){if(!this._handler){console.warn(`[ScheduledChannel:${this.name??"default"}] Cron fired but no message handler is registered. Call onMessage() before listen() to avoid silently losing triggers.`);return}let e=this.normalize(null);try{await this.handleMessage(e)}catch(n){console.error(`[ScheduledChannel:${this.name??"default"}] Error on trigger:`,n)}}_listenWithStore(){let e=this.config.store,n=this._generation;if(n===1){let i=e.resetStuck(this.name);i>0&&console.log(`[ScheduledChannel:${this.name??"default"}] Reset ${i} stuck 'running' job(s) to 'pending'.`)}if(this.config.cron){let{duplicate:i}=e.create({channelName:this.name,cron:this.config.cron,intent:this.config.intent,message:this.config.message});i||console.log(`[ScheduledChannel:${this.name??"default"}] Seeded static cron '${this.config.cron}' into store.`)}let t=e.getDue(Date.now(),this.name);t.length>0&&(console.log(`[ScheduledChannel:${this.name??"default"}] Recovering ${t.length} overdue job(s).`),Promise.allSettled(t.map(i=>this._triggerJob(i)))),this._scheduleNextFromStore(n)}_scheduleNextFromStore(e){if(this._stopped||e!==this._generation)return;let n=this.config.store,t=n.getNextPending(this.name);if(!t){let r=this.config.idlePollMs??3e4;this.timer=setTimeout(()=>this._scheduleNextFromStore(e),r);return}let i=Math.max(0,t.nextRunAt-Date.now());console.log(`[ScheduledChannel:${this.name??"default"}] Next store job at ${new Date(t.nextRunAt).toISOString()} (in ${Math.round(i/1e3)}s)`),this.timer=setTimeout(async()=>{if(this._stopped||e!==this._generation)return;let r=n.getDue(Date.now(),this.name);await Promise.allSettled(r.map(s=>this._triggerJob(s))),this._scheduleNextFromStore(e)},i)}async _triggerJob(e){let n=this.config.store;if(!this._handler){console.warn(`[ScheduledChannel:${this.name??"default"}] Job ${e.id} fired but no message handler is registered. Call onMessage() before listen() to avoid silently losing jobs.`),n.markFailed(e.id,"No message handler registered");return}n.markRunning(e.id);let t=this.normalize(e);try{await this.handleMessage(t),n.markCompleted(e.id)}catch(i){let r=i instanceof Error?i.message:String(i);console.error(`[ScheduledChannel:${this.name??"default"}] Job ${e.id} failed:`,i),n.markFailed(e.id,r)}}_nextRunFromCron(e){return _.CronExpressionParser.parse(e,{currentDate:new Date}).next().toDate()}};var b=class extends d{isTriggerChannel=!1;config;offset=0;pollingInterval;server;botUserId;botUsername;constructor(e){super(),this.name=e.name,this.config=e}listen(){this.runStartupCheck().catch(()=>{}),this.config.webhookUrl?this.startWebhook():this.startPolling()}async runStartupCheck(){try{let n=await(await fetch(`https://api.telegram.org/bot${this.config.token}/getMe`)).json();if(n.ok&&n.result){let t=n.result;this.botUserId=t.id!=null?String(t.id):void 0,this.botUsername=t.username,console.log(`[TelegramChannel] Connected as @${t.username} (id: ${t.id}, name: ${t.first_name})`)}else console.warn(`[TelegramChannel] getMe failed: ${n.description??"unknown error"}. Check your bot token.`)}catch(e){console.warn("[TelegramChannel] Startup self-check failed (network error):",e)}}async send(e){let n=e.metadata?.chatId;if(!n)throw new Error("Telegram send requires chatId in metadata");let t=await fetch(`https://api.telegram.org/bot${this.config.token}/sendMessage`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({chat_id:n,text:e.output,parse_mode:"Markdown"})});if(!t.ok)throw new Error(`Failed to send Telegram message: ${t.statusText}`);let i=await t.json();if(!i.ok)throw new Error(`Telegram API error: ${i.description}`)}normalize(e){let n=e,t=n.message||n.edited_message||{},i=t.text||"",r=t.chat||{},s=t.from||{},o=s.id!=null?String(s.id):void 0,l=s.first_name||s.username||o,c=o?{kind:"user",id:o,displayName:l??o}:void 0,m=t.entities??[],g=[];for(let w of m)if(w.type==="text_mention"&&w.user){let $=w.user;$.id!=null&&g.push(String($.id))}let f=r.type,u=r.id!=null?String(r.id):"";return{message:i,conversationId:u,data:n,participant:c,context:{chatId:r.id,userId:s.id,username:s.username,firstName:s.first_name,lastName:s.last_name,messageId:t.message_id,channelType:f,channelId:u,channelName:r.title,mentions:g.length>0?g:void 0}}}startPolling(){console.log("[TelegramChannel] Starting polling mode"),this.pollingInterval=setInterval(async()=>{try{await this.pollUpdates()}catch(e){console.error("[TelegramChannel] Polling error:",e)}},5e3)}async pollUpdates(){let e=`https://api.telegram.org/bot${this.config.token}/getUpdates?offset=${this.offset}&limit=100`,n=await fetch(e);if(!n.ok)throw new Error(`Telegram getUpdates failed: ${n.statusText}`);let t=await n.json();if(!t.ok)throw new Error("Telegram getUpdates returned not ok");for(let i of t.result){let r=i.update_id;r>=this.offset&&(this.offset=r+1);try{let s=this.normalize(i);await this.handleMessage(s)}catch(s){console.error("[TelegramChannel] Error processing update:",s)}}}startWebhook(){typeof process>"u"||(console.log("[TelegramChannel] Starting webhook mode"),import("http").then(e=>{this.server=e.createServer((i,r)=>{this.handleWebhookRequest(i,r)});let n=new URL(this.config.webhookUrl||"http://localhost:3000"),t=parseInt(n.port,10)||3e3;this.server.listen(t,()=>{console.log(`[TelegramChannel] Webhook server listening on port ${t}`)}),this.setWebhook()}).catch(e=>{console.error("[TelegramChannel] Failed to start webhook server:",e)}))}async setWebhook(){if(!this.config.webhookUrl)return;let e=await fetch(`https://api.telegram.org/bot${this.config.token}/setWebhook`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({url:this.config.webhookUrl})});if(!e.ok){console.error("[TelegramChannel] Failed to set webhook");return}let n=await e.json();n.ok?console.log("[TelegramChannel] Webhook set successfully"):console.error("[TelegramChannel] Failed to set webhook:",n.description)}handleWebhookRequest(e,n){if(e.method!=="POST"){n.writeHead(405),n.end("Method not allowed");return}let t="";e.on("data",i=>{t+=i.toString()}),e.on("end",()=>{try{let i=JSON.parse(t);this.handleMessage(this.normalize(i)).catch(r=>{console.error("[TelegramChannel] Error processing webhook:",r)}),n.writeHead(200),n.end("OK")}catch(i){console.error("[TelegramChannel] Error parsing webhook:",i),n.writeHead(400),n.end("Bad request")}})}async stop(){if(this.pollingInterval&&(clearInterval(this.pollingInterval),this.pollingInterval=void 0),this.server)return new Promise(e=>{this.server.close(e)});if(this.config.webhookUrl)try{await fetch(`https://api.telegram.org/bot${this.config.token}/deleteWebhook`,{method:"POST"})}catch(e){console.error("[TelegramChannel] Failed to delete webhook:",e)}}};var z=new Set([1,3]),M=/<@!?(\d+)>/g,F="https://discord.com/api/v10",I=class extends d{isTriggerChannel=!1;config;allowedChannelIds;botUserId;participantCache=new Map;client;constructor(e){super(),this.config=e,this.name=e.name,e.channelId==null?this.allowedChannelIds=new Set:Array.isArray(e.channelId)?this.allowedChannelIds=new Set(e.channelId):this.allowedChannelIds=new Set([e.channelId])}shouldProcessEvent(e){return!e.author||e.webhookId||this.botUserId&&e.author.id===this.botUserId||this.config.guildId&&e.guildId!==this.config.guildId||this.allowedChannelIds.size>0&&(!e.channelId||!this.allowedChannelIds.has(e.channelId))?!1:e.author.bot||e.author.system?this.config.blockedBotIds?.includes(e.author.id)?!1:this.config.allowedBotIds?this.config.allowedBotIds.includes(e.author.id):!1:!0}normalize(e){let n=e,t=n.channelId,i=(t??"")+(n.thread?.id?`:${n.thread.id}`:""),r=n.channel?.type,s=r!==void 0&&z.has(r),o=n.author?.id,l=n.author?.globalName??n.author?.username,c=[];if(n.content){M.lastIndex=0;let m;for(;(m=M.exec(n.content))!==null;)c.push(m[1])}return{message:n.content,conversationId:i,data:n,participant:o?{kind:"user",id:o,displayName:l??void 0}:void 0,context:{userId:o,username:n.author?.username,channelType:s?"dm":"channel",channelId:t,channelName:n.channel?.name,guildId:n.guildId,threadId:n.thread?.id,messageId:n.id,mentions:c.length>0?c:void 0,isMentioned:this.botUserId?c.includes(this.botUserId):void 0}}}async resolveParticipant(e){let n=e.participant?.id??e.context?.userId;if(!n)return;let t=this.participantCache.get(n);if(t)return t;try{let i=await fetch(`${F}/users/${n}`,{headers:{Authorization:`Bot ${this.config.token}`}});if(!i.ok)return console.warn(`[DiscordChannel] Failed to resolve user ${n}: HTTP ${i.status}`),{kind:"user",id:n};let r=await i.json(),s=r.global_name??r.username,o={kind:"user",id:r.id,displayName:s};return this.participantCache.set(n,o),o}catch(i){return console.warn(`[DiscordChannel] resolveParticipant error for ${n}:`,i),{kind:"user",id:n}}}invalidateParticipant(e){this.participantCache.delete(e)}async send(e){if(!this.client)throw new Error("[DiscordChannel] Client not initialized. Did you call listen()?");let n=e.metadata?.threadId;if(n){let r=await this.client.channels.fetch(n);if(r&&"send"in r)try{await r.send({content:e.output});return}catch(s){throw console.error("[DiscordChannel] Failed to send to thread:",s),new Error(`[DiscordChannel] Failed to send Discord message: ${s instanceof Error?s.message:String(s)}`)}console.warn(`[DiscordChannel] Thread ${n} not found or not sendable; falling back to channel send.`)}let t=e.metadata?.channelId??(this.allowedChannelIds.size>0?[...this.allowedChannelIds][0]:void 0);if(!t)throw new Error("[DiscordChannel] No channel ID available for sending. Configure channelId or pass output.metadata.channelId.");let i=await this.client.channels.fetch(t);if(!i||!("send"in i))throw new Error(`[DiscordChannel] Channel ${t} not found or is not a text channel`);try{await i.send({content:e.output})}catch(r){throw console.error("[DiscordChannel] Failed to send to channel:",r),new Error(`[DiscordChannel] Failed to send Discord message: ${r instanceof Error?r.message:String(r)}`)}}listen(){typeof process>"u"||import("discord.js").then(e=>{let{Client:n,GatewayIntentBits:t}=e;this.client=new n({intents:[t.Guilds,t.GuildMessages,t.MessageContent,t.DirectMessages]}),this.client.on("ready",()=>{let i=this.client.user;this.botUserId=i?.id;let r=this.allowedChannelIds.size===0?"all channels":[...this.allowedChannelIds].join(", "),s=this.config.guildId??"all guilds";console.log(`[DiscordChannel] Logged in as ${i?.tag??"unknown"} (botUserId=${this.botUserId??"unknown"}) | guild=${s} | channels=${r}`)}),this.client.on("messageCreate",i=>{let r=i;if(!this.shouldProcessEvent(r))return;let s=this.normalize(r);this.handleMessage(s)}),this.client.on("userUpdate",(i,r)=>{let s=r;s?.id&&this.invalidateParticipant(s.id)}),this.client.login(this.config.token).catch(i=>{console.error("[DiscordChannel] Failed to login to Discord:",i)})}).catch(e=>{console.error("[DiscordChannel] Failed to initialize Discord client:",e),console.error("[DiscordChannel] Make sure discord.js is installed: npm install discord.js")})}async stop(){this.client&&(await this.client.destroy(),this.client=void 0)}};var T=class extends d{isTriggerChannel=!0;config;transporter;constructor(e){super(),this.config=e,this.name=e.name}listen(){typeof process<"u"&&import("nodemailer").then(e=>{this.transporter=e.default.createTransport({host:this.config.smtp.host,port:this.config.smtp.port,secure:this.config.smtp.secure??this.config.smtp.port===465,auth:{user:this.config.smtp.auth.user,pass:this.config.smtp.auth.pass}}),console.log(`[EmailChannel] Email transporter initialized for ${this.config.from}`)}).catch(e=>{console.error("[EmailChannel] Failed to initialize nodemailer:",e),console.error("[EmailChannel] Make sure to install nodemailer: npm install nodemailer")})}async send(e){if(!this.transporter)throw new Error("Email transporter not initialized. Did you call listen()?");let n=Array.isArray(this.config.to)?this.config.to:[this.config.to],t=this.config.subject||"Message from Agent",i={from:this.config.from,to:n.join(", "),subject:t,text:e.output,html:this.formatAsHtml(e.output)};try{await this.transporter.sendMail(i)}catch(r){throw console.error("[EmailChannel] Failed to send email:",r),new Error(`Failed to send email: ${r instanceof Error?r.message:String(r)}`)}}normalize(e){throw new Error("EmailChannel is outbound-only. Use WebhookChannel with email webhook events for inbound email.")}formatAsHtml(e){return e.split(`
2
2
 
3
- `).map(n=>`<p>${n.replace(/\n/g,"<br>")}</p>`).join("")}async stop(){this.transporter&&(this.transporter.close(),this.transporter=void 0)}};var P=class extends d{config;twilioClient;server;constructor(e){super(),this.config={port:3e3,...e},this.name=e.name}get isTriggerChannel(){return!this.config.webhookPath}listen(){typeof process<"u"&&import("twilio").then(e=>{this.twilioClient=e.default(this.config.accountSid,this.config.authToken),console.log("[SMSChannel] Twilio client initialized"),this.config.webhookPath&&this.startWebhookServer()}).catch(e=>{console.error("[SMSChannel] Failed to initialize Twilio client:",e),console.error("[SMSChannel] Make sure to install twilio: npm install twilio")})}async send(e){if(!this.twilioClient)throw new Error("Twilio client not initialized. Did you call listen()?");let n=e.metadata?.from||this.config.to;if(!n)throw new Error('No recipient phone number specified. Set "to" in config or provide in output.metadata.from');try{await this.twilioClient.messages.create({body:e.output,from:this.config.from,to:n})}catch(t){throw console.error("[SMSChannel] Failed to send SMS:",t),new Error(`Failed to send SMS: ${t instanceof Error?t.message:String(t)}`)}}normalize(e){let n=e,t=n.From,r=n.Body,i=n.MessageSid;return{message:r,conversationId:t,data:n,context:{from:t,to:n.To,messageSid:i}}}startWebhookServer(){import("http").then(e=>{this.server=e.createServer((n,t)=>{this.handleWebhookRequest(n,t)}),this.server.listen(this.config.port,()=>{console.log(`[SMSChannel] Webhook server listening on port ${this.config.port} at ${this.config.webhookPath}`)})}).catch(e=>{console.error("[SMSChannel] Failed to start webhook server:",e)})}handleWebhookRequest(e,n){if(e.method!=="POST"||e.url!==this.config.webhookPath){n.writeHead(404),n.end("Not found");return}let t="";e.on("data",r=>{t+=r.toString()}),e.on("end",()=>{try{let r=new URLSearchParams(t),i={};r.forEach((o,l)=>{i[l]=o});let s=this.normalize(i);this.handleMessage(s),n.writeHead(200,{"Content-Type":"text/xml"}),n.end('<?xml version="1.0" encoding="UTF-8"?><Response></Response>')}catch(r){console.error("[SMSChannel] Error handling webhook:",r),n.writeHead(400),n.end("Bad request")}})}async stop(){if(this.server)return new Promise(e=>{this.server.close(e)})}};0&&(module.exports={BaseChannel,DiscordChannel,EmailChannel,SMSChannel,ScheduledChannel,SlackChannel,TelegramChannel,WebhookChannel});
3
+ `).map(n=>`<p>${n.replace(/\n/g,"<br>")}</p>`).join("")}async stop(){this.transporter&&(this.transporter.close(),this.transporter=void 0)}};var P=class extends d{config;twilioClient;server;constructor(e){super(),this.config={port:3e3,...e},this.name=e.name}get isTriggerChannel(){return!this.config.webhookPath}listen(){typeof process<"u"&&import("twilio").then(e=>{this.twilioClient=e.default(this.config.accountSid,this.config.authToken),console.log("[SMSChannel] Twilio client initialized"),this.config.webhookPath&&this.startWebhookServer()}).catch(e=>{console.error("[SMSChannel] Failed to initialize Twilio client:",e),console.error("[SMSChannel] Make sure to install twilio: npm install twilio")})}async send(e){if(!this.twilioClient)throw new Error("Twilio client not initialized. Did you call listen()?");let n=e.metadata?.from||this.config.to;if(!n)throw new Error('No recipient phone number specified. Set "to" in config or provide in output.metadata.from');try{await this.twilioClient.messages.create({body:e.output,from:this.config.from,to:n})}catch(t){throw console.error("[SMSChannel] Failed to send SMS:",t),new Error(`Failed to send SMS: ${t instanceof Error?t.message:String(t)}`)}}normalize(e){let n=e,t=n.From,i=n.Body,r=n.MessageSid;return{message:i,conversationId:t,data:n,context:{from:t,to:n.To,messageSid:r}}}startWebhookServer(){import("http").then(e=>{this.server=e.createServer((n,t)=>{this.handleWebhookRequest(n,t)}),this.server.listen(this.config.port,()=>{console.log(`[SMSChannel] Webhook server listening on port ${this.config.port} at ${this.config.webhookPath}`)})}).catch(e=>{console.error("[SMSChannel] Failed to start webhook server:",e)})}handleWebhookRequest(e,n){if(e.method!=="POST"||e.url!==this.config.webhookPath){n.writeHead(404),n.end("Not found");return}let t="";e.on("data",i=>{t+=i.toString()}),e.on("end",()=>{try{let i=new URLSearchParams(t),r={};i.forEach((o,l)=>{r[l]=o});let s=this.normalize(r);this.handleMessage(s),n.writeHead(200,{"Content-Type":"text/xml"}),n.end('<?xml version="1.0" encoding="UTF-8"?><Response></Response>')}catch(i){console.error("[SMSChannel] Error handling webhook:",i),n.writeHead(400),n.end("Bad request")}})}async stop(){if(this.server)return new Promise(e=>{this.server.close(e)})}};var x=class extends d{isTriggerChannel=!1;_timeout;_pendingResolve;constructor(e={}){super(),this._timeout=e.timeout??12e4}listen(){}async send(e){this._pendingResolve?.(e),this._pendingResolve=void 0}normalize(e){let n=e;return{message:typeof n.message=="string"?n.message:JSON.stringify(n),data:n,conversationId:`mcp-${Date.now()}-${Math.random().toString(36).slice(2,7)}`}}async trigger(e){let n=this.normalize(e);return new Promise((t,i)=>{let r=setTimeout(()=>{this._pendingResolve=void 0,i(new Error(`McpChannel: agent did not respond within ${this._timeout}ms`))},this._timeout);this._pendingResolve=s=>{clearTimeout(r),t(s.output)},this.handleMessage(n).catch(s=>{clearTimeout(r),this._pendingResolve=void 0,i(s instanceof Error?s:new Error(String(s)))})})}asAgentDefinition(e,n){return{name:e.name,description:e.description,...n!==void 0&&{inputSchema:n},invoke:t=>this.trigger(t)}}};0&&(module.exports={BaseChannel,DiscordChannel,EmailChannel,McpChannel,SMSChannel,ScheduledChannel,SlackChannel,TelegramChannel,WebhookChannel});
@@ -1,4 +1,4 @@
1
- export { B as BaseChannel, D as DiscordChannel, b as DiscordChannelConfig, E as EmailChannel, c as EmailChannelConfig, d as SMSChannel, e as SMSChannelConfig, f as ScheduledChannel, g as ScheduledChannelConfig, i as SlackChannel, j as SlackChannelConfig, T as TelegramChannel, k as TelegramChannelConfig, W as WebhookChannel, l as WebhookChannelConfig } from '../index-Du6S0eG7.cjs';
2
- import '../types-TB6yypig.cjs';
1
+ export { B as BaseChannel, D as DiscordChannel, b as DiscordChannelConfig, E as EmailChannel, c as EmailChannelConfig, M as McpChannel, d as McpChannelConfig, e as SMSChannel, f as SMSChannelConfig, g as ScheduledChannel, h as ScheduledChannelConfig, j as SlackChannel, k as SlackChannelConfig, T as TelegramChannel, l as TelegramChannelConfig, W as WebhookChannel, m as WebhookChannelConfig } from '../index-DrigwC1A.cjs';
2
+ import '../types-C3eW-auY.cjs';
3
3
  import 'toolpack-sdk';
4
4
  import 'events';
@@ -1,4 +1,4 @@
1
- export { B as BaseChannel, D as DiscordChannel, b as DiscordChannelConfig, E as EmailChannel, c as EmailChannelConfig, d as SMSChannel, e as SMSChannelConfig, f as ScheduledChannel, g as ScheduledChannelConfig, i as SlackChannel, j as SlackChannelConfig, T as TelegramChannel, k as TelegramChannelConfig, W as WebhookChannel, l as WebhookChannelConfig } from '../index-o8Lbzv5N.js';
2
- import '../types-TB6yypig.js';
1
+ export { B as BaseChannel, D as DiscordChannel, b as DiscordChannelConfig, E as EmailChannel, c as EmailChannelConfig, M as McpChannel, d as McpChannelConfig, e as SMSChannel, f as SMSChannelConfig, g as ScheduledChannel, h as ScheduledChannelConfig, j as SlackChannel, k as SlackChannelConfig, T as TelegramChannel, l as TelegramChannelConfig, W as WebhookChannel, m as WebhookChannelConfig } from '../index-BxUlu-qG.js';
2
+ import '../types-C3eW-auY.js';
3
3
  import 'toolpack-sdk';
4
4
  import 'events';
@@ -1,3 +1,3 @@
1
- var l=class{name;_handler;onMessage(e){this._handler=e}async handleMessage(e){this._handler&&await this._handler(e)}};import{createHmac as $,timingSafeEqual as _}from"crypto";var C=class extends l{isTriggerChannel=!1;config;server;participantCache=new Map;botUserId;allowedChannels;constructor(e){super(),this.config={port:3e3,...e},this.name=e.name;let n=e.channel;this.allowedChannels=n==null?null:Array.isArray(n)?n:[n]}listen(){typeof process<"u"&&import("http").then(e=>{this.server=e.createServer((n,t)=>{this.handleRequest(n,t)}),this.server.listen(this.config.port,()=>{console.log(`[SlackChannel] Listening on port ${this.config.port}`),this.runStartupCheck().catch(()=>{})})}).catch(e=>{console.error("[SlackChannel] Failed to start HTTP server:",e)})}async runStartupCheck(){try{let n=await(await fetch("https://slack.com/api/auth.test",{method:"POST",headers:{Authorization:`Bearer ${this.config.token}`,"Content-Type":"application/json"}})).json();n.ok?(this.botUserId=n.user_id,console.log(`[SlackChannel] Connected as @${n.user} (${n.user_id}) in workspace "${n.team}" \u2014 ${n.url}`)):console.warn(`[SlackChannel] auth.test failed: ${n.error}. Check your bot token.`)}catch(e){console.warn("[SlackChannel] Startup self-check failed (network error):",e)}}verifySignature(e,n){let t=e["x-slack-request-timestamp"],r=e["x-slack-signature"];if(!t||!r||Array.isArray(t)||Array.isArray(r))return!1;let i=parseInt(t,10),s=Math.floor(Date.now()/1e3);if(isNaN(i)||Math.abs(s-i)>300)return!1;let o=`v0:${t}:${n}`,d=`v0=${$("sha256",this.config.signingSecret).update(o).digest("hex")}`;if(d.length!==r.length)return!1;try{return _(Buffer.from(d),Buffer.from(r))}catch{return!1}}async send(e){let n=e.metadata?.threadTs??e.metadata?.thread_ts??e.metadata?.threadId,r=e.metadata?.channelId??(this.allowedChannels&&this.allowedChannels.length>0?this.allowedChannels[0]:void 0);if(!r)throw new Error("[SlackChannel] Cannot send: no channel configured and metadata.channelId is missing. Provide a target via SlackChannelConfig.channel or output.metadata.channelId.");let i={channel:r,text:e.output};n&&(i.thread_ts=n);let s=await fetch("https://slack.com/api/chat.postMessage",{method:"POST",headers:{Authorization:`Bearer ${this.config.token}`,"Content-Type":"application/json"},body:JSON.stringify(i)});if(!s.ok)throw new Error(`Failed to send Slack message: ${s.statusText}`);let o=await s.json();if(!o.ok)throw new Error(`Slack API error: ${o.error}`)}normalize(e){let n=e,r=(n.text||"").replace(/<(https?:\/\/[^|>]+)\|([^>]+)>/g,"$2 ($1)").replace(/<(https?:\/\/[^>]+)>/g,"$1").replace(/<!here>/g,"@here").replace(/<!channel>/g,"@channel").replace(/<!everyone>/g,"@everyone"),i=n.ts,s=n.thread_ts,o=s!==void 0&&s!==i,a=n.user,d=a?{kind:"user",id:a}:void 0,f=/<@([A-Z0-9]+)>/g,g=[],m;for(;(m=f.exec(r))!==null;)g.push(m[1]);let p=n.channel;return{message:r,conversationId:o?s:p||i||"",data:n,participant:d,context:{user:a,channel:p,team:n.team,channelType:n.channel_type,threadId:o?s:void 0,mentions:g.length>0?g:void 0,channelId:p,channelName:typeof this.config.channel=="string"?this.config.channel:p}}}async resolveParticipant(e){let n=e.participant?.id??e.context?.user;if(!n)return;let t=this.participantCache.get(n);if(t)return t;try{let r=await fetch(`https://slack.com/api/users.info?user=${encodeURIComponent(n)}`,{method:"GET",headers:{Authorization:`Bearer ${this.config.token}`}});if(!r.ok)return{kind:"user",id:n};let i=await r.json();if(!i.ok||!i.user){let a={kind:"user",id:n};return this.participantCache.set(n,a),a}let s=i.user.profile?.display_name||i.user.profile?.real_name||i.user.real_name||i.user.name||n,o={kind:"user",id:n,displayName:s,metadata:{slackUser:i.user}};return this.participantCache.set(n,o),o}catch{return{kind:"user",id:n}}}invalidateParticipant(e){this.participantCache.delete(e)}shouldProcessEvent(e){let n=e.type;if(n!=="message"&&n!=="app_mention")return!1;if(this.allowedChannels!==null){let s=e.channel_type;if(!(s==="im"||s==="mpim")){let a=e.channel;if(!a||!this.allowedChannels.includes(a))return!1}}let t=e.user;if(this.botUserId&&t===this.botUserId)return!1;let r=e.bot_id;if(!r)return!0;let i=this.config.blockedBotIds??[];if(i.includes(r)||t!==void 0&&i.includes(t))return!1;if(this.config.allowedBotIds!==void 0){let s=this.config.allowedBotIds;return s.includes(r)||t!==void 0&&s.includes(t)}return!0}handleRequest(e,n){if(e.method!=="POST"){n.writeHead(405),n.end("Method not allowed");return}let t="";e.on("data",r=>{t+=r.toString()}),e.on("end",()=>{if(!this.verifySignature(e.headers,t)){n.writeHead(401),n.end("Invalid signature");return}try{let r=JSON.parse(t);if(r.type==="url_verification"){n.writeHead(200,{"Content-Type":"application/json"}),n.end(JSON.stringify({challenge:r.challenge}));return}if(r.type==="event_callback"&&r.event){let i=r.event;if(this.shouldProcessEvent(i)){let s=this.normalize(i);this.handleMessage(s)}else i.type==="user_change"&&i.user&&this.invalidateParticipant(i.user.id);n.writeHead(200),n.end("OK");return}n.writeHead(200),n.end("OK")}catch(r){console.error("[SlackChannel] Error handling request:",r),n.writeHead(400),n.end("Bad request")}})}async stop(){if(this.server)return new Promise(e=>{this.server.close(e)})}};var v=class extends l{isTriggerChannel=!1;config;server;pendingResponses=new Map;constructor(e){super(),this.name=e.name,this.config={port:e.port??3e3,path:e.path??"/webhook"}}listen(){typeof process<"u"&&import("http").then(e=>{this.server=e.createServer((n,t)=>{this.handleRequest(n,t)}),this.server.listen(this.config.port,()=>{console.log(`[WebhookChannel] Listening on port ${this.config.port}${this.config.path}`)})}).catch(e=>{console.error("[WebhookChannel] Failed to start HTTP server:",e)})}async send(e){let n=e.metadata?.conversationId;if(n&&this.pendingResponses.has(n)){let t=this.pendingResponses.get(n);this.pendingResponses.delete(n),t.resolve({output:e.output,metadata:e.metadata})}}normalize(e){let n=e,t=n.headers||{},r=t["x-session-id"]||t["X-Session-Id"]||n.sessionId||n.conversationId||this.generateSessionId();return{message:n.message||n.text||"",intent:n.intent,conversationId:r,data:n,context:{headers:n.headers,method:n.method,sessionId:r}}}handleRequest(e,n){if(e.url!==this.config.path){n.writeHead(404),n.end("Not found");return}if(e.method!=="POST"){n.writeHead(405),n.end("Method not allowed");return}let t="";e.on("data",r=>{t+=r.toString()}),e.on("end",()=>{try{let r=JSON.parse(t),i=this.normalize(r),s=i.conversationId||this.generateSessionId(),o=new Promise((a,d)=>{this.pendingResponses.set(s,{resolve:a,reject:d}),setTimeout(()=>{this.pendingResponses.has(s)&&(this.pendingResponses.delete(s),d(new Error("Agent response timeout")))},3e4)});this.handleMessage({...i,conversationId:s,context:{...i.context,sessionId:s}}),o.then(a=>{n.writeHead(200,{"Content-Type":"application/json"}),n.end(JSON.stringify(a))}).catch(a=>{n.writeHead(500,{"Content-Type":"application/json"}),n.end(JSON.stringify({error:a.message}))})}catch(r){console.error("[WebhookChannel] Error handling request:",r),n.writeHead(400,{"Content-Type":"application/json"}),n.end(JSON.stringify({error:"Bad request"}))}})}generateSessionId(){return`webhook-${Date.now()}-${Math.random().toString(36).substring(2,9)}`}async stop(){if(this.server)return new Promise(e=>{this.server.close(e)})}};import{CronExpressionParser as P}from"cron-parser";var k=class extends l{isTriggerChannel=!0;config;timer;_stopped=!1;_generation=0;constructor(e){if(super(),!e.cron&&!e.store)throw new Error("ScheduledChannel: provide at least one of `cron` (static schedule) or `store` (dynamic scheduling).");if(e.cron)try{P.parse(e.cron)}catch(n){throw new Error(`ScheduledChannel: invalid cron expression '${e.cron}': ${n.message}`)}if(e.store&&!e.name&&console.warn("[ScheduledChannel] A `store` was provided without a `name`. All store queries will be unscoped and will pick up jobs from every channel. Set `name` to scope this channel to its own jobs."),e.idlePollMs!==void 0&&e.idlePollMs<1e3)throw new Error(`ScheduledChannel: idlePollMs must be at least 1000ms (got ${e.idlePollMs}). Values below 1 second create a tight polling loop.`);this.config=e,this.name=e.name}listen(){this._generation++,this.timer&&(clearTimeout(this.timer),this.timer=void 0),this._stopped=!1,this.config.store?this._listenWithStore():this._listenStatic()}async stop(){this._stopped=!0,this.timer&&(clearTimeout(this.timer),this.timer=void 0)}async send(e){}normalize(e){let n=e,t=new Date,r=`${t.getFullYear()}-${t.getMonth()+1}-${t.getDate()}`;return{intent:n?.intent??this.config.intent,message:n?.message??this.config.message??`Scheduled task triggered at ${t.toISOString()}`,conversationId:`scheduled:${this.name??"default"}:${r}`,data:{...n?.payload??{},scheduled:!0,jobId:n?.id,cron:n?.cron??this.config.cron,timestamp:t.toISOString()}}}_listenStatic(){this._scheduleNextStatic(this._generation)}_scheduleNextStatic(e){if(this._stopped||e!==this._generation)return;let n=this._nextRunFromCron(this.config.cron),t=n.getTime()-Date.now();if(t<=0){this.timer=setTimeout(()=>this._scheduleNextStatic(e),0);return}console.log(`[ScheduledChannel:${this.name??"default"}] Next run: ${n.toISOString()}`),this.timer=setTimeout(async()=>{this._stopped||e!==this._generation||(await this._triggerStatic(),this._scheduleNextStatic(e))},t)}async _triggerStatic(){if(!this._handler){console.warn(`[ScheduledChannel:${this.name??"default"}] Cron fired but no message handler is registered. Call onMessage() before listen() to avoid silently losing triggers.`);return}let e=this.normalize(null);try{await this.handleMessage(e)}catch(n){console.error(`[ScheduledChannel:${this.name??"default"}] Error on trigger:`,n)}}_listenWithStore(){let e=this.config.store,n=this._generation;if(n===1){let r=e.resetStuck(this.name);r>0&&console.log(`[ScheduledChannel:${this.name??"default"}] Reset ${r} stuck 'running' job(s) to 'pending'.`)}if(this.config.cron){let{duplicate:r}=e.create({channelName:this.name,cron:this.config.cron,intent:this.config.intent,message:this.config.message});r||console.log(`[ScheduledChannel:${this.name??"default"}] Seeded static cron '${this.config.cron}' into store.`)}let t=e.getDue(Date.now(),this.name);t.length>0&&(console.log(`[ScheduledChannel:${this.name??"default"}] Recovering ${t.length} overdue job(s).`),Promise.allSettled(t.map(r=>this._triggerJob(r)))),this._scheduleNextFromStore(n)}_scheduleNextFromStore(e){if(this._stopped||e!==this._generation)return;let n=this.config.store,t=n.getNextPending(this.name);if(!t){let i=this.config.idlePollMs??3e4;this.timer=setTimeout(()=>this._scheduleNextFromStore(e),i);return}let r=Math.max(0,t.nextRunAt-Date.now());console.log(`[ScheduledChannel:${this.name??"default"}] Next store job at ${new Date(t.nextRunAt).toISOString()} (in ${Math.round(r/1e3)}s)`),this.timer=setTimeout(async()=>{if(this._stopped||e!==this._generation)return;let i=n.getDue(Date.now(),this.name);await Promise.allSettled(i.map(s=>this._triggerJob(s))),this._scheduleNextFromStore(e)},r)}async _triggerJob(e){let n=this.config.store;if(!this._handler){console.warn(`[ScheduledChannel:${this.name??"default"}] Job ${e.id} fired but no message handler is registered. Call onMessage() before listen() to avoid silently losing jobs.`),n.markFailed(e.id,"No message handler registered");return}n.markRunning(e.id);let t=this.normalize(e);try{await this.handleMessage(t),n.markCompleted(e.id)}catch(r){let i=r instanceof Error?r.message:String(r);console.error(`[ScheduledChannel:${this.name??"default"}] Job ${e.id} failed:`,r),n.markFailed(e.id,i)}}_nextRunFromCron(e){return P.parse(e,{currentDate:new Date}).next().toDate()}};var y=class extends l{isTriggerChannel=!1;config;offset=0;pollingInterval;server;botUserId;botUsername;constructor(e){super(),this.name=e.name,this.config=e}listen(){this.runStartupCheck().catch(()=>{}),this.config.webhookUrl?this.startWebhook():this.startPolling()}async runStartupCheck(){try{let n=await(await fetch(`https://api.telegram.org/bot${this.config.token}/getMe`)).json();if(n.ok&&n.result){let t=n.result;this.botUserId=t.id!=null?String(t.id):void 0,this.botUsername=t.username,console.log(`[TelegramChannel] Connected as @${t.username} (id: ${t.id}, name: ${t.first_name})`)}else console.warn(`[TelegramChannel] getMe failed: ${n.description??"unknown error"}. Check your bot token.`)}catch(e){console.warn("[TelegramChannel] Startup self-check failed (network error):",e)}}async send(e){let n=e.metadata?.chatId;if(!n)throw new Error("Telegram send requires chatId in metadata");let t=await fetch(`https://api.telegram.org/bot${this.config.token}/sendMessage`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({chat_id:n,text:e.output,parse_mode:"Markdown"})});if(!t.ok)throw new Error(`Failed to send Telegram message: ${t.statusText}`);let r=await t.json();if(!r.ok)throw new Error(`Telegram API error: ${r.description}`)}normalize(e){let n=e,t=n.message||n.edited_message||{},r=t.text||"",i=t.chat||{},s=t.from||{},o=s.id!=null?String(s.id):void 0,a=s.first_name||s.username||o,d=o?{kind:"user",id:o,displayName:a??o}:void 0,f=t.entities??[],g=[];for(let w of f)if(w.type==="text_mention"&&w.user){let T=w.user;T.id!=null&&g.push(String(T.id))}let m=i.type,p=i.id!=null?String(i.id):"";return{message:r,conversationId:p,data:n,participant:d,context:{chatId:i.id,userId:s.id,username:s.username,firstName:s.first_name,lastName:s.last_name,messageId:t.message_id,channelType:m,channelId:p,channelName:i.title,mentions:g.length>0?g:void 0}}}startPolling(){console.log("[TelegramChannel] Starting polling mode"),this.pollingInterval=setInterval(async()=>{try{await this.pollUpdates()}catch(e){console.error("[TelegramChannel] Polling error:",e)}},5e3)}async pollUpdates(){let e=`https://api.telegram.org/bot${this.config.token}/getUpdates?offset=${this.offset}&limit=100`,n=await fetch(e);if(!n.ok)throw new Error(`Telegram getUpdates failed: ${n.statusText}`);let t=await n.json();if(!t.ok)throw new Error("Telegram getUpdates returned not ok");for(let r of t.result){let i=r.update_id;i>=this.offset&&(this.offset=i+1);try{let s=this.normalize(r);await this.handleMessage(s)}catch(s){console.error("[TelegramChannel] Error processing update:",s)}}}startWebhook(){typeof process>"u"||(console.log("[TelegramChannel] Starting webhook mode"),import("http").then(e=>{this.server=e.createServer((r,i)=>{this.handleWebhookRequest(r,i)});let n=new URL(this.config.webhookUrl||"http://localhost:3000"),t=parseInt(n.port,10)||3e3;this.server.listen(t,()=>{console.log(`[TelegramChannel] Webhook server listening on port ${t}`)}),this.setWebhook()}).catch(e=>{console.error("[TelegramChannel] Failed to start webhook server:",e)}))}async setWebhook(){if(!this.config.webhookUrl)return;let e=await fetch(`https://api.telegram.org/bot${this.config.token}/setWebhook`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({url:this.config.webhookUrl})});if(!e.ok){console.error("[TelegramChannel] Failed to set webhook");return}let n=await e.json();n.ok?console.log("[TelegramChannel] Webhook set successfully"):console.error("[TelegramChannel] Failed to set webhook:",n.description)}handleWebhookRequest(e,n){if(e.method!=="POST"){n.writeHead(405),n.end("Method not allowed");return}let t="";e.on("data",r=>{t+=r.toString()}),e.on("end",()=>{try{let r=JSON.parse(t);this.handleMessage(this.normalize(r)).catch(i=>{console.error("[TelegramChannel] Error processing webhook:",i)}),n.writeHead(200),n.end("OK")}catch(r){console.error("[TelegramChannel] Error parsing webhook:",r),n.writeHead(400),n.end("Bad request")}})}async stop(){if(this.pollingInterval&&(clearInterval(this.pollingInterval),this.pollingInterval=void 0),this.server)return new Promise(e=>{this.server.close(e)});if(this.config.webhookUrl)try{await fetch(`https://api.telegram.org/bot${this.config.token}/deleteWebhook`,{method:"POST"})}catch(e){console.error("[TelegramChannel] Failed to delete webhook:",e)}}};var A=new Set([1,3]),x=/<@!?(\d+)>/g,M="https://discord.com/api/v10",S=class extends l{isTriggerChannel=!1;config;allowedChannelIds;botUserId;participantCache=new Map;client;constructor(e){super(),this.config=e,this.name=e.name,e.channelId==null?this.allowedChannelIds=new Set:Array.isArray(e.channelId)?this.allowedChannelIds=new Set(e.channelId):this.allowedChannelIds=new Set([e.channelId])}shouldProcessEvent(e){return!e.author||e.webhookId||this.botUserId&&e.author.id===this.botUserId||this.config.guildId&&e.guildId!==this.config.guildId||this.allowedChannelIds.size>0&&(!e.channelId||!this.allowedChannelIds.has(e.channelId))?!1:e.author.bot||e.author.system?this.config.blockedBotIds?.includes(e.author.id)?!1:this.config.allowedBotIds?this.config.allowedBotIds.includes(e.author.id):!1:!0}normalize(e){let n=e,t=n.channelId,r=(t??"")+(n.thread?.id?`:${n.thread.id}`:""),i=n.channel?.type,s=i!==void 0&&A.has(i),o=n.author?.id,a=n.author?.globalName??n.author?.username,d=[];if(n.content){x.lastIndex=0;let f;for(;(f=x.exec(n.content))!==null;)d.push(f[1])}return{message:n.content,conversationId:r,data:n,participant:o?{kind:"user",id:o,displayName:a??void 0}:void 0,context:{userId:o,username:n.author?.username,channelType:s?"dm":"channel",channelId:t,channelName:n.channel?.name,guildId:n.guildId,threadId:n.thread?.id,messageId:n.id,mentions:d.length>0?d:void 0,isMentioned:this.botUserId?d.includes(this.botUserId):void 0}}}async resolveParticipant(e){let n=e.participant?.id??e.context?.userId;if(!n)return;let t=this.participantCache.get(n);if(t)return t;try{let r=await fetch(`${M}/users/${n}`,{headers:{Authorization:`Bot ${this.config.token}`}});if(!r.ok)return console.warn(`[DiscordChannel] Failed to resolve user ${n}: HTTP ${r.status}`),{kind:"user",id:n};let i=await r.json(),s=i.global_name??i.username,o={kind:"user",id:i.id,displayName:s};return this.participantCache.set(n,o),o}catch(r){return console.warn(`[DiscordChannel] resolveParticipant error for ${n}:`,r),{kind:"user",id:n}}}invalidateParticipant(e){this.participantCache.delete(e)}async send(e){if(!this.client)throw new Error("[DiscordChannel] Client not initialized. Did you call listen()?");let n=e.metadata?.threadId;if(n){let i=await this.client.channels.fetch(n);if(i&&"send"in i)try{await i.send({content:e.output});return}catch(s){throw console.error("[DiscordChannel] Failed to send to thread:",s),new Error(`[DiscordChannel] Failed to send Discord message: ${s instanceof Error?s.message:String(s)}`)}console.warn(`[DiscordChannel] Thread ${n} not found or not sendable; falling back to channel send.`)}let t=e.metadata?.channelId??(this.allowedChannelIds.size>0?[...this.allowedChannelIds][0]:void 0);if(!t)throw new Error("[DiscordChannel] No channel ID available for sending. Configure channelId or pass output.metadata.channelId.");let r=await this.client.channels.fetch(t);if(!r||!("send"in r))throw new Error(`[DiscordChannel] Channel ${t} not found or is not a text channel`);try{await r.send({content:e.output})}catch(i){throw console.error("[DiscordChannel] Failed to send to channel:",i),new Error(`[DiscordChannel] Failed to send Discord message: ${i instanceof Error?i.message:String(i)}`)}}listen(){typeof process>"u"||import("discord.js").then(e=>{let{Client:n,GatewayIntentBits:t}=e;this.client=new n({intents:[t.Guilds,t.GuildMessages,t.MessageContent,t.DirectMessages]}),this.client.on("ready",()=>{let r=this.client.user;this.botUserId=r?.id;let i=this.allowedChannelIds.size===0?"all channels":[...this.allowedChannelIds].join(", "),s=this.config.guildId??"all guilds";console.log(`[DiscordChannel] Logged in as ${r?.tag??"unknown"} (botUserId=${this.botUserId??"unknown"}) | guild=${s} | channels=${i}`)}),this.client.on("messageCreate",r=>{let i=r;if(!this.shouldProcessEvent(i))return;let s=this.normalize(i);this.handleMessage(s)}),this.client.on("userUpdate",(r,i)=>{let s=i;s?.id&&this.invalidateParticipant(s.id)}),this.client.login(this.config.token).catch(r=>{console.error("[DiscordChannel] Failed to login to Discord:",r)})}).catch(e=>{console.error("[DiscordChannel] Failed to initialize Discord client:",e),console.error("[DiscordChannel] Make sure discord.js is installed: npm install discord.js")})}async stop(){this.client&&(await this.client.destroy(),this.client=void 0)}};var b=class extends l{isTriggerChannel=!0;config;transporter;constructor(e){super(),this.config=e,this.name=e.name}listen(){typeof process<"u"&&import("nodemailer").then(e=>{this.transporter=e.default.createTransport({host:this.config.smtp.host,port:this.config.smtp.port,secure:this.config.smtp.secure??this.config.smtp.port===465,auth:{user:this.config.smtp.auth.user,pass:this.config.smtp.auth.pass}}),console.log(`[EmailChannel] Email transporter initialized for ${this.config.from}`)}).catch(e=>{console.error("[EmailChannel] Failed to initialize nodemailer:",e),console.error("[EmailChannel] Make sure to install nodemailer: npm install nodemailer")})}async send(e){if(!this.transporter)throw new Error("Email transporter not initialized. Did you call listen()?");let n=Array.isArray(this.config.to)?this.config.to:[this.config.to],t=this.config.subject||"Message from Agent",r={from:this.config.from,to:n.join(", "),subject:t,text:e.output,html:this.formatAsHtml(e.output)};try{await this.transporter.sendMail(r)}catch(i){throw console.error("[EmailChannel] Failed to send email:",i),new Error(`Failed to send email: ${i instanceof Error?i.message:String(i)}`)}}normalize(e){throw new Error("EmailChannel is outbound-only. Use WebhookChannel with email webhook events for inbound email.")}formatAsHtml(e){return e.split(`
1
+ var l=class{name;_handler;onMessage(e){this._handler=e}async handleMessage(e){this._handler&&await this._handler(e)}};import{createHmac as $,timingSafeEqual as A}from"crypto";var C=class extends l{isTriggerChannel=!1;config;server;participantCache=new Map;botUserId;allowedChannels;constructor(e){super(),this.config={port:3e3,...e},this.name=e.name;let n=e.channel;this.allowedChannels=n==null?null:Array.isArray(n)?n:[n]}listen(){typeof process<"u"&&import("http").then(e=>{this.server=e.createServer((n,t)=>{this.handleRequest(n,t)}),this.server.listen(this.config.port,()=>{console.log(`[SlackChannel] Listening on port ${this.config.port}`),this.runStartupCheck().catch(()=>{})})}).catch(e=>{console.error("[SlackChannel] Failed to start HTTP server:",e)})}async runStartupCheck(){try{let n=await(await fetch("https://slack.com/api/auth.test",{method:"POST",headers:{Authorization:`Bearer ${this.config.token}`,"Content-Type":"application/json"}})).json();n.ok?(this.botUserId=n.user_id,console.log(`[SlackChannel] Connected as @${n.user} (${n.user_id}) in workspace "${n.team}" \u2014 ${n.url}`)):console.warn(`[SlackChannel] auth.test failed: ${n.error}. Check your bot token.`)}catch(e){console.warn("[SlackChannel] Startup self-check failed (network error):",e)}}verifySignature(e,n){let t=e["x-slack-request-timestamp"],i=e["x-slack-signature"];if(!t||!i||Array.isArray(t)||Array.isArray(i))return!1;let r=parseInt(t,10),s=Math.floor(Date.now()/1e3);if(isNaN(r)||Math.abs(s-r)>300)return!1;let o=`v0:${t}:${n}`,d=`v0=${$("sha256",this.config.signingSecret).update(o).digest("hex")}`;if(d.length!==i.length)return!1;try{return A(Buffer.from(d),Buffer.from(i))}catch{return!1}}async send(e){let n=e.metadata?.threadTs??e.metadata?.thread_ts??e.metadata?.threadId,i=e.metadata?.channelId??(this.allowedChannels&&this.allowedChannels.length>0?this.allowedChannels[0]:void 0);if(!i)throw new Error("[SlackChannel] Cannot send: no channel configured and metadata.channelId is missing. Provide a target via SlackChannelConfig.channel or output.metadata.channelId.");let r={channel:i,text:e.output};n&&(r.thread_ts=n);let s=await fetch("https://slack.com/api/chat.postMessage",{method:"POST",headers:{Authorization:`Bearer ${this.config.token}`,"Content-Type":"application/json"},body:JSON.stringify(r)});if(!s.ok)throw new Error(`Failed to send Slack message: ${s.statusText}`);let o=await s.json();if(!o.ok)throw new Error(`Slack API error: ${o.error}`)}normalize(e){let n=e,i=(n.text||"").replace(/<(https?:\/\/[^|>]+)\|([^>]+)>/g,"$2 ($1)").replace(/<(https?:\/\/[^>]+)>/g,"$1").replace(/<!here>/g,"@here").replace(/<!channel>/g,"@channel").replace(/<!everyone>/g,"@everyone"),r=n.ts,s=n.thread_ts,o=s!==void 0&&s!==r,a=n.user,d=a?{kind:"user",id:a}:void 0,m=/<@([A-Z0-9]+)>/g,u=[],f;for(;(f=m.exec(i))!==null;)u.push(f[1]);let p=n.channel;return{message:i,conversationId:o?s:p||r||"",data:n,participant:d,context:{user:a,channel:p,team:n.team,channelType:n.channel_type,threadId:o?s:void 0,mentions:u.length>0?u:void 0,channelId:p,channelName:typeof this.config.channel=="string"?this.config.channel:p}}}async resolveParticipant(e){let n=e.participant?.id??e.context?.user;if(!n)return;let t=this.participantCache.get(n);if(t)return t;try{let i=await fetch(`https://slack.com/api/users.info?user=${encodeURIComponent(n)}`,{method:"GET",headers:{Authorization:`Bearer ${this.config.token}`}});if(!i.ok)return{kind:"user",id:n};let r=await i.json();if(!r.ok||!r.user){let a={kind:"user",id:n};return this.participantCache.set(n,a),a}let s=r.user.profile?.display_name||r.user.profile?.real_name||r.user.real_name||r.user.name||n,o={kind:"user",id:n,displayName:s,metadata:{slackUser:r.user}};return this.participantCache.set(n,o),o}catch{return{kind:"user",id:n}}}invalidateParticipant(e){this.participantCache.delete(e)}shouldProcessEvent(e){let n=e.type;if(n!=="message"&&n!=="app_mention")return!1;if(this.allowedChannels!==null){let s=e.channel_type;if(!(s==="im"||s==="mpim")){let a=e.channel;if(!a||!this.allowedChannels.includes(a))return!1}}let t=e.user;if(this.botUserId&&t===this.botUserId)return!1;let i=e.bot_id;if(!i)return!0;let r=this.config.blockedBotIds??[];if(r.includes(i)||t!==void 0&&r.includes(t))return!1;if(this.config.allowedBotIds!==void 0){let s=this.config.allowedBotIds;return s.includes(i)||t!==void 0&&s.includes(t)}return!0}handleRequest(e,n){if(e.method!=="POST"){n.writeHead(405),n.end("Method not allowed");return}let t="";e.on("data",i=>{t+=i.toString()}),e.on("end",()=>{if(!this.verifySignature(e.headers,t)){n.writeHead(401),n.end("Invalid signature");return}try{let i=JSON.parse(t);if(i.type==="url_verification"){n.writeHead(200,{"Content-Type":"application/json"}),n.end(JSON.stringify({challenge:i.challenge}));return}if(i.type==="event_callback"&&i.event){let r=i.event;if(this.shouldProcessEvent(r)){let s=this.normalize(r);this.handleMessage(s)}else r.type==="user_change"&&r.user&&this.invalidateParticipant(r.user.id);n.writeHead(200),n.end("OK");return}n.writeHead(200),n.end("OK")}catch(i){console.error("[SlackChannel] Error handling request:",i),n.writeHead(400),n.end("Bad request")}})}async stop(){if(this.server)return new Promise(e=>{this.server.close(e)})}};var v=class extends l{isTriggerChannel=!1;config;server;pendingResponses=new Map;constructor(e){super(),this.name=e.name,this.config={port:e.port??3e3,path:e.path??"/webhook"}}listen(){typeof process<"u"&&import("http").then(e=>{this.server=e.createServer((n,t)=>{this.handleRequest(n,t)}),this.server.listen(this.config.port,()=>{console.log(`[WebhookChannel] Listening on port ${this.config.port}${this.config.path}`)})}).catch(e=>{console.error("[WebhookChannel] Failed to start HTTP server:",e)})}async send(e){let n=e.metadata?.conversationId;if(n&&this.pendingResponses.has(n)){let t=this.pendingResponses.get(n);this.pendingResponses.delete(n),t.resolve({output:e.output,metadata:e.metadata})}}normalize(e){let n=e,t=n.headers||{},i=t["x-session-id"]||t["X-Session-Id"]||n.sessionId||n.conversationId||this.generateSessionId();return{message:n.message||n.text||"",intent:n.intent,conversationId:i,data:n,context:{headers:n.headers,method:n.method,sessionId:i}}}handleRequest(e,n){if(e.url!==this.config.path){n.writeHead(404),n.end("Not found");return}if(e.method!=="POST"){n.writeHead(405),n.end("Method not allowed");return}let t="";e.on("data",i=>{t+=i.toString()}),e.on("end",()=>{try{let i=JSON.parse(t),r=this.normalize(i),s=r.conversationId||this.generateSessionId(),o=new Promise((a,d)=>{this.pendingResponses.set(s,{resolve:a,reject:d}),setTimeout(()=>{this.pendingResponses.has(s)&&(this.pendingResponses.delete(s),d(new Error("Agent response timeout")))},3e4)});this.handleMessage({...r,conversationId:s,context:{...r.context,sessionId:s}}),o.then(a=>{n.writeHead(200,{"Content-Type":"application/json"}),n.end(JSON.stringify(a))}).catch(a=>{n.writeHead(500,{"Content-Type":"application/json"}),n.end(JSON.stringify({error:a.message}))})}catch(i){console.error("[WebhookChannel] Error handling request:",i),n.writeHead(400,{"Content-Type":"application/json"}),n.end(JSON.stringify({error:"Bad request"}))}})}generateSessionId(){return`webhook-${Date.now()}-${Math.random().toString(36).substring(2,9)}`}async stop(){if(this.server)return new Promise(e=>{this.server.close(e)})}};import{CronExpressionParser as x}from"cron-parser";var y=class extends l{isTriggerChannel=!0;config;timer;_stopped=!1;_generation=0;constructor(e){if(super(),!e.cron&&!e.store)throw new Error("ScheduledChannel: provide at least one of `cron` (static schedule) or `store` (dynamic scheduling).");if(e.cron)try{x.parse(e.cron)}catch(n){throw new Error(`ScheduledChannel: invalid cron expression '${e.cron}': ${n.message}`)}if(e.store&&!e.name&&console.warn("[ScheduledChannel] A `store` was provided without a `name`. All store queries will be unscoped and will pick up jobs from every channel. Set `name` to scope this channel to its own jobs."),e.idlePollMs!==void 0&&e.idlePollMs<1e3)throw new Error(`ScheduledChannel: idlePollMs must be at least 1000ms (got ${e.idlePollMs}). Values below 1 second create a tight polling loop.`);this.config=e,this.name=e.name}listen(){this._generation++,this.timer&&(clearTimeout(this.timer),this.timer=void 0),this._stopped=!1,this.config.store?this._listenWithStore():this._listenStatic()}async stop(){this._stopped=!0,this.timer&&(clearTimeout(this.timer),this.timer=void 0)}async send(e){}normalize(e){let n=e,t=new Date,i=`${t.getFullYear()}-${t.getMonth()+1}-${t.getDate()}`;return{intent:n?.intent??this.config.intent,message:n?.message??this.config.message??`Scheduled task triggered at ${t.toISOString()}`,conversationId:`scheduled:${this.name??"default"}:${i}`,data:{...n?.payload??{},scheduled:!0,jobId:n?.id,cron:n?.cron??this.config.cron,timestamp:t.toISOString()}}}_listenStatic(){this._scheduleNextStatic(this._generation)}_scheduleNextStatic(e){if(this._stopped||e!==this._generation)return;let n=this._nextRunFromCron(this.config.cron),t=n.getTime()-Date.now();if(t<=0){this.timer=setTimeout(()=>this._scheduleNextStatic(e),0);return}console.log(`[ScheduledChannel:${this.name??"default"}] Next run: ${n.toISOString()}`),this.timer=setTimeout(async()=>{this._stopped||e!==this._generation||(await this._triggerStatic(),this._scheduleNextStatic(e))},t)}async _triggerStatic(){if(!this._handler){console.warn(`[ScheduledChannel:${this.name??"default"}] Cron fired but no message handler is registered. Call onMessage() before listen() to avoid silently losing triggers.`);return}let e=this.normalize(null);try{await this.handleMessage(e)}catch(n){console.error(`[ScheduledChannel:${this.name??"default"}] Error on trigger:`,n)}}_listenWithStore(){let e=this.config.store,n=this._generation;if(n===1){let i=e.resetStuck(this.name);i>0&&console.log(`[ScheduledChannel:${this.name??"default"}] Reset ${i} stuck 'running' job(s) to 'pending'.`)}if(this.config.cron){let{duplicate:i}=e.create({channelName:this.name,cron:this.config.cron,intent:this.config.intent,message:this.config.message});i||console.log(`[ScheduledChannel:${this.name??"default"}] Seeded static cron '${this.config.cron}' into store.`)}let t=e.getDue(Date.now(),this.name);t.length>0&&(console.log(`[ScheduledChannel:${this.name??"default"}] Recovering ${t.length} overdue job(s).`),Promise.allSettled(t.map(i=>this._triggerJob(i)))),this._scheduleNextFromStore(n)}_scheduleNextFromStore(e){if(this._stopped||e!==this._generation)return;let n=this.config.store,t=n.getNextPending(this.name);if(!t){let r=this.config.idlePollMs??3e4;this.timer=setTimeout(()=>this._scheduleNextFromStore(e),r);return}let i=Math.max(0,t.nextRunAt-Date.now());console.log(`[ScheduledChannel:${this.name??"default"}] Next store job at ${new Date(t.nextRunAt).toISOString()} (in ${Math.round(i/1e3)}s)`),this.timer=setTimeout(async()=>{if(this._stopped||e!==this._generation)return;let r=n.getDue(Date.now(),this.name);await Promise.allSettled(r.map(s=>this._triggerJob(s))),this._scheduleNextFromStore(e)},i)}async _triggerJob(e){let n=this.config.store;if(!this._handler){console.warn(`[ScheduledChannel:${this.name??"default"}] Job ${e.id} fired but no message handler is registered. Call onMessage() before listen() to avoid silently losing jobs.`),n.markFailed(e.id,"No message handler registered");return}n.markRunning(e.id);let t=this.normalize(e);try{await this.handleMessage(t),n.markCompleted(e.id)}catch(i){let r=i instanceof Error?i.message:String(i);console.error(`[ScheduledChannel:${this.name??"default"}] Job ${e.id} failed:`,i),n.markFailed(e.id,r)}}_nextRunFromCron(e){return x.parse(e,{currentDate:new Date}).next().toDate()}};var k=class extends l{isTriggerChannel=!1;config;offset=0;pollingInterval;server;botUserId;botUsername;constructor(e){super(),this.name=e.name,this.config=e}listen(){this.runStartupCheck().catch(()=>{}),this.config.webhookUrl?this.startWebhook():this.startPolling()}async runStartupCheck(){try{let n=await(await fetch(`https://api.telegram.org/bot${this.config.token}/getMe`)).json();if(n.ok&&n.result){let t=n.result;this.botUserId=t.id!=null?String(t.id):void 0,this.botUsername=t.username,console.log(`[TelegramChannel] Connected as @${t.username} (id: ${t.id}, name: ${t.first_name})`)}else console.warn(`[TelegramChannel] getMe failed: ${n.description??"unknown error"}. Check your bot token.`)}catch(e){console.warn("[TelegramChannel] Startup self-check failed (network error):",e)}}async send(e){let n=e.metadata?.chatId;if(!n)throw new Error("Telegram send requires chatId in metadata");let t=await fetch(`https://api.telegram.org/bot${this.config.token}/sendMessage`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({chat_id:n,text:e.output,parse_mode:"Markdown"})});if(!t.ok)throw new Error(`Failed to send Telegram message: ${t.statusText}`);let i=await t.json();if(!i.ok)throw new Error(`Telegram API error: ${i.description}`)}normalize(e){let n=e,t=n.message||n.edited_message||{},i=t.text||"",r=t.chat||{},s=t.from||{},o=s.id!=null?String(s.id):void 0,a=s.first_name||s.username||o,d=o?{kind:"user",id:o,displayName:a??o}:void 0,m=t.entities??[],u=[];for(let w of m)if(w.type==="text_mention"&&w.user){let P=w.user;P.id!=null&&u.push(String(P.id))}let f=r.type,p=r.id!=null?String(r.id):"";return{message:i,conversationId:p,data:n,participant:d,context:{chatId:r.id,userId:s.id,username:s.username,firstName:s.first_name,lastName:s.last_name,messageId:t.message_id,channelType:f,channelId:p,channelName:r.title,mentions:u.length>0?u:void 0}}}startPolling(){console.log("[TelegramChannel] Starting polling mode"),this.pollingInterval=setInterval(async()=>{try{await this.pollUpdates()}catch(e){console.error("[TelegramChannel] Polling error:",e)}},5e3)}async pollUpdates(){let e=`https://api.telegram.org/bot${this.config.token}/getUpdates?offset=${this.offset}&limit=100`,n=await fetch(e);if(!n.ok)throw new Error(`Telegram getUpdates failed: ${n.statusText}`);let t=await n.json();if(!t.ok)throw new Error("Telegram getUpdates returned not ok");for(let i of t.result){let r=i.update_id;r>=this.offset&&(this.offset=r+1);try{let s=this.normalize(i);await this.handleMessage(s)}catch(s){console.error("[TelegramChannel] Error processing update:",s)}}}startWebhook(){typeof process>"u"||(console.log("[TelegramChannel] Starting webhook mode"),import("http").then(e=>{this.server=e.createServer((i,r)=>{this.handleWebhookRequest(i,r)});let n=new URL(this.config.webhookUrl||"http://localhost:3000"),t=parseInt(n.port,10)||3e3;this.server.listen(t,()=>{console.log(`[TelegramChannel] Webhook server listening on port ${t}`)}),this.setWebhook()}).catch(e=>{console.error("[TelegramChannel] Failed to start webhook server:",e)}))}async setWebhook(){if(!this.config.webhookUrl)return;let e=await fetch(`https://api.telegram.org/bot${this.config.token}/setWebhook`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({url:this.config.webhookUrl})});if(!e.ok){console.error("[TelegramChannel] Failed to set webhook");return}let n=await e.json();n.ok?console.log("[TelegramChannel] Webhook set successfully"):console.error("[TelegramChannel] Failed to set webhook:",n.description)}handleWebhookRequest(e,n){if(e.method!=="POST"){n.writeHead(405),n.end("Method not allowed");return}let t="";e.on("data",i=>{t+=i.toString()}),e.on("end",()=>{try{let i=JSON.parse(t);this.handleMessage(this.normalize(i)).catch(r=>{console.error("[TelegramChannel] Error processing webhook:",r)}),n.writeHead(200),n.end("OK")}catch(i){console.error("[TelegramChannel] Error parsing webhook:",i),n.writeHead(400),n.end("Bad request")}})}async stop(){if(this.pollingInterval&&(clearInterval(this.pollingInterval),this.pollingInterval=void 0),this.server)return new Promise(e=>{this.server.close(e)});if(this.config.webhookUrl)try{await fetch(`https://api.telegram.org/bot${this.config.token}/deleteWebhook`,{method:"POST"})}catch(e){console.error("[TelegramChannel] Failed to delete webhook:",e)}}};var M=new Set([1,3]),_=/<@!?(\d+)>/g,D="https://discord.com/api/v10",S=class extends l{isTriggerChannel=!1;config;allowedChannelIds;botUserId;participantCache=new Map;client;constructor(e){super(),this.config=e,this.name=e.name,e.channelId==null?this.allowedChannelIds=new Set:Array.isArray(e.channelId)?this.allowedChannelIds=new Set(e.channelId):this.allowedChannelIds=new Set([e.channelId])}shouldProcessEvent(e){return!e.author||e.webhookId||this.botUserId&&e.author.id===this.botUserId||this.config.guildId&&e.guildId!==this.config.guildId||this.allowedChannelIds.size>0&&(!e.channelId||!this.allowedChannelIds.has(e.channelId))?!1:e.author.bot||e.author.system?this.config.blockedBotIds?.includes(e.author.id)?!1:this.config.allowedBotIds?this.config.allowedBotIds.includes(e.author.id):!1:!0}normalize(e){let n=e,t=n.channelId,i=(t??"")+(n.thread?.id?`:${n.thread.id}`:""),r=n.channel?.type,s=r!==void 0&&M.has(r),o=n.author?.id,a=n.author?.globalName??n.author?.username,d=[];if(n.content){_.lastIndex=0;let m;for(;(m=_.exec(n.content))!==null;)d.push(m[1])}return{message:n.content,conversationId:i,data:n,participant:o?{kind:"user",id:o,displayName:a??void 0}:void 0,context:{userId:o,username:n.author?.username,channelType:s?"dm":"channel",channelId:t,channelName:n.channel?.name,guildId:n.guildId,threadId:n.thread?.id,messageId:n.id,mentions:d.length>0?d:void 0,isMentioned:this.botUserId?d.includes(this.botUserId):void 0}}}async resolveParticipant(e){let n=e.participant?.id??e.context?.userId;if(!n)return;let t=this.participantCache.get(n);if(t)return t;try{let i=await fetch(`${D}/users/${n}`,{headers:{Authorization:`Bot ${this.config.token}`}});if(!i.ok)return console.warn(`[DiscordChannel] Failed to resolve user ${n}: HTTP ${i.status}`),{kind:"user",id:n};let r=await i.json(),s=r.global_name??r.username,o={kind:"user",id:r.id,displayName:s};return this.participantCache.set(n,o),o}catch(i){return console.warn(`[DiscordChannel] resolveParticipant error for ${n}:`,i),{kind:"user",id:n}}}invalidateParticipant(e){this.participantCache.delete(e)}async send(e){if(!this.client)throw new Error("[DiscordChannel] Client not initialized. Did you call listen()?");let n=e.metadata?.threadId;if(n){let r=await this.client.channels.fetch(n);if(r&&"send"in r)try{await r.send({content:e.output});return}catch(s){throw console.error("[DiscordChannel] Failed to send to thread:",s),new Error(`[DiscordChannel] Failed to send Discord message: ${s instanceof Error?s.message:String(s)}`)}console.warn(`[DiscordChannel] Thread ${n} not found or not sendable; falling back to channel send.`)}let t=e.metadata?.channelId??(this.allowedChannelIds.size>0?[...this.allowedChannelIds][0]:void 0);if(!t)throw new Error("[DiscordChannel] No channel ID available for sending. Configure channelId or pass output.metadata.channelId.");let i=await this.client.channels.fetch(t);if(!i||!("send"in i))throw new Error(`[DiscordChannel] Channel ${t} not found or is not a text channel`);try{await i.send({content:e.output})}catch(r){throw console.error("[DiscordChannel] Failed to send to channel:",r),new Error(`[DiscordChannel] Failed to send Discord message: ${r instanceof Error?r.message:String(r)}`)}}listen(){typeof process>"u"||import("discord.js").then(e=>{let{Client:n,GatewayIntentBits:t}=e;this.client=new n({intents:[t.Guilds,t.GuildMessages,t.MessageContent,t.DirectMessages]}),this.client.on("ready",()=>{let i=this.client.user;this.botUserId=i?.id;let r=this.allowedChannelIds.size===0?"all channels":[...this.allowedChannelIds].join(", "),s=this.config.guildId??"all guilds";console.log(`[DiscordChannel] Logged in as ${i?.tag??"unknown"} (botUserId=${this.botUserId??"unknown"}) | guild=${s} | channels=${r}`)}),this.client.on("messageCreate",i=>{let r=i;if(!this.shouldProcessEvent(r))return;let s=this.normalize(r);this.handleMessage(s)}),this.client.on("userUpdate",(i,r)=>{let s=r;s?.id&&this.invalidateParticipant(s.id)}),this.client.login(this.config.token).catch(i=>{console.error("[DiscordChannel] Failed to login to Discord:",i)})}).catch(e=>{console.error("[DiscordChannel] Failed to initialize Discord client:",e),console.error("[DiscordChannel] Make sure discord.js is installed: npm install discord.js")})}async stop(){this.client&&(await this.client.destroy(),this.client=void 0)}};var b=class extends l{isTriggerChannel=!0;config;transporter;constructor(e){super(),this.config=e,this.name=e.name}listen(){typeof process<"u"&&import("nodemailer").then(e=>{this.transporter=e.default.createTransport({host:this.config.smtp.host,port:this.config.smtp.port,secure:this.config.smtp.secure??this.config.smtp.port===465,auth:{user:this.config.smtp.auth.user,pass:this.config.smtp.auth.pass}}),console.log(`[EmailChannel] Email transporter initialized for ${this.config.from}`)}).catch(e=>{console.error("[EmailChannel] Failed to initialize nodemailer:",e),console.error("[EmailChannel] Make sure to install nodemailer: npm install nodemailer")})}async send(e){if(!this.transporter)throw new Error("Email transporter not initialized. Did you call listen()?");let n=Array.isArray(this.config.to)?this.config.to:[this.config.to],t=this.config.subject||"Message from Agent",i={from:this.config.from,to:n.join(", "),subject:t,text:e.output,html:this.formatAsHtml(e.output)};try{await this.transporter.sendMail(i)}catch(r){throw console.error("[EmailChannel] Failed to send email:",r),new Error(`Failed to send email: ${r instanceof Error?r.message:String(r)}`)}}normalize(e){throw new Error("EmailChannel is outbound-only. Use WebhookChannel with email webhook events for inbound email.")}formatAsHtml(e){return e.split(`
2
2
 
3
- `).map(n=>`<p>${n.replace(/\n/g,"<br>")}</p>`).join("")}async stop(){this.transporter&&(this.transporter.close(),this.transporter=void 0)}};var I=class extends l{config;twilioClient;server;constructor(e){super(),this.config={port:3e3,...e},this.name=e.name}get isTriggerChannel(){return!this.config.webhookPath}listen(){typeof process<"u"&&import("twilio").then(e=>{this.twilioClient=e.default(this.config.accountSid,this.config.authToken),console.log("[SMSChannel] Twilio client initialized"),this.config.webhookPath&&this.startWebhookServer()}).catch(e=>{console.error("[SMSChannel] Failed to initialize Twilio client:",e),console.error("[SMSChannel] Make sure to install twilio: npm install twilio")})}async send(e){if(!this.twilioClient)throw new Error("Twilio client not initialized. Did you call listen()?");let n=e.metadata?.from||this.config.to;if(!n)throw new Error('No recipient phone number specified. Set "to" in config or provide in output.metadata.from');try{await this.twilioClient.messages.create({body:e.output,from:this.config.from,to:n})}catch(t){throw console.error("[SMSChannel] Failed to send SMS:",t),new Error(`Failed to send SMS: ${t instanceof Error?t.message:String(t)}`)}}normalize(e){let n=e,t=n.From,r=n.Body,i=n.MessageSid;return{message:r,conversationId:t,data:n,context:{from:t,to:n.To,messageSid:i}}}startWebhookServer(){import("http").then(e=>{this.server=e.createServer((n,t)=>{this.handleWebhookRequest(n,t)}),this.server.listen(this.config.port,()=>{console.log(`[SMSChannel] Webhook server listening on port ${this.config.port} at ${this.config.webhookPath}`)})}).catch(e=>{console.error("[SMSChannel] Failed to start webhook server:",e)})}handleWebhookRequest(e,n){if(e.method!=="POST"||e.url!==this.config.webhookPath){n.writeHead(404),n.end("Not found");return}let t="";e.on("data",r=>{t+=r.toString()}),e.on("end",()=>{try{let r=new URLSearchParams(t),i={};r.forEach((o,a)=>{i[a]=o});let s=this.normalize(i);this.handleMessage(s),n.writeHead(200,{"Content-Type":"text/xml"}),n.end('<?xml version="1.0" encoding="UTF-8"?><Response></Response>')}catch(r){console.error("[SMSChannel] Error handling webhook:",r),n.writeHead(400),n.end("Bad request")}})}async stop(){if(this.server)return new Promise(e=>{this.server.close(e)})}};export{l as BaseChannel,S as DiscordChannel,b as EmailChannel,I as SMSChannel,k as ScheduledChannel,C as SlackChannel,y as TelegramChannel,v as WebhookChannel};
3
+ `).map(n=>`<p>${n.replace(/\n/g,"<br>")}</p>`).join("")}async stop(){this.transporter&&(this.transporter.close(),this.transporter=void 0)}};var I=class extends l{config;twilioClient;server;constructor(e){super(),this.config={port:3e3,...e},this.name=e.name}get isTriggerChannel(){return!this.config.webhookPath}listen(){typeof process<"u"&&import("twilio").then(e=>{this.twilioClient=e.default(this.config.accountSid,this.config.authToken),console.log("[SMSChannel] Twilio client initialized"),this.config.webhookPath&&this.startWebhookServer()}).catch(e=>{console.error("[SMSChannel] Failed to initialize Twilio client:",e),console.error("[SMSChannel] Make sure to install twilio: npm install twilio")})}async send(e){if(!this.twilioClient)throw new Error("Twilio client not initialized. Did you call listen()?");let n=e.metadata?.from||this.config.to;if(!n)throw new Error('No recipient phone number specified. Set "to" in config or provide in output.metadata.from');try{await this.twilioClient.messages.create({body:e.output,from:this.config.from,to:n})}catch(t){throw console.error("[SMSChannel] Failed to send SMS:",t),new Error(`Failed to send SMS: ${t instanceof Error?t.message:String(t)}`)}}normalize(e){let n=e,t=n.From,i=n.Body,r=n.MessageSid;return{message:i,conversationId:t,data:n,context:{from:t,to:n.To,messageSid:r}}}startWebhookServer(){import("http").then(e=>{this.server=e.createServer((n,t)=>{this.handleWebhookRequest(n,t)}),this.server.listen(this.config.port,()=>{console.log(`[SMSChannel] Webhook server listening on port ${this.config.port} at ${this.config.webhookPath}`)})}).catch(e=>{console.error("[SMSChannel] Failed to start webhook server:",e)})}handleWebhookRequest(e,n){if(e.method!=="POST"||e.url!==this.config.webhookPath){n.writeHead(404),n.end("Not found");return}let t="";e.on("data",i=>{t+=i.toString()}),e.on("end",()=>{try{let i=new URLSearchParams(t),r={};i.forEach((o,a)=>{r[a]=o});let s=this.normalize(r);this.handleMessage(s),n.writeHead(200,{"Content-Type":"text/xml"}),n.end('<?xml version="1.0" encoding="UTF-8"?><Response></Response>')}catch(i){console.error("[SMSChannel] Error handling webhook:",i),n.writeHead(400),n.end("Bad request")}})}async stop(){if(this.server)return new Promise(e=>{this.server.close(e)})}};var T=class extends l{isTriggerChannel=!1;_timeout;_pendingResolve;constructor(e={}){super(),this._timeout=e.timeout??12e4}listen(){}async send(e){this._pendingResolve?.(e),this._pendingResolve=void 0}normalize(e){let n=e;return{message:typeof n.message=="string"?n.message:JSON.stringify(n),data:n,conversationId:`mcp-${Date.now()}-${Math.random().toString(36).slice(2,7)}`}}async trigger(e){let n=this.normalize(e);return new Promise((t,i)=>{let r=setTimeout(()=>{this._pendingResolve=void 0,i(new Error(`McpChannel: agent did not respond within ${this._timeout}ms`))},this._timeout);this._pendingResolve=s=>{clearTimeout(r),t(s.output)},this.handleMessage(n).catch(s=>{clearTimeout(r),this._pendingResolve=void 0,i(s instanceof Error?s:new Error(String(s)))})})}asAgentDefinition(e,n){return{name:e.name,description:e.description,...n!==void 0&&{inputSchema:n},invoke:t=>this.trigger(t)}}};export{l as BaseChannel,S as DiscordChannel,b as EmailChannel,T as McpChannel,I as SMSChannel,y as ScheduledChannel,C as SlackChannel,k as TelegramChannel,v as WebhookChannel};
@@ -0,0 +1,343 @@
1
+ import { B as BaseAgent } from './base-agent-65162dq7.cjs';
2
+
3
+ /**
4
+ * Eval primitives — shared types across EvalDataset, EvalRunner, EvalScorer, and EvalReport.
5
+ */
6
+ /**
7
+ * A single eval case: an input fed to the agent and the expected output used
8
+ * for scoring.
9
+ */
10
+ interface EvalCase {
11
+ /** Unique identifier for this case. */
12
+ id: string;
13
+ /** The input passed to `agent.invokeAgent()`. */
14
+ input: {
15
+ message: string;
16
+ intent?: string;
17
+ conversationId?: string;
18
+ context?: Record<string, unknown>;
19
+ };
20
+ /**
21
+ * The expected output used by scorers.
22
+ * Exact-match and contains scorers compare `actualOutput` against this.
23
+ * LLM-judge scorers use it as the reference answer.
24
+ */
25
+ expectedOutput: string;
26
+ /** Optional free-form metadata (e.g. tags, difficulty, source). */
27
+ metadata?: Record<string, unknown>;
28
+ }
29
+ /**
30
+ * The actual output produced by running a single eval case against an agent.
31
+ */
32
+ interface EvalCaseResult {
33
+ /** The eval case that was run. */
34
+ evalCase: EvalCase;
35
+ /** The output produced by the agent. */
36
+ actualOutput: string;
37
+ /** Wall-clock duration in milliseconds. */
38
+ durationMs: number;
39
+ /** Error message if the agent threw, otherwise undefined. */
40
+ error?: string;
41
+ }
42
+ /**
43
+ * The result of running an entire dataset through an agent.
44
+ */
45
+ interface EvalRun {
46
+ /** Identifier for this run (e.g. "v1.2", "pr-456"). */
47
+ runId: string;
48
+ /** ISO timestamp of when the run started. */
49
+ startedAt: string;
50
+ /** ISO timestamp of when the run completed. */
51
+ completedAt: string;
52
+ /** Total wall-clock duration in milliseconds. */
53
+ totalDurationMs: number;
54
+ /** Per-case results, in dataset order. */
55
+ results: EvalCaseResult[];
56
+ }
57
+ /** The verdict for a single scored case. */
58
+ type EvalVerdict = 'pass' | 'fail';
59
+ /**
60
+ * A scored result — wraps an EvalCaseResult with a pass/fail verdict and
61
+ * an optional explanation.
62
+ */
63
+ interface EvalScoredResult {
64
+ /** The underlying case result. */
65
+ caseResult: EvalCaseResult;
66
+ /** Pass or fail. */
67
+ verdict: EvalVerdict;
68
+ /**
69
+ * Optional human-readable explanation of the verdict.
70
+ * Populated by LLMJudgeScorer; optional for other scorers.
71
+ */
72
+ explanation?: string;
73
+ }
74
+ /**
75
+ * A fully scored run — an EvalRun annotated with per-case verdicts and
76
+ * aggregate pass/fail counts.
77
+ */
78
+ interface EvalScoredRun {
79
+ /** The original run. */
80
+ run: EvalRun;
81
+ /** Scored results, in run order. */
82
+ scoredResults: EvalScoredResult[];
83
+ /** Number of passing cases. */
84
+ passCount: number;
85
+ /** Number of failing cases. */
86
+ failCount: number;
87
+ /** Pass rate as a fraction between 0 and 1. */
88
+ passRate: number;
89
+ }
90
+ /**
91
+ * A regression entry — a case that passed in the baseline but fails in the
92
+ * candidate.
93
+ */
94
+ interface EvalRegression {
95
+ caseId: string;
96
+ baselineOutput: string;
97
+ candidateOutput: string;
98
+ }
99
+ /**
100
+ * An improvement entry — a case that failed in the baseline but passes in the
101
+ * candidate.
102
+ */
103
+ interface EvalImprovement {
104
+ caseId: string;
105
+ baselineOutput: string;
106
+ candidateOutput: string;
107
+ }
108
+ /**
109
+ * Comparison report between a baseline scored run and a candidate scored run.
110
+ */
111
+ interface EvalReport {
112
+ baselineRunId: string;
113
+ candidateRunId: string;
114
+ baselinePassRate: number;
115
+ candidatePassRate: number;
116
+ /** Δ pass rate (candidate − baseline). Positive = improvement. */
117
+ delta: number;
118
+ regressions: EvalRegression[];
119
+ improvements: EvalImprovement[];
120
+ /** Cases that passed in both runs. */
121
+ stablePasses: string[];
122
+ /** Cases that failed in both runs. */
123
+ stableFails: string[];
124
+ }
125
+
126
+ /**
127
+ * A collection of eval cases that can be loaded from / saved to JSON.
128
+ *
129
+ * @example
130
+ * ```ts
131
+ * const dataset = new EvalDataset([
132
+ * {
133
+ * id: 'q1',
134
+ * input: { message: 'What is 2 + 2?' },
135
+ * expectedOutput: '4',
136
+ * },
137
+ * ]);
138
+ *
139
+ * dataset.save('./evals/math.json');
140
+ *
141
+ * const loaded = EvalDataset.load('./evals/math.json');
142
+ * ```
143
+ */
144
+ declare class EvalDataset {
145
+ private _cases;
146
+ constructor(cases?: EvalCase[]);
147
+ /** All cases in the dataset. */
148
+ get cases(): EvalCase[];
149
+ /** Number of cases. */
150
+ get size(): number;
151
+ /**
152
+ * Get a case by ID.
153
+ * Returns `undefined` if not found.
154
+ */
155
+ get(id: string): EvalCase | undefined;
156
+ /**
157
+ * Add one or more cases.
158
+ * Throws if a case with the same ID already exists.
159
+ */
160
+ add(...cases: EvalCase[]): this;
161
+ /**
162
+ * Remove a case by ID.
163
+ * Returns `true` if removed, `false` if not found.
164
+ */
165
+ remove(id: string): boolean;
166
+ /**
167
+ * Filter cases by a predicate. Returns a new EvalDataset.
168
+ */
169
+ filter(predicate: (c: EvalCase) => boolean): EvalDataset;
170
+ /**
171
+ * Serialize to a plain array (suitable for `JSON.stringify`).
172
+ */
173
+ toJSON(): EvalCase[];
174
+ /**
175
+ * Save cases to a JSON file.
176
+ *
177
+ * @param filePath Absolute or relative path to the output file.
178
+ */
179
+ save(filePath: string): void;
180
+ /**
181
+ * Load cases from a JSON file.
182
+ * The file must contain a JSON array of `EvalCase` objects.
183
+ *
184
+ * @param filePath Absolute or relative path to the JSON file.
185
+ */
186
+ static load(filePath: string): EvalDataset;
187
+ /**
188
+ * Create an `EvalDataset` from a plain array (e.g. from a database query).
189
+ */
190
+ static from(cases: EvalCase[]): EvalDataset;
191
+ }
192
+
193
+ interface EvalRunnerOptions {
194
+ /**
195
+ * Identifier for this run — use something meaningful like a version or PR number.
196
+ * Defaults to a timestamp string.
197
+ */
198
+ runId?: string;
199
+ /**
200
+ * Concurrency limit — how many cases to run in parallel.
201
+ * Defaults to 1 (sequential) to avoid overwhelming the provider.
202
+ */
203
+ concurrency?: number;
204
+ }
205
+ /**
206
+ * Runs an agent against every case in an `EvalDataset` and collects the
207
+ * results into an `EvalRun`.
208
+ *
209
+ * @example
210
+ * ```ts
211
+ * const runner = new EvalRunner(agent);
212
+ * const run = await runner.run(dataset, { runId: 'v1.2' });
213
+ *
214
+ * console.log(`${run.results.length} cases run in ${run.totalDurationMs}ms`);
215
+ * ```
216
+ */
217
+ declare class EvalRunner {
218
+ private agent;
219
+ constructor(agent: BaseAgent);
220
+ /**
221
+ * Run all cases in the dataset and return an `EvalRun`.
222
+ */
223
+ run(dataset: EvalDataset, options?: EvalRunnerOptions): Promise<EvalRun>;
224
+ }
225
+
226
+ /**
227
+ * A scorer evaluates each `EvalCaseResult` in a run and produces a
228
+ * pass/fail verdict with an optional explanation.
229
+ *
230
+ * Implement this interface to create custom scoring logic.
231
+ */
232
+ interface EvalScorer {
233
+ score(run: EvalRun): Promise<EvalScoredRun>;
234
+ }
235
+ /**
236
+ * Passes a case when `actualOutput` exactly equals `expectedOutput`.
237
+ * Optionally case-insensitive and/or trimmed.
238
+ *
239
+ * @example
240
+ * ```ts
241
+ * const scorer = new ExactMatchScorer({ trim: true, caseInsensitive: true });
242
+ * const scored = await scorer.score(run);
243
+ * ```
244
+ */
245
+ declare class ExactMatchScorer implements EvalScorer {
246
+ private trim;
247
+ private caseInsensitive;
248
+ constructor(options?: {
249
+ trim?: boolean;
250
+ caseInsensitive?: boolean;
251
+ });
252
+ score(run: EvalRun): Promise<EvalScoredRun>;
253
+ }
254
+ /**
255
+ * Passes a case when `actualOutput` contains `expectedOutput` as a substring.
256
+ * Optionally case-insensitive.
257
+ *
258
+ * @example
259
+ * ```ts
260
+ * const scorer = new ContainsScorer({ caseInsensitive: true });
261
+ * const scored = await scorer.score(run);
262
+ * ```
263
+ */
264
+ declare class ContainsScorer implements EvalScorer {
265
+ private caseInsensitive;
266
+ constructor(options?: {
267
+ caseInsensitive?: boolean;
268
+ });
269
+ score(run: EvalRun): Promise<EvalScoredRun>;
270
+ }
271
+ interface LLMJudgeScorerOptions {
272
+ /**
273
+ * Custom judge prompt template.
274
+ * Use `{{question}}`, `{{expected}}`, and `{{actual}}` as placeholders.
275
+ * Must instruct the LLM to respond with only "pass" or "fail" on the first line,
276
+ * optionally followed by an explanation.
277
+ */
278
+ promptTemplate?: string;
279
+ }
280
+ /**
281
+ * Uses an LLM agent as a judge to score each case.
282
+ * The judge is prompted with the question, expected answer, and actual answer.
283
+ *
284
+ * @example
285
+ * ```ts
286
+ * const judgeAgent = new MyAgent({ toolpack });
287
+ * const scorer = new LLMJudgeScorer(judgeAgent);
288
+ * const scored = await scorer.score(run);
289
+ * ```
290
+ */
291
+ declare class LLMJudgeScorer implements EvalScorer {
292
+ private judgeAgent;
293
+ private promptTemplate;
294
+ constructor(judgeAgent: BaseAgent, options?: LLMJudgeScorerOptions);
295
+ score(run: EvalRun): Promise<EvalScoredRun>;
296
+ }
297
+ /**
298
+ * Wraps a user-supplied scoring function.
299
+ *
300
+ * @example
301
+ * ```ts
302
+ * const scorer = new CustomScorer(async (result) => {
303
+ * const pass = result.actualOutput.includes('Paris');
304
+ * return { verdict: pass ? 'pass' : 'fail' };
305
+ * });
306
+ * ```
307
+ */
308
+ declare class CustomScorer implements EvalScorer {
309
+ private fn;
310
+ constructor(fn: (result: EvalCaseResult) => Promise<{
311
+ verdict: EvalVerdict;
312
+ explanation?: string;
313
+ }>);
314
+ score(run: EvalRun): Promise<EvalScoredRun>;
315
+ }
316
+
317
+ /**
318
+ * Compares two scored runs and produces a regression/improvement report.
319
+ *
320
+ * @example
321
+ * ```ts
322
+ * const report = compareEvalRuns(baselineScoredRun, candidateScoredRun);
323
+ *
324
+ * if (report.regressions.length > 0) {
325
+ * console.error('Regressions detected:', report.regressions);
326
+ * process.exit(1);
327
+ * }
328
+ *
329
+ * console.log(`Pass rate: ${report.baselinePassRate} → ${report.candidatePassRate} (Δ${report.delta > 0 ? '+' : ''}${report.delta.toFixed(2)})`);
330
+ * ```
331
+ */
332
+ declare function compareEvalRuns(baseline: EvalScoredRun, candidate: EvalScoredRun): EvalReport;
333
+ /**
334
+ * Format an `EvalReport` as a human-readable summary string.
335
+ *
336
+ * @example
337
+ * ```ts
338
+ * console.log(formatEvalReport(report));
339
+ * ```
340
+ */
341
+ declare function formatEvalReport(report: EvalReport): string;
342
+
343
+ export { ContainsScorer as C, type EvalCase as E, LLMJudgeScorer as L, CustomScorer as a, type EvalCaseResult as b, EvalDataset as c, type EvalImprovement as d, type EvalRegression as e, type EvalReport as f, type EvalRun as g, EvalRunner as h, type EvalRunnerOptions as i, type EvalScoredResult as j, type EvalScoredRun as k, type EvalScorer as l, type EvalVerdict as m, ExactMatchScorer as n, type LLMJudgeScorerOptions as o, compareEvalRuns as p, formatEvalReport as q };