everclaw 0.3.15 → 0.3.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -312,7 +312,7 @@ ${t.stderr.trim()}`:"",typeof t.exitCode=="number"?`Exit code: ${t.exitCode}`:""
312
312
  `);return r?`${n} failed
313
313
  ${r}`:`${n} failed`}function gc(){let o=process.argv[1]??"";return Gt(o)}function jr(o){let e=Math.max(1,Number(o??0))*1e3;return Math.max(dc,e)}var jo=class{constructor(e,t,n,r=new ve,s){this.target=e;this.timeoutMs=t;this.buildCommand=n;this.runner=r;this.defaultEnv=s}async run(e){if(St(this.target).mode!=="local-process")throw new Error(`${pi(this.target)} requires local-process isolation`);let n=await this.runner.run({command:this.buildCommand(e.payload),cwd:e.cwd,env:e.env??this.defaultEnv,timeoutMs:this.timeoutMs,signal:e.signal,maxCaptureBytes:e.maxCaptureBytes??cc});if(n.status==="aborted")throw cn();if(n.status==="ok")return n.stdout;throw new Error(uc(this.target,this.timeoutMs,n))}};function Cn(o){let e=gc();return new jo(o.target,o.timeoutMs,t=>({kind:"argv",file:e.file,args:[...e.args,"agent","--message",t.message,"--session",t.session,"--channel",t.channel,"--chat-id",t.chatId,"--raw-output"]}),o.runner,e.env)}var Vt=class o{constructor(e,t,n,r){this.config=e;this.bus=t;this.debounce=n;this.logger=r}running=!1;isAllowed(e){let t=this.config?.allowFrom??[];return!t.length||t.includes(String(e))?!0:String(e).includes("|")?String(e).split("|").some(n=>t.includes(n)):!1}static channelColors={telegram:"\x1B[36m",discord:"\x1B[35m"};async handleMessage(e){let t=typeof e.content=="string"?e.content:JSON.stringify(e.content),n=o.channelColors[this.name]??"\x1B[0m",r=t.length>80?t.slice(0,80)+"\u2026":t,s=e.metadata?._skip_allow_from===!0,i=typeof e.metadata?.chat_type=="string"?e.metadata.chat_type:void 0,a=e.metadata?{...e.metadata}:void 0;if(a&&"_skip_allow_from"in a&&delete a._skip_allow_from,!s&&!this.isAllowed(e.senderId)){this.logger?.info("message.inbound","blocked",{channel:this.name,chatId:e.chatId,senderId:e.senderId,reason:"allow_from",chatType:i,groupTitle:typeof a?.chat_title=="string"?a.chat_title:void 0,content:r}),console.log(`[${n}${this.name}\x1B[0m] \x1B[31mBLOCKED\x1B[0m from=${e.senderId}`);return}console.log(`[${n}${this.name}\x1B[0m] from=${e.senderId} chat=${e.chatId} "${r}"`),this.logger?.info("message.inbound","received",{channel:this.name,chatId:e.chatId,senderId:e.senderId,chatType:i,groupTitle:typeof a?.chat_title=="string"?a.chat_title:void 0,threadId:a?.thread_id,botMentioned:a?.bot_mentioned===!0,content:r});let l={channel:this.name,senderId:String(e.senderId),chatId:String(e.chatId),content:e.content};e.media&&e.media.length>0&&(l.media=e.media),a&&Object.keys(a).length>0&&(l.metadata=a),e.sessionKey&&(l.sessionKeyOverride=e.sessionKey),await this.bus.publishInbound(l)}get isRunning(){return this.running}};var Yt=class{buffers=new Map;config;onEmit;constructor(e,t){this.config=e,this.onEmit=t}submit(e){if(!this.config.enabled){this.onEmit(e);return}let t=`${e.channel}:${e.chatId}`,n=this.buffers.get(t);if(n){clearTimeout(n.quietTimer),n.messages.push(e),n.quietTimer=setTimeout(()=>this.emit(t),this.config.quietMs);return}let r=setTimeout(()=>this.emit(t),this.config.quietMs),s=setTimeout(()=>this.emit(t),this.config.maxWaitMs);this.buffers.set(t,{messages:[e],quietTimer:r,maxTimer:s})}emit(e){let t=this.buffers.get(e);if(!t)return;clearTimeout(t.quietTimer),clearTimeout(t.maxTimer),this.buffers.delete(e);let n=t.messages[0];if(!n)return;let r=t.messages.map(a=>typeof a.content=="string"?a.content:a.content.find(c=>c.type==="text")?.text??JSON.stringify(a.content)).join(`
314
314
  `),s=t.messages.flatMap(a=>a.media??[]),i=t.messages.reduce((a,l)=>({...a,...l.metadata??{}}),{});this.onEmit({...n,content:r,media:s.length>0?s:void 0,metadata:Object.keys(i).length>0?i:void 0})}flush(){for(let e of this.buffers.keys())this.emit(e)}get size(){return this.buffers.size}};function fi(o){return o.message?{kind:"message",msg:o.message}:o.edited_message?{kind:"edited_message",msg:o.edited_message}:o.channel_post?{kind:"channel_post",msg:o.channel_post}:o.edited_channel_post?{kind:"edited_channel_post",msg:o.edited_channel_post}:{kind:"unknown",msg:null}}function mc(o,e=4e3){if(o.length<=e)return[o];let t=[],n=o;for(;n.length>e;){let r=n.slice(0,e),s=Math.max(r.lastIndexOf(`
315
- `),r.lastIndexOf(" "));s<=0&&(s=e),t.push(n.slice(0,s)),n=n.slice(s).trimStart()}return n&&t.push(n),t}function pc(o){if(!o)return"[Unsupported message]";if(o.text)return o.text;let e=o.caption?` ${o.caption}`:"";return o.photo?`[Photo]${e}`:o.video?`[Video]${e}`:o.voice?`[Voice message]${e}`:o.audio?`[Audio]${e}`:o.document?`[Document: ${o.document.file_name??"file"}]${e}`:o.sticker?`[Sticker ${o.sticker.emoji??""}]${e}`:o.location?`[Location]${e}`:o.contact?`[Contact]${e}`:`[Unsupported message]${e}`}function fc(o){return o?o.text??o.caption??null:null}function hc(o){return o?o.entities??o.caption_entities??[]:[]}function yc(o){return o.toLowerCase().replace(/[^a-z0-9_]/g,"_").slice(0,32)}var bc=new Set(["chatid","ping"]);function wc(o){let e=o.match(/^\/([a-zA-Z0-9_]+)(?:@([a-zA-Z0-9_]+))?(?:\s|$)/);return e?{name:e[1].toLowerCase(),target:e[2]?.toLowerCase()??null}:null}var vc=3e3,Fr=class extends Vt{name="telegram";offset=0;inbox;botUsername=null;botId=null;startTime=Date.now();lastTypingAt=new Map;constructor(e,t,n){super(e,t,e.debounce,n),this.inbox=new Yt(e.debounce,r=>this.handleMessage(r))}api(e){return`https://api.telegram.org/bot${this.config.token}/${e}`}async start(){if(!this.config.token){console.error("Telegram token not configured"),this.logger?.error("channel","telegram.start_failed",{reason:"missing_token"});return}if(this.logger?.info("channel","telegram.start",{mode:"polling"}),!this.botUsername)try{let e=await fetch(this.api("getMe"));if(e.ok){let t=await e.json();t.ok&&t.result&&(t.result.id&&(this.botId=String(t.result.id)),t.result.username&&(this.botUsername=t.result.username.toLowerCase(),this.logger?.info("channel","telegram.identity",{botId:this.botId,botUsername:this.botUsername}),this.config.commands?.native!==!1&&await this.registerCommands()))}else this.logger?.warn("channel","telegram.get_me_failed",{status:e.status})}catch(e){this.logger?.warn("channel","telegram.get_me_failed",{error:e instanceof Error?e.message:String(e)})}for(this.running=!0;this.running;)try{let e=new URL(this.api("getUpdates"));e.searchParams.set("timeout","25"),e.searchParams.set("offset",String(this.offset)),e.searchParams.set("allowed_updates",JSON.stringify(["message","edited_message","channel_post","edited_channel_post"]));let t=await fetch(e);if(!t.ok){this.logger?.warn("channel","telegram.get_updates_failed",{status:t.status}),await new Promise(r=>setTimeout(r,1500));continue}let n=await t.json();if(!n.ok){this.logger?.warn("channel","telegram.get_updates_rejected",{description:n.description??"unknown"});continue}n.result&&n.result.length>0&&this.logger?.info("channel","telegram.updates_received",{count:n.result.length,updateKinds:n.result.map(r=>fi(r).kind).join(",")});for(let r of n.result??[]){this.offset=r.update_id+1;let{kind:s,msg:i}=fi(r);if(!i){this.logger?.info("channel","telegram.update_skipped",{updateId:r.update_id,reason:"unsupported_update_type",updateKind:s});continue}if(i.from?.is_bot){this.logger?.debug("channel","telegram.update_skipped",{updateId:r.update_id,chatId:String(i.chat.id),reason:"sender_is_bot",updateKind:s});continue}let a=i.from?.id!=null?String(i.from.id):i.sender_chat?.id!=null?`chat:${i.sender_chat.id}`:"unknown",l=i.from?.username?`${a}|@${i.from.username}`:i.sender_chat?.username?`${a}|@${i.sender_chat.username}`:i.sender_chat?.title?`${a}|${i.sender_chat.title}`:a,c=pc(i),d=fc(i),p=hc(i),g=d?wc(d):null,x=g?.target!=null&&this.botUsername!=null&&g.target!==this.botUsername;if(g&&!x&&bc.has(g.name)){await this.handleBotCommand(g.name,i),this.logger?.info("channel","telegram.command_handled",{updateId:r.update_id,updateKind:s,chatId:String(i.chat.id),command:g.name,chatType:i.chat.type??"unknown"});continue}let y={message_id:i.message_id};i.chat.type&&(y.chat_type=i.chat.type),i.chat.title&&(y.chat_title=i.chat.title),i.sender_chat?.id!=null&&(y.sender_chat_id=String(i.sender_chat.id)),i.sender_chat?.title&&(y.sender_chat_title=i.sender_chat.title),!i.from&&i.sender_chat&&(y.sender_is_chat=!0);let u=!1;if(this.botUsername&&d&&p.length>0&&(u=p.some(T=>T.type==="mention"?d.substring(T.offset,T.offset+T.length).toLowerCase().trim()===`@${this.botUsername}`:T.type==="text_mention"?!0:T.type==="bot_command"?d.substring(T.offset,T.offset+T.length).toLowerCase().includes(`@${this.botUsername}`):!1)),i.chat.type==="group"||i.chat.type==="supergroup"){let T=this.config.groups?.allowedGroupIds,v=T==null?null:Array.isArray(T)?T:[String(T)];if(v!==null&&!v.includes(String(i.chat.id))){this.logger?.info("channel","telegram.inbound_skipped",{updateId:r.update_id,updateKind:s,chatId:String(i.chat.id),chatType:i.chat.type??"unknown",groupTitle:i.chat.title??null,senderId:l,reason:"group_not_allowed"});continue}y._skip_allow_from=!0;let O=this.config.groups?.requireMention!==!1;if(O){let G=!!g&&!x,I=!!(i.reply_to_message?.from?.is_bot&&(!this.botId||String(i.reply_to_message.from.id)===this.botId));if(!u&&!G&&!I){this.logger?.info("channel","telegram.inbound_skipped",{updateId:r.update_id,updateKind:s,chatId:String(i.chat.id),chatType:i.chat.type??"unknown",groupTitle:i.chat.title??null,senderId:l,reason:"group_activation",requireMention:O});continue}}}u&&this.botUsername&&(y.bot_mentioned=!0,d&&(c=c.replace(new RegExp(`@${this.botUsername}`,"gi"),"").trim())),i.message_thread_id&&(y.thread_id=i.message_thread_id),this.inbox.submit({channel:this.name,senderId:l,chatId:String(i.chat.id),content:c,metadata:y,...i.message_thread_id&&{sessionKey:`telegram:${i.chat.id}:${i.message_thread_id}`}})}}catch(e){this.logger?.warn("channel","telegram.poll_loop_error",{error:e instanceof Error?e.message:String(e)}),await new Promise(t=>setTimeout(t,1500))}}async registerCommands(){let e=[{command:"chatid",description:"Show this chat's ID for configuration"},{command:"ping",description:"Check if the bot is online"},{command:"new",description:"Start a new conversation session"},{command:"reset",description:"Clear the current session context"},{command:"stop",description:"Stop the current task"},{command:"help",description:"Show available commands"}],t=(this.config.commands?.custom??[]).map(r=>({command:yc(r.command),description:r.description})),n=[...e,...t].slice(0,100);try{let r=await fetch(this.api("setMyCommands"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({commands:n})});if(!r.ok){let s=await r.json().catch(()=>({}));console.warn(`[telegram] setMyCommands failed: ${s?.description??r.status}`)}}catch{console.warn("[telegram] setMyCommands request failed")}}async handleBotCommand(e,t){let n=String(t.chat.id);if(e==="chatid"){let r=t.chat.type??"private",s=r==="private"?t.from?.first_name??t.from?.username??"Private":t.chat.title??"Unknown";await this.sendChunk(n,`Chat ID: \`telegram:${n}\`
315
+ `),r.lastIndexOf(" "));s<=0&&(s=e),t.push(n.slice(0,s)),n=n.slice(s).trimStart()}return n&&t.push(n),t}function pc(o){if(!o)return"[Unsupported message]";if(o.text)return o.text;let e=o.caption?` ${o.caption}`:"";return o.photo?`[Photo]${e}`:o.video?`[Video]${e}`:o.voice?`[Voice message]${e}`:o.audio?`[Audio]${e}`:o.document?`[Document: ${o.document.file_name??"file"}]${e}`:o.sticker?`[Sticker ${o.sticker.emoji??""}]${e}`:o.location?`[Location]${e}`:o.contact?`[Contact]${e}`:`[Unsupported message]${e}`}function fc(o){return o?o.text??o.caption??null:null}function hc(o){return o?o.entities??o.caption_entities??[]:[]}function yc(o){return o.toLowerCase().replace(/[^a-z0-9_]/g,"_").slice(0,32)}var bc=new Set(["chatid","ping"]);function wc(o){let e=o.match(/^\/([a-zA-Z0-9_]+)(?:@([a-zA-Z0-9_]+))?(?:\s|$)/);return e?{name:e[1].toLowerCase(),target:e[2]?.toLowerCase()??null}:null}var vc=3e3,Fr=class extends Vt{name="telegram";offset=0;inbox;botUsername=null;botId=null;startTime=Date.now();lastTypingAt=new Map;constructor(e,t,n){super(e,t,e.debounce,n),this.inbox=new Yt(e.debounce,r=>this.handleMessage(r))}api(e){return`https://api.telegram.org/bot${this.config.token}/${e}`}async start(){if(!this.config.token){console.error("Telegram token not configured"),this.logger?.error("channel","telegram.start_failed",{reason:"missing_token"});return}if(this.logger?.info("channel","telegram.start",{mode:"polling"}),!this.botUsername)try{let e=await fetch(this.api("getMe"));if(e.ok){let t=await e.json();t.ok&&t.result&&(t.result.id&&(this.botId=String(t.result.id)),t.result.username&&(this.botUsername=t.result.username.toLowerCase(),this.logger?.info("channel","telegram.identity",{botId:this.botId,botUsername:this.botUsername}),this.config.commands?.native!==!1&&await this.registerCommands()))}else this.logger?.warn("channel","telegram.get_me_failed",{status:e.status})}catch(e){this.logger?.warn("channel","telegram.get_me_failed",{error:e instanceof Error?e.message:String(e)})}for(this.running=!0;this.running;)try{let e=new URL(this.api("getUpdates"));e.searchParams.set("timeout","25"),e.searchParams.set("offset",String(this.offset)),e.searchParams.set("allowed_updates",JSON.stringify(["message","edited_message","channel_post","edited_channel_post"]));let t=await fetch(e);if(!t.ok){this.logger?.warn("channel","telegram.get_updates_failed",{status:t.status}),await new Promise(r=>setTimeout(r,1500));continue}let n=await t.json();if(!n.ok){this.logger?.warn("channel","telegram.get_updates_rejected",{description:n.description??"unknown"});continue}n.result&&n.result.length>0&&this.logger?.info("channel","telegram.updates_received",{count:n.result.length,updateKinds:n.result.map(r=>fi(r).kind).join(",")});for(let r of n.result??[]){this.offset=r.update_id+1;let{kind:s,msg:i}=fi(r);if(!i){this.logger?.info("channel","telegram.update_skipped",{updateId:r.update_id,reason:"unsupported_update_type",updateKind:s});continue}if(i.from?.is_bot&&!i.sender_chat){this.logger?.info("channel","telegram.update_skipped",{updateId:r.update_id,chatId:String(i.chat.id),reason:"sender_is_bot",updateKind:s,fromId:i.from?.id!=null?String(i.from.id):void 0});continue}let a=i.sender_chat?.id!=null?`chat:${i.sender_chat.id}`:i.from?.id!=null?String(i.from.id):"unknown",l=i.sender_chat?.username?`${a}|@${i.sender_chat.username}`:i.sender_chat?.title?`${a}|${i.sender_chat.title}`:i.from?.username?`${a}|@${i.from.username}`:a,c=pc(i),d=fc(i),p=hc(i),g=d?wc(d):null,x=g?.target!=null&&this.botUsername!=null&&g.target!==this.botUsername;if(g&&!x&&bc.has(g.name)){await this.handleBotCommand(g.name,i),this.logger?.info("channel","telegram.command_handled",{updateId:r.update_id,updateKind:s,chatId:String(i.chat.id),command:g.name,chatType:i.chat.type??"unknown"});continue}let y={message_id:i.message_id};i.chat.type&&(y.chat_type=i.chat.type),i.chat.title&&(y.chat_title=i.chat.title),i.sender_chat?.id!=null&&(y.sender_chat_id=String(i.sender_chat.id)),i.sender_chat?.title&&(y.sender_chat_title=i.sender_chat.title),i.sender_chat&&(y.sender_is_chat=!0);let u=!1;if(this.botUsername&&d&&p.length>0&&(u=p.some(T=>T.type==="mention"?d.substring(T.offset,T.offset+T.length).toLowerCase().trim()===`@${this.botUsername}`:T.type==="text_mention"?!0:T.type==="bot_command"?d.substring(T.offset,T.offset+T.length).toLowerCase().includes(`@${this.botUsername}`):!1)),i.chat.type==="group"||i.chat.type==="supergroup"){let T=this.config.groups?.allowedGroupIds,v=T==null?null:Array.isArray(T)?T:[String(T)];if(v!==null&&!v.includes(String(i.chat.id))){this.logger?.info("channel","telegram.inbound_skipped",{updateId:r.update_id,updateKind:s,chatId:String(i.chat.id),chatType:i.chat.type??"unknown",groupTitle:i.chat.title??null,senderId:l,reason:"group_not_allowed"});continue}y._skip_allow_from=!0;let O=this.config.groups?.requireMention!==!1;if(O){let G=!!g&&!x,I=!!(i.reply_to_message?.from?.is_bot&&(!this.botId||String(i.reply_to_message.from.id)===this.botId));if(!u&&!G&&!I){this.logger?.info("channel","telegram.inbound_skipped",{updateId:r.update_id,updateKind:s,chatId:String(i.chat.id),chatType:i.chat.type??"unknown",groupTitle:i.chat.title??null,senderId:l,reason:"group_activation",requireMention:O});continue}}}u&&this.botUsername&&(y.bot_mentioned=!0,d&&(c=c.replace(new RegExp(`@${this.botUsername}`,"gi"),"").trim())),i.message_thread_id&&(y.thread_id=i.message_thread_id),this.inbox.submit({channel:this.name,senderId:l,chatId:String(i.chat.id),content:c,metadata:y,...i.message_thread_id&&{sessionKey:`telegram:${i.chat.id}:${i.message_thread_id}`}})}}catch(e){this.logger?.warn("channel","telegram.poll_loop_error",{error:e instanceof Error?e.message:String(e)}),await new Promise(t=>setTimeout(t,1500))}}async registerCommands(){let e=[{command:"chatid",description:"Show this chat's ID for configuration"},{command:"ping",description:"Check if the bot is online"},{command:"new",description:"Start a new conversation session"},{command:"reset",description:"Clear the current session context"},{command:"stop",description:"Stop the current task"},{command:"help",description:"Show available commands"}],t=(this.config.commands?.custom??[]).map(r=>({command:yc(r.command),description:r.description})),n=[...e,...t].slice(0,100);try{let r=await fetch(this.api("setMyCommands"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({commands:n})});if(!r.ok){let s=await r.json().catch(()=>({}));console.warn(`[telegram] setMyCommands failed: ${s?.description??r.status}`)}}catch{console.warn("[telegram] setMyCommands request failed")}}async handleBotCommand(e,t){let n=String(t.chat.id);if(e==="chatid"){let r=t.chat.type??"private",s=r==="private"?t.from?.first_name??t.from?.username??"Private":t.chat.title??"Unknown";await this.sendChunk(n,`Chat ID: \`telegram:${n}\`
316
316
  Name: ${s}
317
317
  Type: ${r}`,{message_id:t.message_id,thread_id:t.message_thread_id})}else if(e==="ping"){let r=Math.floor((Date.now()-this.startTime)/1e3);await this.sendChunk(n,`Pong! Uptime: ${r}s`,{message_id:t.message_id,thread_id:t.message_thread_id})}}async stop(){this.running=!1}async send(e){if(!this.config.token)return;if(e.metadata?._progress){await this.sendTyping(e.chatId);return}let t=mc(e.content||"");for(let n of t)await this.sendChunk(e.chatId,n,e.metadata)}async sendTyping(e){let t=Date.now(),n=this.lastTypingAt.get(e)??0;t-n<vc||(this.lastTypingAt.set(e,t),await fetch(this.api("sendChatAction"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({chat_id:e,action:"typing"})}).catch(()=>{}))}async sendChunk(e,t,n){let r={chat_id:e,text:t,parse_mode:"Markdown"},s=n?.message_id;this.config.replyToMessage&&s&&(r.reply_parameters={message_id:s});let i=n?.thread_id;i&&(r.message_thread_id=i);let a=await this.fetchWithRetry(this.api("sendMessage"),r);if(a&&!a.ok&&a.status===400)try{if((await a.json()).description?.toLowerCase().includes("parse")){let c={...r};delete c.parse_mode,await this.fetchWithRetry(this.api("sendMessage"),c)}}catch{}}async fetchWithRetry(e,t,n=1){for(let r=0;r<=n;r++)try{return await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})}catch(s){r<n?await new Promise(i=>setTimeout(i,1e3)):console.error(`[telegram] send failed after ${n+1} attempts:`,s instanceof Error?s.message:String(s))}}};import hi from"ws";var Br=class extends Vt{name="discord";ws=null;hb=null;seq=null;inbox;constructor(e,t,n){super(e,t,e.debounce,n),this.inbox=new Yt(e.debounce,r=>this.handleMessage(r))}async start(){if(!this.config.token){console.error("Discord token not configured");return}for(this.running=!0;this.running;){try{await this.connectOnce()}catch{}this.running&&await new Promise(e=>setTimeout(e,2e3))}}async stop(){if(this.running=!1,this.hb&&(clearInterval(this.hb),this.hb=null),this.ws){try{this.ws.close()}catch{}this.ws=null}}async send(e){this.config.token&&await fetch(`https://discord.com/api/v10/channels/${e.chatId}/messages`,{method:"POST",headers:{Authorization:`Bot ${this.config.token}`,"Content-Type":"application/json"},body:JSON.stringify({content:e.content||""})}).catch(()=>{})}async connectOnce(){await new Promise(e=>{let t=new hi(this.config.gatewayUrl);this.ws=t,t.on("message",async n=>{let r;try{r=JSON.parse(n.toString())}catch{return}if(r.s!=null&&(this.seq=r.s),r.op===10){let s=Number(r.d?.heartbeat_interval??3e4);this.startHeartbeat(s),this.sendPayload({op:2,d:{token:this.config.token,intents:this.config.intents,properties:{os:process.platform,browser:"everclaw",device:"everclaw"}}});return}if(r.op===7){t.close();return}if(r.op!==11&&r.t==="MESSAGE_CREATE"){let s=r.d;if(!s||s.author?.bot)return;let i=String(s.content??"").trim();if(!i)return;let a=`${s.author?.id}${s.author?.username?`|${s.author.username}`:""}`;this.inbox.submit({channel:this.name,senderId:a,chatId:String(s.channel_id),content:i,...s.attachments?.length?{media:s.attachments.map(l=>l.url)}:{},metadata:{message_id:s.id,guild_id:s.guild_id??null}})}}),t.on("close",()=>{this.hb&&(clearInterval(this.hb),this.hb=null),e()}),t.on("error",()=>{this.hb&&(clearInterval(this.hb),this.hb=null),e()})})}startHeartbeat(e){this.hb&&clearInterval(this.hb),this.hb=setInterval(()=>{this.sendPayload({op:1,d:this.seq})},e)}sendPayload(e){!this.ws||this.ws.readyState!==hi.OPEN||this.ws.send(JSON.stringify(e))}};var yi="_shutdown",Ur=class{constructor(e,t,n,r){this.config=e;this.bus=t;this.lifecycle=n??rt(),this.logger=r,this.initChannels()}channels=new Map;dispatchLoop=null;stopping=!1;lifecycle;logger;initChannels(){this.config.channels.telegram.enabled&&this.channels.set("telegram",new Fr(this.config.channels.telegram,this.bus,this.logger)),this.config.channels.discord.enabled&&this.channels.set("discord",new Br(this.config.channels.discord,this.bus,this.logger))}async startAll(){this.channels.size&&(this.stopping=!1,this.dispatchLoop=this.dispatchOutbound(),await Promise.all([...this.channels.values()].map(e=>e.start().catch(()=>{}))))}async stopAll(){this.stopping=!0,await Promise.all([...this.channels.values()].map(e=>e.stop().catch(()=>{}))),this.dispatchLoop&&(await this.bus.publishOutbound({channel:"system",chatId:"gateway:shutdown",content:"",metadata:{[yi]:!0}}),await this.dispatchLoop.catch(()=>{}),this.dispatchLoop=null)}getSkipReason(e){if(this.stopping)return"stopping";if(e.metadata?._progress){let t=!!e.metadata._tool_hint;if(t&&!this.config.channels.sendToolHints||!t&&!this.config.channels.sendProgress)return"progress_disabled"}return this.channels.has(e.channel)?null:"channel_unavailable"}async dispatchOutbound(){for(;;){let e=await this.bus.consumeOutbound();if(e.metadata?.[yi])break;let t=this.channels.has(e.channel),n=this.getSkipReason(e),r=!1,s=null;try{await qe(this.lifecycle.beforeOutbound,"beforeOutbound",{message:e,hasChannel:t,skippedReason:n})}catch(i){s=i,console.warn(String(i))}if(!s&&!n){let i=this.channels.get(e.channel);if(i)try{await i.send(e),r=!0,this.logger?.info("message.outbound","sent",{channel:e.channel,chatId:e.chatId,replyToMessageId:e.metadata?.message_id,threadId:e.metadata?.thread_id,progress:e.metadata?._progress===!0,content:e.content.slice(0,80)})}catch(a){s=a,this.logger?.error("message.outbound","failed",{channel:e.channel,chatId:e.chatId,error:a instanceof Error?a.message:String(a),content:e.content.slice(0,80)})}}else n&&this.logger?.info("message.outbound","skipped",{channel:e.channel,chatId:e.chatId,reason:n,progress:e.metadata?._progress===!0,content:e.content.slice(0,80)});try{await qe(this.lifecycle.afterOutbound,"afterOutbound",{message:e,hasChannel:t,skippedReason:n,dispatched:r,error:s})}catch(i){console.warn(String(i))}}}get enabledChannels(){return[...this.channels.keys()]}getStatus(){return Object.fromEntries([...this.channels].map(([e,t])=>[e,{enabled:!0,running:t.isRunning}]))}};Ue();import{randomUUID as Sc}from"node:crypto";import kc from"node:path";function Cc(o,e=160){let t=typeof o=="string"?o.trim().replace(/\s+/g," "):"";return t?t.length>e?`${t.slice(0,Math.max(0,e-1)).trimEnd()}\u2026`:t:"No delivery content available."}function xc(o){return kc.resolve(o)}function bi(o){if(!o)return null;let e=String(o.content??"");return{id:String(o.delivery_id??""),jobId:String(o.job_id??""),jobName:String(o.job_name??"Delivery"),sessionKey:String(o.session_key??""),channel:String(o.channel??"cli"),recipient:String(o.recipient??"direct"),content:e,contentPreview:Cc(e),deliveredAt:String(o.delivered_at??""),clearedAt:o.cleared_at?String(o.cleared_at):null}}var Xt=class{storage;workspace;constructor(e,t,n){this.workspace=xc(e),this.storage=n??Fe({dataDir:t})}list(e={}){return this.storage.prepare(`SELECT delivery_id, job_id, job_name, session_key, channel, recipient, content, delivered_at, cleared_at
318
318
  FROM inbox_deliveries
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "everclaw",
3
- "version": "0.3.15",
3
+ "version": "0.3.16",
4
4
  "description": "Enterprise-level Agent Cli for digital productivity and privacy security.",
5
5
  "keywords": [
6
6
  "coding-agent",
@@ -9,7 +9,7 @@ always: true
9
9
  ## Structure
10
10
 
11
11
  - `memory/MEMORY.md` — Long-term facts (preferences, project context, relationships). Always loaded into your context.
12
- - `memory/HISTORY.md` — Append-only event log. NOT loaded into context. Search it with grep. Each entry starts with [YYYY-MM-DD HH:MM].
12
+ - `memory/HISTORY.md` — Append-only event log. NOT loaded into context. Search it with grep. Entries use ISO-prefix timestamps like `[2024-01-15T10:30]`.
13
13
 
14
14
  ## Search Past Events
15
15
 
@@ -19,13 +19,19 @@ grep -i "keyword" memory/HISTORY.md
19
19
 
20
20
  Use the `exec` tool to run grep. Combine patterns: `grep -iE "meeting|deadline" memory/HISTORY.md`
21
21
 
22
+ ## Auto-consolidation
23
+
24
+ Old conversations are automatically summarized when the session grows beyond the `memoryWindow` threshold. The consolidation LLM:
25
+ 1. Appends a summary to `HISTORY.md`
26
+ 2. **Overwrites** `MEMORY.md` with an updated long-term memory snapshot
27
+
28
+ Because consolidation overwrites MEMORY.md entirely, do **not** manually edit it for transient notes. The consolidation prompt includes the current MEMORY.md contents, so existing facts are generally preserved and refined automatically.
29
+
22
30
  ## When to Update MEMORY.md
23
31
 
24
- Write important facts immediately using `edit_file` or `write_file`:
32
+ Only write to MEMORY.md for **critical persistent facts** that must survive before the next consolidation:
25
33
  - User preferences ("I prefer dark mode")
26
34
  - Project context ("The API uses OAuth2")
27
35
  - Relationships ("Alice is the project lead")
28
36
 
29
- ## Auto-consolidation
30
-
31
- Old conversations are automatically summarized and appended to HISTORY.md when the session grows large. Long-term facts are extracted to MEMORY.md. You don't need to manage this.
37
+ Use `edit_file` to update specific sections rather than `write_file` to avoid clobbering existing content.