@juspay/neurolink 9.48.3 → 9.50.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## [9.50.0](https://github.com/juspay/neurolink/compare/v9.49.0...v9.50.0) (2026-04-08)
2
+
3
+ ### Features
4
+
5
+ - **(image-compression):** add sharp-based compression for AI providers ([75645bf](https://github.com/juspay/neurolink/commit/75645bf69d9eb21bf46763c5b058cb92146c71f2)), closes [#553](https://github.com/juspay/neurolink/issues/553)
6
+
7
+ ## [9.49.0](https://github.com/juspay/neurolink/compare/v9.48.3...v9.49.0) (2026-04-08)
8
+
9
+ ### Features
10
+
11
+ - **(redis):** add URL-based connection support with TLS ([0b22f46](https://github.com/juspay/neurolink/commit/0b22f46d2c1783fac2bbd5eba62c7f34967fb4ff))
12
+
1
13
  ## [9.48.3](https://github.com/juspay/neurolink/compare/v9.48.2...v9.48.3) (2026-04-08)
2
14
 
3
15
  ### Bug Fixes
@@ -2017,14 +2017,14 @@ For "closing" slides (shows checkmarks \u2713):
2017
2017
 
2018
2018
  [... ${Math.max(0,l-Buffer.byteLength(h,"utf-8")-Buffer.byteLength(y,"utf-8"))} bytes omitted. Use ${a4t} tool to access full output ...]
2019
2019
 
2020
- `;return{preview:h+_+y,truncated:!0,originalSize:l}}var a4t,QIe=I(()=>{"use strict";a4t="retrieve_context"});async function eAe(r){let e=`${r.host}:${r.port}:${r.db}:${r.password?"auth":"noauth"}`,t=Zl.get(e);if(t&&t.client.isOpen)return t.refCount++,g.debug("[Redis] Reusing pooled connection",{key:e,refCount:t.refCount}),t.client;let n=dX.get(e);if(n){let s=await n,i=Zl.get(e);if(i){if(!i.client.isOpen){Zl.delete(e);let a=await pX(r);return Zl.set(e,{client:a,refCount:1}),a}i.refCount++}else if(s.isOpen)Zl.set(e,{client:s,refCount:1});else{let a=await pX(r);return Zl.set(e,{client:a,refCount:1}),a}return s}t&&Zl.delete(e);let o=pX(r);dX.set(e,o);try{let s=await o;return Zl.set(e,{client:s,refCount:1}),g.info("[Redis] Created pooled connection",{key:e,refCount:1}),s}finally{dX.delete(e)}}async function tAe(r){let e=`${r.host}:${r.port}:${r.db}:${r.password?"auth":"noauth"}`,t=Zl.get(e);if(t&&(t.refCount--,g.debug("[Redis] Released pooled connection",{key:e,refCount:t.refCount}),t.refCount<=0)){try{t.client.isOpen&&await t.client.quit()}catch(n){g.warn("[Redis] Error closing pooled connection",{key:e,error:String(n)})}Zl.delete(e),g.info("[Redis] Closed pooled connection",{key:e})}}function l4t(){return Array.from(Zl.entries()).map(([r,e])=>({key:r,refCount:e.refCount,isOpen:e.client.isOpen}))}async function pX(r){let t={url:`redis://${r.host}:${r.port}/${r.db}`,socket:{connectTimeout:r.connectionOptions?.connectTimeout,reconnectStrategy:o=>o>(r.connectionOptions?.maxRetriesPerRequest||3)?(g.error("Redis connection retries exhausted"),new Error("Redis connection retries exhausted")):Math.min((r.connectionOptions?.retryDelayOnFailover||100)*Math.pow(2,o),1e4)}};r.password&&(t.password=r.password);let n=I0(t);return n.on("error",o=>{let s=o.message.replace(/redis:\/\/.*?@/g,"redis://[redacted]@");g.error("Redis client error",{error:s})}),n.on("connect",()=>{g.debug("Redis client connected",{host:r.host,port:r.port,db:r.db})}),n.on("reconnecting",()=>{g.debug("Redis client reconnecting")}),n.isOpen||await n.connect(),n}function xc(r,e,t){if(!t)return g.warn("[REDIS] getSessionKey called without userId",{sessionId:e}),`${r.keyPrefix}${c4t}${e}`;let n=`${r.keyPrefix}${t}:${e}`;return g.debug("[redisUtils] Generated session key",{sessionId:e,userId:t,keyPrefix:r.keyPrefix,fullKey:n}),n}function DF(r,e){return`${r.userSessionsKeyPrefix}${e}`}function fT(r){try{return JSON.stringify(r)}catch(e){throw g.error("[redisUtils] Failed to serialize conversation",{error:e instanceof Error?e.message:String(e),stack:e instanceof Error?e.stack:void 0,sessionId:r?.sessionId,userId:r?.userId}),e}}function bc(r){if(!r)return null;try{let e=JSON.parse(String(r));if(typeof e!="object"||e===null||!("title"in e)||!("sessionId"in e)||!("userId"in e)||!("createdAt"in e)||!("updatedAt"in e)||!("messages"in e))return g.warn("[redisUtils] Deserialized data is not a valid conversation object",{type:typeof e,hasRequiredFields:e&&typeof e=="object"?Object.keys(e).join(", "):"none",preview:JSON.stringify(e).substring(0,100)}),null;let t=e;return Array.isArray(t.messages)?t.messages.every(o=>typeof o=="object"&&o!==null&&"role"in o&&"content"in o&&typeof o.role=="string"&&typeof o.content=="string"&&(o.role==="user"||o.role==="assistant"||o.role==="system"||o.role==="tool_call"||o.role==="tool_result"))?(g.debug("[redisUtils] Conversation deserialized successfully",{sessionId:t.sessionId,userId:t.userId,title:t.title,messageCount:t.messages.length,createdAt:t.createdAt,updatedAt:t.updatedAt}),t):(g.warn("[redisUtils] Invalid messages structure",{messageCount:t.messages.length,firstMessage:t.messages.length>0?JSON.stringify(t.messages[0]):null}),null):(g.warn("[redisUtils] messages is not an array",{type:typeof t.messages}),null)}catch(e){return g.error("[redisUtils] Failed to deserialize conversation",{error:e instanceof Error?e.message:String(e),stack:e instanceof Error?e.stack:void 0,dataLength:r.length,dataPreview:"[REDACTED]"}),null}}async function LF(r,e,t=100){g.debug("[redisUtils] Starting SCAN operation",{pattern:e,batchSize:t});let n=[],o="0",s=0,i=0;try{do{s++;let a=await r.scan(o,{MATCH:e,COUNT:t});o=String(a.cursor);let l=(a.keys||[]).map(String);n.push(...l),i+=l.length,g.debug("[redisUtils] SCAN iteration completed",{iteration:s,currentCursor:o,keysInBatch:l.length,totalKeysFound:n.length})}while(o!=="0");return g.info("[redisUtils] SCAN operation completed",{pattern:e,totalIterations:s,totalKeysFound:n.length,totalScanned:i}),n}catch(a){throw g.error("[redisUtils] Error during SCAN operation",{pattern:e,error:a instanceof Error?a.message:String(a),stack:a instanceof Error?a.stack:void 0}),a}}function rAe(r){let e=r.keyPrefix||"neurolink:conversation:",t=e.replace(/conversation:?$/,"user:sessions:");return{host:r.host||"localhost",port:r.port||6379,password:r.password||"",db:r.db||0,keyPrefix:e,userSessionsKeyPrefix:r.userSessionsKeyPrefix||t,ttl:r.ttl||86400,connectionOptions:{connectTimeout:3e4,lazyConnect:!0,retryDelayOnFailover:100,maxRetriesPerRequest:3,...r.connectionOptions}}}var c4t,Zl,dX,mX=I(()=>{"use strict";XU();te();c4t="session-only:",Zl=new Map,dX=new Map});var J0,fX,UF,nAe=I(async()=>{"use strict";Er();su();Yt();QIe();O8();await H1();K1();wt();gh();Jd();te();mX();J0=ft.redis,fX=5e3,UF=class{config;isInitialized=!1;summarizationEngine=new Tb;redisConfig;redisClient=null;pendingToolExecutions=new Map;titleGenerationInProgress=new Set;summarizationInProgress=new Set;constructor(e,t={}){this.config=e,this.redisConfig=rAe(t)}async initialize(){if(this.isInitialized){g.debug("[RedisConversationMemoryManager] Already initialized, skipping");return}await J0.startActiveSpan("neurolink.memory.initialize",{kind:jt.CLIENT,attributes:{"redis.host":this.redisConfig.host,"redis.port":this.redisConfig.port,"redis.key_prefix":this.redisConfig.keyPrefix}},async e=>{try{g.debug("[RedisConversationMemoryManager] Initializing with config",{host:this.redisConfig.host,port:this.redisConfig.port,keyPrefix:this.redisConfig.keyPrefix,ttl:this.redisConfig.ttl}),this.redisClient=await eAe(this.redisConfig),this.isInitialized=!0,g.info("RedisConversationMemoryManager initialized",{storage:"redis",host:this.redisConfig.host,port:this.redisConfig.port,maxSessions:this.config.maxSessions,maxTurnsPerSession:this.config.maxTurnsPerSession}),g.debug("[RedisConversationMemoryManager] Redis client created successfully",{clientType:this.redisClient?.constructor?.name||"unknown",isConnected:!!this.redisClient})}catch(t){throw e.setStatus({code:ke.ERROR,message:t instanceof Error?t.message:String(t)}),e.recordException(t instanceof Error?t:new Error(String(t))),g.error("[RedisConversationMemoryManager] Failed to initialize",{error:t instanceof Error?t.message:String(t),stack:t instanceof Error?t.stack:void 0,config:{host:this.redisConfig.host,port:this.redisConfig.port}}),new gi("Failed to initialize Redis conversation memory","CONFIG_ERROR",{error:t instanceof Error?t.message:String(t)})}finally{e.end()}})}get canPersist(){return this.isInitialized&&this.redisClient!==null&&this.redisClient.isOpen}get isRedisConfigured(){return this.redisClient!==null&&this.redisClient.isOpen}getHealthStatus(){return{initialized:this.isInitialized,connected:this.redisClient?.isOpen??!1,host:this.redisConfig.host,keyPrefix:this.redisConfig.keyPrefix}}async getSession(e,t,n){if(await this.ensureInitialized(),!this.redisClient)return;let o=this.redisClient;return J0.startActiveSpan("neurolink.memory.getSession",{kind:jt.CLIENT,attributes:{"session.id":e}},async s=>{t&&s.setAttribute("user.id",t);try{let i=xc(this.redisConfig,e,t),a=await Be(o.get(i),fX),l=bc(a||null);if(!l){s.setAttribute("session.found",!1);return}s.setAttribute("session.found",!0);let u=a?Buffer.byteLength(a,"utf8"):0,d=l.messages.length,p=!!l.summarizedUpToMessageId,m=p?l.messages.findIndex(h=>h.id===l.summarizedUpToMessageId):-1,f=p&&m!==-1?d-m-1:d;return s.setAttribute("message.count",d),s.setAttribute("blob.size_bytes",u),g.info("[ConversationMemory] Session loaded",{requestId:n,sessionId:e,blobSizeBytes:u,messageCount:d,hasSummary:p,recentMessageCount:f}),u>512*1024&&g.warn("[ConversationMemory] Large session blob",{requestId:n,sessionId:e,blobSizeBytes:u,messageCount:d}),{sessionId:l.sessionId,userId:l.userId,messages:l.messages,summarizedUpToMessageId:l.summarizedUpToMessageId,summarizedMessage:l.summarizedMessage,tokenThreshold:l.tokenThreshold,lastTokenCount:l.lastTokenCount,lastCountedAt:l.lastCountedAt,lastApiTokenCount:l.lastApiTokenCount,createdAt:new Date(l.createdAt).getTime(),lastActivity:new Date(l.updatedAt).getTime()}}catch(i){s.setStatus({code:ke.ERROR,message:i instanceof Error?i.message:String(i)}),s.recordException(i instanceof Error?i:new Error(String(i))),g.error("[RedisConversationMemoryManager] Failed to get session",{sessionId:e,userId:t,error:i instanceof Error?i.message:String(i)});return}finally{s.end()}})}async getSessionRaw(e,t){try{if(await this.ensureInitialized(),!this.redisClient)return null;let n=xc(this.redisConfig,e,t),o=await this.redisClient.get(n);return bc(o||null)}catch(n){return g.error("[RedisConversationMemoryManager] Failed to get raw session",{sessionId:e,userId:t,error:n instanceof Error?n.message:String(n)}),null}}async getUserSessions(e){if(await this.ensureInitialized(),!this.redisClient)return g.warn("[RedisConversationMemoryManager] Redis client not available",{userId:e}),[];try{let t=DF(this.redisConfig,e),n=await this.redisClient.sMembers(t);return Array.from(n).map(String)}catch(t){return g.error("[RedisConversationMemoryManager] Failed to get user sessions",{userId:e,error:t instanceof Error?t.message:String(t)}),[]}}async addUserSession(e,t){if(!(!this.redisClient||!e))try{let n=DF(this.redisConfig,e);await this.redisClient.sAdd(n,t),this.redisConfig.ttl>0&&await this.redisClient.expire(n,this.redisConfig.ttl)}catch(n){g.error("[RedisConversationMemoryManager] Failed to add session to user set",{userId:e,sessionId:t,error:n instanceof Error?n.message:String(n)})}}async removeUserSession(e,t){if(!this.redisClient||!e)return!1;try{let n=DF(this.redisConfig,e),o=await this.redisClient.sRem(n,t);return Number(o)>0}catch(n){return g.error("[RedisConversationMemoryManager] Failed to remove session from user set",{userId:e,sessionId:t,error:n instanceof Error?n.message:String(n)}),!1}}generateTimestamp(){return new Date().toISOString()}async storeToolExecution(e,t,n,o,s){g.debug("[RedisConversationMemoryManager] Storing tool execution temporarily",{sessionId:e,userId:t,toolCallsCount:n?.length||0,toolResultsCount:o?.length||0});try{let i=t||"randomUser",a=`${e}:${i}`,l={toolCalls:(n||[]).map(d=>({...d,timestamp:s})),toolResults:(o||[]).map(d=>({...d,timestamp:s})),timestamp:Date.now()},u=this.pendingToolExecutions.get(a);u&&(g.debug("[RedisConversationMemoryManager] Merging with existing pending tool data",{sessionId:e,existingToolCalls:u.toolCalls.length,existingToolResults:u.toolResults.length,newToolCalls:n?.length||0,newToolResults:o?.length||0}),l.toolCalls=[...u.toolCalls,...l.toolCalls],l.toolResults=[...u.toolResults,...l.toolResults]),this.pendingToolExecutions.set(a,l),g.debug("[RedisConversationMemoryManager] Tool execution stored temporarily",{sessionId:e,userId:i,pendingKey:a,totalToolCalls:l.toolCalls.length,totalToolResults:l.toolResults.length}),this.cleanupStalePendingData()}catch(i){g.error("[RedisConversationMemoryManager] Failed to store tool execution temporarily",{sessionId:e,error:i instanceof Error?i.message:String(i)})}}async storeConversationTurn(e){return g.debug("[RedisConversationMemoryManager] Storing conversation turn",{sessionId:e.sessionId,userId:e.userId}),await this.ensureInitialized(),J0.startActiveSpan("neurolink.memory.storeTurn",{kind:jt.CLIENT,attributes:{"session.id":e.sessionId,...e.userId&&{"user.id":e.userId}}},async t=>{try{if(!this.redisClient)throw new Error("Redis client not initialized");let n=xc(this.redisConfig,e.sessionId,e.userId),o=await this.redisClient.get(n),s=bc(o),i=new Date().toISOString(),a=e.userId||"randomUser";if(s)s.updatedAt=i;else{let h=`${e.sessionId}:${a}`,y=Qg(async()=>{if(!this.titleGenerationInProgress.has(h)){this.titleGenerationInProgress.add(h);try{let v=await this.generateConversationTitle(e.userMessage),T=xc(this.redisConfig,e.sessionId,e.userId||void 0),S=await this.redisClient?.get(T),_=bc(S||null);if(_){_.title=v,_.updatedAt=new Date().toISOString();let w=fT(_);await this.redisClient?.set(T,w),this.redisConfig.ttl>0&&await this.redisClient?.expire(T,this.redisConfig.ttl)}}catch(v){g.warn("[RedisConversationMemoryManager] Failed to generate conversation title in background",{sessionId:e.sessionId,userId:a,error:v instanceof Error?v.message:String(v)})}finally{this.titleGenerationInProgress.delete(h)}}});setImmediate(y),s={id:Ne(),title:"New Conversation",sessionId:e.sessionId,userId:a,createdAt:e.startTimeStamp?.toISOString()||i,updatedAt:e.startTimeStamp?.toISOString()||i,messages:[]}}let l=e.providerDetails?q1(e.providerDetails.provider,e.providerDetails.model,this.config.tokenThreshold,s.tokenThreshold):this.config.tokenThreshold||5e4,u={id:Ne(),timestamp:e.startTimeStamp?.toISOString()||this.generateTimestamp(),role:"user",content:e.userMessage};s.messages.push(u),await this.flushPendingToolData(s,e.sessionId,a);let d={id:Ne(),timestamp:this.generateTimestamp(),role:"assistant",content:e.aiResponse,events:e.events||void 0};if(s.messages.push(d),e.tokenUsage&&(s.lastApiTokenCount=e.tokenUsage),g.info("[RedisConversationMemoryManager] Added new messages",{sessionId:s.sessionId,userId:s.userId}),e.enableSummarization!==void 0?e.enableSummarization:this.config.enableSummarization){let h=e.userId||"randomUser",y=`${e.sessionId}:${h}`;if(this.summarizationInProgress.has(y))g.debug("[RedisConversationMemoryManager] Summarization already in progress, skipping",{sessionId:e.sessionId,userId:h});else{let v=Qg(async()=>{try{await this.checkAndSummarize(s,l,e.sessionId,e.userId,e.requestId)}catch(T){g.error("Background summarization failed",{sessionId:s.sessionId,error:T instanceof Error?T.message:String(T)})}});setImmediate(v)}}let m=fT(s);await this.redisClient.set(n,m);let f=Buffer.byteLength(m,"utf8");g.info("[ConversationMemory] Turn stored",{requestId:e.requestId,sessionId:e.sessionId,blobSizeBytes:f,totalMessages:s.messages.length,userMsgChars:e.userMessage.length,assistantMsgChars:e.aiResponse.length}),f>512*1024&&g.warn("[ConversationMemory] Large session blob",{requestId:e.requestId,sessionId:e.sessionId,blobSizeBytes:f,messageCount:s.messages.length}),this.redisConfig.ttl>0&&await this.redisClient.expire(n,this.redisConfig.ttl),e.userId&&await this.addUserSession(e.userId,e.sessionId),t.setAttribute("message.count",s.messages.length),t.setStatus({code:ke.OK}),g.debug("[RedisConversationMemoryManager] Successfully stored conversation turn",{sessionId:e.sessionId,totalMessages:s.messages.length,title:s.title})}catch(n){throw t.setStatus({code:ke.ERROR,message:n instanceof Error?n.message:String(n)}),t.recordException(n instanceof Error?n:new Error(String(n))),new gi(`Failed to store conversation turn in Redis for session ${e.sessionId}`,"STORAGE_ERROR",{sessionId:e.sessionId,error:n instanceof Error?n.message:String(n)})}finally{t.end()}})}async checkAndSummarize(e,t,n,o,s){let i=o||"randomUser",a=`${n}:${i}`;if(this.summarizationInProgress.has(a)){g.debug("[RedisConversationMemoryManager] Summarization already in progress, skipping",{sessionId:n,userId:i});return}this.summarizationInProgress.add(a);try{let l={sessionId:e.sessionId,userId:e.userId,messages:e.messages,summarizedUpToMessageId:e.summarizedUpToMessageId,summarizedMessage:e.summarizedMessage,tokenThreshold:e.tokenThreshold,lastTokenCount:e.lastTokenCount,lastCountedAt:e.lastCountedAt,createdAt:new Date(e.createdAt).getTime(),lastActivity:new Date(e.updatedAt).getTime()},u=await this.summarizationEngine.checkAndSummarize(l,t,this.config,"[RedisConversationMemoryManager]",s);if(e.lastTokenCount=l.lastTokenCount,e.lastCountedAt=l.lastCountedAt,u&&(e.summarizedUpToMessageId=l.summarizedUpToMessageId,e.summarizedMessage=l.summarizedMessage,this.redisClient)){let d=xc(this.redisConfig,n,o),p=await this.redisClient.get(d);if(p){let m=bc(p);if(m){m.summarizedUpToMessageId=e.summarizedUpToMessageId,m.summarizedMessage=e.summarizedMessage,m.lastTokenCount=e.lastTokenCount,m.lastCountedAt=e.lastCountedAt;let f=fT(m);await this.redisClient.set(d,f),this.redisConfig.ttl>0&&await this.redisClient.expire(d,this.redisConfig.ttl)}}}}catch(l){g.error("Token counting or summarization failed",{sessionId:e.sessionId,error:l instanceof Error?l.message:String(l)})}finally{this.summarizationInProgress.delete(a)}}async buildContextMessages(e,t,n,o){if(await this.ensureInitialized(),!this.redisClient)return g.warn("[RedisConversationMemoryManager] Redis client not available in buildContextMessages"),[];let s=this.redisClient;return J0.startActiveSpan("neurolink.memory.buildContext",{kind:jt.CLIENT,attributes:{"session.id":e,...t&&{"user.id":t}}},async i=>{try{g.info("[RedisConversationMemoryManager] Building context messages",{sessionId:e,userId:t,method:"buildContextMessages"});let a=xc(this.redisConfig,e,t),l=await Be(s.get(a),fX),u=bc(l||null);if(!u)return i.setAttribute("session.found",!1),i.setStatus({code:ke.OK}),[];let d={sessionId:u.sessionId,userId:u.userId,messages:u.messages,summarizedUpToMessageId:u.summarizedUpToMessageId,summarizedMessage:u.summarizedMessage,tokenThreshold:u.tokenThreshold,lastTokenCount:u.lastTokenCount,lastCountedAt:u.lastCountedAt,createdAt:new Date(u.createdAt).getTime(),lastActivity:new Date(u.updatedAt).getTime()},p=xb(d,o),m=this.config?.contextCompaction?.sendToolPreview===!0,f=p.map(h=>{if(h.role!=="tool_result")return h;let y=m&&h.metadata?.toolOutputPreview?h.metadata.toolOutputPreview:h.content,v=h.result;if(h.result&&h.result.result===void 0){let T=y;try{T=JSON.parse(y)}catch{}v={...h.result,result:T}}return{...h,content:y,result:v}});return i.setAttribute("context.message_count",f.length),i.setStatus({code:ke.OK}),g.info("[RedisConversationMemoryManager] Retrieved context messages",{sessionId:e,userId:t}),f}catch(a){throw i.setStatus({code:ke.ERROR,message:a instanceof Error?a.message:String(a)}),i.recordException(a instanceof Error?a:new Error(String(a))),a}finally{i.end()}})}async getUserSessionMetadata(e,t){if(g.debug("[RedisConversationMemoryManager] Getting user session metadata",{userId:e,sessionId:t}),await this.ensureInitialized(),!this.redisClient)return g.warn("[RedisConversationMemoryManager] Redis client not available",{userId:e,sessionId:t}),null;try{let n=xc(this.redisConfig,t,e),o=await this.redisClient.get(n);if(!o)return g.debug("[RedisConversationMemoryManager] No session data found",{userId:e,sessionId:t,sessionKey:n}),null;let s=bc(o);return s?{id:s.sessionId,title:s.title,createdAt:s.createdAt,updatedAt:s.updatedAt,metadata:s.additionalMetadata?.agenticLoopReports?{agenticLoopReports:s.additionalMetadata.agenticLoopReports}:void 0}:(g.debug("[RedisConversationMemoryManager] No valid conversation data found",{userId:e,sessionId:t,sessionKey:n}),null)}catch(n){return g.error("[RedisConversationMemoryManager] Failed to get user session metadata",{userId:e,sessionId:t,error:n instanceof Error?n.message:String(n),stack:n instanceof Error?n.stack:void 0}),null}}async getUserSessionHistory(e,t){g.debug("[RedisConversationMemoryManager] Getting user session history via getUserSessionObject",{userId:e,sessionId:t});try{let n=await this.getUserSessionObject(e,t);return n?n.messages:(g.debug("[RedisConversationMemoryManager] No session object found, returning null",{userId:e,sessionId:t}),null)}catch(n){return g.error("[RedisConversationMemoryManager] Failed to get user session history via getUserSessionObject",{userId:e,sessionId:t,error:n instanceof Error?n.message:String(n),errorName:n instanceof Error?n.name:"UnknownError",stack:n instanceof Error?n.stack:void 0}),null}}async getUserSessionObject(e,t){if(g.debug("[RedisConversationMemoryManager] Getting complete user session object",{userId:e,sessionId:t,method:"getUserSessionObject"}),!e||typeof e!="string")return g.warn("[RedisConversationMemoryManager] Invalid userId provided",{userId:e,sessionId:t}),null;if(!t||typeof t!="string")return g.warn("[RedisConversationMemoryManager] Invalid sessionId provided",{userId:e,sessionId:t}),null;if(await this.ensureInitialized(),!this.redisClient)return g.warn("[RedisConversationMemoryManager] Redis client not available for getUserSessionObject",{userId:e,sessionId:t}),null;try{let n=xc(this.redisConfig,t,e),o=await this.redisClient.get(n);if(!o)return g.debug("[RedisConversationMemoryManager] No conversation data found in Redis",{userId:e,sessionId:t,sessionKey:n}),null;let s=bc(o);return s?!s.messages||!Array.isArray(s.messages)?(g.warn("[RedisConversationMemoryManager] Invalid conversation structure - missing messages array",{userId:e,sessionId:t,hasMessages:!!s.messages,messagesType:typeof s.messages}),null):s:(g.debug("[RedisConversationMemoryManager] Failed to deserialize conversation data",{userId:e,sessionId:t,sessionKey:n,dataLength:o.length}),null)}catch(n){return g.error("[RedisConversationMemoryManager] Failed to get complete user session object",{userId:e,sessionId:t,error:n instanceof Error?n.message:String(n),errorName:n instanceof Error?n.name:"UnknownError",stack:n instanceof Error?n.stack:void 0}),null}}async generateConversationTitle(e){g.info("[RedisConversationMemoryManager] Generating conversation title",{userMessageLength:e.length,userMessagePreview:e.substring(0,100)});try{let t=new zh({conversationMemory:{enabled:!1}}),n=`Generate a clear, concise, and descriptive title (5\u20138 words maximum) for a conversation based on the following user message.
2020
+ `;return{preview:h+_+y,truncated:!0,originalSize:l}}var a4t,QIe=I(()=>{"use strict";a4t="retrieve_context"});async function eAe(r){let e=r.url?`url:${r.url.replace(/:\/\/[^:]+:[^@]+@/,"://[redacted]@")}`:`${r.host}:${r.port}:${r.db}:${r.password?"auth":"noauth"}`,t=Zl.get(e);if(t&&t.client.isOpen)return t.refCount++,g.debug("[Redis] Reusing pooled connection",{key:e,refCount:t.refCount}),t.client;let n=dX.get(e);if(n){let s=await n,i=Zl.get(e);if(i){if(!i.client.isOpen){Zl.delete(e);let a=await pX(r);return Zl.set(e,{client:a,refCount:1}),a}i.refCount++}else if(s.isOpen)Zl.set(e,{client:s,refCount:1});else{let a=await pX(r);return Zl.set(e,{client:a,refCount:1}),a}return s}t&&Zl.delete(e);let o=pX(r);dX.set(e,o);try{let s=await o;return Zl.set(e,{client:s,refCount:1}),g.info("[Redis] Created pooled connection",{key:e,refCount:1}),s}finally{dX.delete(e)}}async function tAe(r){let e=`${r.host}:${r.port}:${r.db}:${r.password?"auth":"noauth"}`,t=Zl.get(e);if(t&&(t.refCount--,g.debug("[Redis] Released pooled connection",{key:e,refCount:t.refCount}),t.refCount<=0)){try{t.client.isOpen&&await t.client.quit()}catch(n){g.warn("[Redis] Error closing pooled connection",{key:e,error:String(n)})}Zl.delete(e),g.info("[Redis] Closed pooled connection",{key:e})}}function l4t(){return Array.from(Zl.entries()).map(([r,e])=>({key:r,refCount:e.refCount,isOpen:e.client.isOpen}))}async function pX(r){let t={url:r.url||`redis://${r.host}:${r.port}/${r.db}`,socket:{connectTimeout:r.connectionOptions?.connectTimeout,reconnectStrategy:o=>o>(r.connectionOptions?.maxRetriesPerRequest||3)?(g.error("Redis connection retries exhausted"),new Error("Redis connection retries exhausted")):Math.min((r.connectionOptions?.retryDelayOnFailover||100)*2**o,1e4)}};r.password&&!r.url&&(t.password=r.password);let n=I0(t);return n.on("error",o=>{let s=o.message.replace(/redis:\/\/.*?@/g,"redis://[redacted]@");g.error("Redis client error",{error:s})}),n.on("connect",()=>{g.debug("Redis client connected",{host:r.host,port:r.port,db:r.db})}),n.on("reconnecting",()=>{g.debug("Redis client reconnecting")}),n.isOpen||await n.connect(),n}function xc(r,e,t){if(!t)return g.warn("[REDIS] getSessionKey called without userId",{sessionId:e}),`${r.keyPrefix}${c4t}${e}`;let n=`${r.keyPrefix}${t}:${e}`;return g.debug("[redisUtils] Generated session key",{sessionId:e,userId:t,keyPrefix:r.keyPrefix,fullKey:n}),n}function DF(r,e){return`${r.userSessionsKeyPrefix}${e}`}function fT(r){try{return JSON.stringify(r)}catch(e){throw g.error("[redisUtils] Failed to serialize conversation",{error:e instanceof Error?e.message:String(e),stack:e instanceof Error?e.stack:void 0,sessionId:r?.sessionId,userId:r?.userId}),e}}function bc(r){if(!r)return null;try{let e=JSON.parse(String(r));if(typeof e!="object"||e===null||!("title"in e)||!("sessionId"in e)||!("userId"in e)||!("createdAt"in e)||!("updatedAt"in e)||!("messages"in e))return g.warn("[redisUtils] Deserialized data is not a valid conversation object",{type:typeof e,hasRequiredFields:e&&typeof e=="object"?Object.keys(e).join(", "):"none",preview:JSON.stringify(e).substring(0,100)}),null;let t=e;return Array.isArray(t.messages)?t.messages.every(o=>typeof o=="object"&&o!==null&&"role"in o&&"content"in o&&typeof o.role=="string"&&typeof o.content=="string"&&(o.role==="user"||o.role==="assistant"||o.role==="system"||o.role==="tool_call"||o.role==="tool_result"))?(g.debug("[redisUtils] Conversation deserialized successfully",{sessionId:t.sessionId,userId:t.userId,title:t.title,messageCount:t.messages.length,createdAt:t.createdAt,updatedAt:t.updatedAt}),t):(g.warn("[redisUtils] Invalid messages structure",{messageCount:t.messages.length,firstMessage:t.messages.length>0?JSON.stringify(t.messages[0]):null}),null):(g.warn("[redisUtils] messages is not an array",{type:typeof t.messages}),null)}catch(e){return g.error("[redisUtils] Failed to deserialize conversation",{error:e instanceof Error?e.message:String(e),stack:e instanceof Error?e.stack:void 0,dataLength:r.length,dataPreview:"[REDACTED]"}),null}}async function LF(r,e,t=100){g.debug("[redisUtils] Starting SCAN operation",{pattern:e,batchSize:t});let n=[],o="0",s=0,i=0;try{do{s++;let a=await r.scan(o,{MATCH:e,COUNT:t});o=String(a.cursor);let l=(a.keys||[]).map(String);n.push(...l),i+=l.length,g.debug("[redisUtils] SCAN iteration completed",{iteration:s,currentCursor:o,keysInBatch:l.length,totalKeysFound:n.length})}while(o!=="0");return g.info("[redisUtils] SCAN operation completed",{pattern:e,totalIterations:s,totalKeysFound:n.length,totalScanned:i}),n}catch(a){throw g.error("[redisUtils] Error during SCAN operation",{pattern:e,error:a instanceof Error?a.message:String(a),stack:a instanceof Error?a.stack:void 0}),a}}function rAe(r){let e=r.keyPrefix||"neurolink:conversation:",t=e.replace(/conversation:?$/,"user:sessions:"),n=r.host||"localhost",o=r.port||6379,s=r.password||"",i=r.db||0,a=r.url;if(a)try{let l=new URL(a);n=l.hostname,o=l.port?parseInt(l.port):6379,s=l.password||s,i=l.pathname&&parseInt(l.pathname.replace("/",""))||0}catch(l){let u=a.replace(/:\/\/[^:]+:[^@]+@/,"://[redacted]@");g.warn("[redisUtils] Failed to parse Redis URL, falling back to component-based connection",{url:u,error:l instanceof Error?l.message:String(l)}),a=void 0}return{url:a||"",host:n,port:o,password:s,db:i,keyPrefix:e,userSessionsKeyPrefix:r.userSessionsKeyPrefix||t,ttl:r.ttl||86400,connectionOptions:{connectTimeout:3e4,lazyConnect:!0,retryDelayOnFailover:100,maxRetriesPerRequest:3,...r.connectionOptions}}}var c4t,Zl,dX,mX=I(()=>{"use strict";XU();te();c4t="session-only:",Zl=new Map,dX=new Map});var J0,fX,UF,nAe=I(async()=>{"use strict";Er();su();Yt();QIe();O8();await H1();K1();wt();gh();Jd();te();mX();J0=ft.redis,fX=5e3,UF=class{config;isInitialized=!1;summarizationEngine=new Tb;redisConfig;redisClient=null;pendingToolExecutions=new Map;titleGenerationInProgress=new Set;summarizationInProgress=new Set;constructor(e,t={}){this.config=e,this.redisConfig=rAe(t)}async initialize(){if(this.isInitialized){g.debug("[RedisConversationMemoryManager] Already initialized, skipping");return}await J0.startActiveSpan("neurolink.memory.initialize",{kind:jt.CLIENT,attributes:{"redis.host":this.redisConfig.host,"redis.port":this.redisConfig.port,"redis.key_prefix":this.redisConfig.keyPrefix}},async e=>{try{g.debug("[RedisConversationMemoryManager] Initializing with config",{host:this.redisConfig.host,port:this.redisConfig.port,keyPrefix:this.redisConfig.keyPrefix,ttl:this.redisConfig.ttl}),this.redisClient=await eAe(this.redisConfig),this.isInitialized=!0,g.info("RedisConversationMemoryManager initialized",{storage:"redis",host:this.redisConfig.host,port:this.redisConfig.port,maxSessions:this.config.maxSessions,maxTurnsPerSession:this.config.maxTurnsPerSession}),g.debug("[RedisConversationMemoryManager] Redis client created successfully",{clientType:this.redisClient?.constructor?.name||"unknown",isConnected:!!this.redisClient})}catch(t){throw e.setStatus({code:ke.ERROR,message:t instanceof Error?t.message:String(t)}),e.recordException(t instanceof Error?t:new Error(String(t))),g.error("[RedisConversationMemoryManager] Failed to initialize",{error:t instanceof Error?t.message:String(t),stack:t instanceof Error?t.stack:void 0,config:{host:this.redisConfig.host,port:this.redisConfig.port}}),new gi("Failed to initialize Redis conversation memory","CONFIG_ERROR",{error:t instanceof Error?t.message:String(t)})}finally{e.end()}})}get canPersist(){return this.isInitialized&&this.redisClient!==null&&this.redisClient.isOpen}get isRedisConfigured(){return this.redisClient!==null&&this.redisClient.isOpen}getHealthStatus(){return{initialized:this.isInitialized,connected:this.redisClient?.isOpen??!1,host:this.redisConfig.host,keyPrefix:this.redisConfig.keyPrefix}}async getSession(e,t,n){if(await this.ensureInitialized(),!this.redisClient)return;let o=this.redisClient;return J0.startActiveSpan("neurolink.memory.getSession",{kind:jt.CLIENT,attributes:{"session.id":e}},async s=>{t&&s.setAttribute("user.id",t);try{let i=xc(this.redisConfig,e,t),a=await Be(o.get(i),fX),l=bc(a||null);if(!l){s.setAttribute("session.found",!1);return}s.setAttribute("session.found",!0);let u=a?Buffer.byteLength(a,"utf8"):0,d=l.messages.length,p=!!l.summarizedUpToMessageId,m=p?l.messages.findIndex(h=>h.id===l.summarizedUpToMessageId):-1,f=p&&m!==-1?d-m-1:d;return s.setAttribute("message.count",d),s.setAttribute("blob.size_bytes",u),g.info("[ConversationMemory] Session loaded",{requestId:n,sessionId:e,blobSizeBytes:u,messageCount:d,hasSummary:p,recentMessageCount:f}),u>512*1024&&g.warn("[ConversationMemory] Large session blob",{requestId:n,sessionId:e,blobSizeBytes:u,messageCount:d}),{sessionId:l.sessionId,userId:l.userId,messages:l.messages,summarizedUpToMessageId:l.summarizedUpToMessageId,summarizedMessage:l.summarizedMessage,tokenThreshold:l.tokenThreshold,lastTokenCount:l.lastTokenCount,lastCountedAt:l.lastCountedAt,lastApiTokenCount:l.lastApiTokenCount,createdAt:new Date(l.createdAt).getTime(),lastActivity:new Date(l.updatedAt).getTime()}}catch(i){s.setStatus({code:ke.ERROR,message:i instanceof Error?i.message:String(i)}),s.recordException(i instanceof Error?i:new Error(String(i))),g.error("[RedisConversationMemoryManager] Failed to get session",{sessionId:e,userId:t,error:i instanceof Error?i.message:String(i)});return}finally{s.end()}})}async getSessionRaw(e,t){try{if(await this.ensureInitialized(),!this.redisClient)return null;let n=xc(this.redisConfig,e,t),o=await this.redisClient.get(n);return bc(o||null)}catch(n){return g.error("[RedisConversationMemoryManager] Failed to get raw session",{sessionId:e,userId:t,error:n instanceof Error?n.message:String(n)}),null}}async getUserSessions(e){if(await this.ensureInitialized(),!this.redisClient)return g.warn("[RedisConversationMemoryManager] Redis client not available",{userId:e}),[];try{let t=DF(this.redisConfig,e),n=await this.redisClient.sMembers(t);return Array.from(n).map(String)}catch(t){return g.error("[RedisConversationMemoryManager] Failed to get user sessions",{userId:e,error:t instanceof Error?t.message:String(t)}),[]}}async addUserSession(e,t){if(!(!this.redisClient||!e))try{let n=DF(this.redisConfig,e);await this.redisClient.sAdd(n,t),this.redisConfig.ttl>0&&await this.redisClient.expire(n,this.redisConfig.ttl)}catch(n){g.error("[RedisConversationMemoryManager] Failed to add session to user set",{userId:e,sessionId:t,error:n instanceof Error?n.message:String(n)})}}async removeUserSession(e,t){if(!this.redisClient||!e)return!1;try{let n=DF(this.redisConfig,e),o=await this.redisClient.sRem(n,t);return Number(o)>0}catch(n){return g.error("[RedisConversationMemoryManager] Failed to remove session from user set",{userId:e,sessionId:t,error:n instanceof Error?n.message:String(n)}),!1}}generateTimestamp(){return new Date().toISOString()}async storeToolExecution(e,t,n,o,s){g.debug("[RedisConversationMemoryManager] Storing tool execution temporarily",{sessionId:e,userId:t,toolCallsCount:n?.length||0,toolResultsCount:o?.length||0});try{let i=t||"randomUser",a=`${e}:${i}`,l={toolCalls:(n||[]).map(d=>({...d,timestamp:s})),toolResults:(o||[]).map(d=>({...d,timestamp:s})),timestamp:Date.now()},u=this.pendingToolExecutions.get(a);u&&(g.debug("[RedisConversationMemoryManager] Merging with existing pending tool data",{sessionId:e,existingToolCalls:u.toolCalls.length,existingToolResults:u.toolResults.length,newToolCalls:n?.length||0,newToolResults:o?.length||0}),l.toolCalls=[...u.toolCalls,...l.toolCalls],l.toolResults=[...u.toolResults,...l.toolResults]),this.pendingToolExecutions.set(a,l),g.debug("[RedisConversationMemoryManager] Tool execution stored temporarily",{sessionId:e,userId:i,pendingKey:a,totalToolCalls:l.toolCalls.length,totalToolResults:l.toolResults.length}),this.cleanupStalePendingData()}catch(i){g.error("[RedisConversationMemoryManager] Failed to store tool execution temporarily",{sessionId:e,error:i instanceof Error?i.message:String(i)})}}async storeConversationTurn(e){return g.debug("[RedisConversationMemoryManager] Storing conversation turn",{sessionId:e.sessionId,userId:e.userId}),await this.ensureInitialized(),J0.startActiveSpan("neurolink.memory.storeTurn",{kind:jt.CLIENT,attributes:{"session.id":e.sessionId,...e.userId&&{"user.id":e.userId}}},async t=>{try{if(!this.redisClient)throw new Error("Redis client not initialized");let n=xc(this.redisConfig,e.sessionId,e.userId),o=await this.redisClient.get(n),s=bc(o),i=new Date().toISOString(),a=e.userId||"randomUser";if(s)s.updatedAt=i;else{let h=`${e.sessionId}:${a}`,y=Qg(async()=>{if(!this.titleGenerationInProgress.has(h)){this.titleGenerationInProgress.add(h);try{let v=await this.generateConversationTitle(e.userMessage),T=xc(this.redisConfig,e.sessionId,e.userId||void 0),S=await this.redisClient?.get(T),_=bc(S||null);if(_){_.title=v,_.updatedAt=new Date().toISOString();let w=fT(_);await this.redisClient?.set(T,w),this.redisConfig.ttl>0&&await this.redisClient?.expire(T,this.redisConfig.ttl)}}catch(v){g.warn("[RedisConversationMemoryManager] Failed to generate conversation title in background",{sessionId:e.sessionId,userId:a,error:v instanceof Error?v.message:String(v)})}finally{this.titleGenerationInProgress.delete(h)}}});setImmediate(y),s={id:Ne(),title:"New Conversation",sessionId:e.sessionId,userId:a,createdAt:e.startTimeStamp?.toISOString()||i,updatedAt:e.startTimeStamp?.toISOString()||i,messages:[]}}let l=e.providerDetails?q1(e.providerDetails.provider,e.providerDetails.model,this.config.tokenThreshold,s.tokenThreshold):this.config.tokenThreshold||5e4,u={id:Ne(),timestamp:e.startTimeStamp?.toISOString()||this.generateTimestamp(),role:"user",content:e.userMessage};s.messages.push(u),await this.flushPendingToolData(s,e.sessionId,a);let d={id:Ne(),timestamp:this.generateTimestamp(),role:"assistant",content:e.aiResponse,events:e.events||void 0};if(s.messages.push(d),e.tokenUsage&&(s.lastApiTokenCount=e.tokenUsage),g.info("[RedisConversationMemoryManager] Added new messages",{sessionId:s.sessionId,userId:s.userId}),e.enableSummarization!==void 0?e.enableSummarization:this.config.enableSummarization){let h=e.userId||"randomUser",y=`${e.sessionId}:${h}`;if(this.summarizationInProgress.has(y))g.debug("[RedisConversationMemoryManager] Summarization already in progress, skipping",{sessionId:e.sessionId,userId:h});else{let v=Qg(async()=>{try{await this.checkAndSummarize(s,l,e.sessionId,e.userId,e.requestId)}catch(T){g.error("Background summarization failed",{sessionId:s.sessionId,error:T instanceof Error?T.message:String(T)})}});setImmediate(v)}}let m=fT(s);await this.redisClient.set(n,m);let f=Buffer.byteLength(m,"utf8");g.info("[ConversationMemory] Turn stored",{requestId:e.requestId,sessionId:e.sessionId,blobSizeBytes:f,totalMessages:s.messages.length,userMsgChars:e.userMessage.length,assistantMsgChars:e.aiResponse.length}),f>512*1024&&g.warn("[ConversationMemory] Large session blob",{requestId:e.requestId,sessionId:e.sessionId,blobSizeBytes:f,messageCount:s.messages.length}),this.redisConfig.ttl>0&&await this.redisClient.expire(n,this.redisConfig.ttl),e.userId&&await this.addUserSession(e.userId,e.sessionId),t.setAttribute("message.count",s.messages.length),t.setStatus({code:ke.OK}),g.debug("[RedisConversationMemoryManager] Successfully stored conversation turn",{sessionId:e.sessionId,totalMessages:s.messages.length,title:s.title})}catch(n){throw t.setStatus({code:ke.ERROR,message:n instanceof Error?n.message:String(n)}),t.recordException(n instanceof Error?n:new Error(String(n))),new gi(`Failed to store conversation turn in Redis for session ${e.sessionId}`,"STORAGE_ERROR",{sessionId:e.sessionId,error:n instanceof Error?n.message:String(n)})}finally{t.end()}})}async checkAndSummarize(e,t,n,o,s){let i=o||"randomUser",a=`${n}:${i}`;if(this.summarizationInProgress.has(a)){g.debug("[RedisConversationMemoryManager] Summarization already in progress, skipping",{sessionId:n,userId:i});return}this.summarizationInProgress.add(a);try{let l={sessionId:e.sessionId,userId:e.userId,messages:e.messages,summarizedUpToMessageId:e.summarizedUpToMessageId,summarizedMessage:e.summarizedMessage,tokenThreshold:e.tokenThreshold,lastTokenCount:e.lastTokenCount,lastCountedAt:e.lastCountedAt,createdAt:new Date(e.createdAt).getTime(),lastActivity:new Date(e.updatedAt).getTime()},u=await this.summarizationEngine.checkAndSummarize(l,t,this.config,"[RedisConversationMemoryManager]",s);if(e.lastTokenCount=l.lastTokenCount,e.lastCountedAt=l.lastCountedAt,u&&(e.summarizedUpToMessageId=l.summarizedUpToMessageId,e.summarizedMessage=l.summarizedMessage,this.redisClient)){let d=xc(this.redisConfig,n,o),p=await this.redisClient.get(d);if(p){let m=bc(p);if(m){m.summarizedUpToMessageId=e.summarizedUpToMessageId,m.summarizedMessage=e.summarizedMessage,m.lastTokenCount=e.lastTokenCount,m.lastCountedAt=e.lastCountedAt;let f=fT(m);await this.redisClient.set(d,f),this.redisConfig.ttl>0&&await this.redisClient.expire(d,this.redisConfig.ttl)}}}}catch(l){g.error("Token counting or summarization failed",{sessionId:e.sessionId,error:l instanceof Error?l.message:String(l)})}finally{this.summarizationInProgress.delete(a)}}async buildContextMessages(e,t,n,o){if(await this.ensureInitialized(),!this.redisClient)return g.warn("[RedisConversationMemoryManager] Redis client not available in buildContextMessages"),[];let s=this.redisClient;return J0.startActiveSpan("neurolink.memory.buildContext",{kind:jt.CLIENT,attributes:{"session.id":e,...t&&{"user.id":t}}},async i=>{try{g.info("[RedisConversationMemoryManager] Building context messages",{sessionId:e,userId:t,method:"buildContextMessages"});let a=xc(this.redisConfig,e,t),l=await Be(s.get(a),fX),u=bc(l||null);if(!u)return i.setAttribute("session.found",!1),i.setStatus({code:ke.OK}),[];let d={sessionId:u.sessionId,userId:u.userId,messages:u.messages,summarizedUpToMessageId:u.summarizedUpToMessageId,summarizedMessage:u.summarizedMessage,tokenThreshold:u.tokenThreshold,lastTokenCount:u.lastTokenCount,lastCountedAt:u.lastCountedAt,createdAt:new Date(u.createdAt).getTime(),lastActivity:new Date(u.updatedAt).getTime()},p=xb(d,o),m=this.config?.contextCompaction?.sendToolPreview===!0,f=p.map(h=>{if(h.role!=="tool_result")return h;let y=m&&h.metadata?.toolOutputPreview?h.metadata.toolOutputPreview:h.content,v=h.result;if(h.result&&h.result.result===void 0){let T=y;try{T=JSON.parse(y)}catch{}v={...h.result,result:T}}return{...h,content:y,result:v}});return i.setAttribute("context.message_count",f.length),i.setStatus({code:ke.OK}),g.info("[RedisConversationMemoryManager] Retrieved context messages",{sessionId:e,userId:t}),f}catch(a){throw i.setStatus({code:ke.ERROR,message:a instanceof Error?a.message:String(a)}),i.recordException(a instanceof Error?a:new Error(String(a))),a}finally{i.end()}})}async getUserSessionMetadata(e,t){if(g.debug("[RedisConversationMemoryManager] Getting user session metadata",{userId:e,sessionId:t}),await this.ensureInitialized(),!this.redisClient)return g.warn("[RedisConversationMemoryManager] Redis client not available",{userId:e,sessionId:t}),null;try{let n=xc(this.redisConfig,t,e),o=await this.redisClient.get(n);if(!o)return g.debug("[RedisConversationMemoryManager] No session data found",{userId:e,sessionId:t,sessionKey:n}),null;let s=bc(o);return s?{id:s.sessionId,title:s.title,createdAt:s.createdAt,updatedAt:s.updatedAt,metadata:s.additionalMetadata?.agenticLoopReports?{agenticLoopReports:s.additionalMetadata.agenticLoopReports}:void 0}:(g.debug("[RedisConversationMemoryManager] No valid conversation data found",{userId:e,sessionId:t,sessionKey:n}),null)}catch(n){return g.error("[RedisConversationMemoryManager] Failed to get user session metadata",{userId:e,sessionId:t,error:n instanceof Error?n.message:String(n),stack:n instanceof Error?n.stack:void 0}),null}}async getUserSessionHistory(e,t){g.debug("[RedisConversationMemoryManager] Getting user session history via getUserSessionObject",{userId:e,sessionId:t});try{let n=await this.getUserSessionObject(e,t);return n?n.messages:(g.debug("[RedisConversationMemoryManager] No session object found, returning null",{userId:e,sessionId:t}),null)}catch(n){return g.error("[RedisConversationMemoryManager] Failed to get user session history via getUserSessionObject",{userId:e,sessionId:t,error:n instanceof Error?n.message:String(n),errorName:n instanceof Error?n.name:"UnknownError",stack:n instanceof Error?n.stack:void 0}),null}}async getUserSessionObject(e,t){if(g.debug("[RedisConversationMemoryManager] Getting complete user session object",{userId:e,sessionId:t,method:"getUserSessionObject"}),!e||typeof e!="string")return g.warn("[RedisConversationMemoryManager] Invalid userId provided",{userId:e,sessionId:t}),null;if(!t||typeof t!="string")return g.warn("[RedisConversationMemoryManager] Invalid sessionId provided",{userId:e,sessionId:t}),null;if(await this.ensureInitialized(),!this.redisClient)return g.warn("[RedisConversationMemoryManager] Redis client not available for getUserSessionObject",{userId:e,sessionId:t}),null;try{let n=xc(this.redisConfig,t,e),o=await this.redisClient.get(n);if(!o)return g.debug("[RedisConversationMemoryManager] No conversation data found in Redis",{userId:e,sessionId:t,sessionKey:n}),null;let s=bc(o);return s?!s.messages||!Array.isArray(s.messages)?(g.warn("[RedisConversationMemoryManager] Invalid conversation structure - missing messages array",{userId:e,sessionId:t,hasMessages:!!s.messages,messagesType:typeof s.messages}),null):s:(g.debug("[RedisConversationMemoryManager] Failed to deserialize conversation data",{userId:e,sessionId:t,sessionKey:n,dataLength:o.length}),null)}catch(n){return g.error("[RedisConversationMemoryManager] Failed to get complete user session object",{userId:e,sessionId:t,error:n instanceof Error?n.message:String(n),errorName:n instanceof Error?n.name:"UnknownError",stack:n instanceof Error?n.stack:void 0}),null}}async generateConversationTitle(e){g.info("[RedisConversationMemoryManager] Generating conversation title",{userMessageLength:e.length,userMessagePreview:e.substring(0,100)});try{let t=new zh({conversationMemory:{enabled:!1}}),n=`Generate a clear, concise, and descriptive title (5\u20138 words maximum) for a conversation based on the following user message.
2021
2021
  The title must meaningfully reflect the topic or intent of the message.
2022
2022
  Do not output anything unrelated, vague, or generic.
2023
2023
  Do not say you cannot create a title. Always return a valid title.
2024
2024
 
2025
2025
  User message: "${e}"`,o=await t.generate({input:{text:n},provider:this.config.summarizationProvider||"vertex",model:this.config.summarizationModel||"gemini-2.5-flash",disableTools:!0}),s=o.content?.trim()||"New Conversation";return s=s.replace(/^(Title:|Here's a title:|The title is:)\s*/i,""),s=s.replace(/['"]/g,""),s=s.replace(/\.$/,""),s.length>60&&(s=s.substring(0,57)+"..."),s.length<3&&(s="New Conversation"),g.info("[RedisConversationMemoryManager] Generated conversation title",{originalLength:o.content?.length||0,cleanedTitle:s,titleLength:s.length}),s}catch(t){return g.error("[RedisConversationMemoryManager] Failed to generate conversation title",{error:t instanceof Error?t.message:String(t),userMessagePreview:e.substring(0,100)}),e.length>30?e.substring(0,30)+"...":e||"New Conversation"}}createSummarySystemMessage(e,t,n){return{id:`summary-${Ne()}`,role:"system",content:`Summary of previous conversation turns:
2026
2026
 
2027
- ${e}`,timestamp:new Date().toISOString(),metadata:{isSummary:!0,summarizesFrom:t,summarizesTo:n}}}async getSessionMessages(e,t){if(await this.ensureInitialized(),!this.redisClient)return[];try{let n=xc(this.redisConfig,e,t),o=await this.redisClient.get(n);return bc(o||null)?.messages??[]}catch(n){return g.error("[RedisConversationMemoryManager] Failed to get session messages",{sessionId:e,userId:t,error:n instanceof Error?n.message:String(n)}),[]}}async setSessionMessages(e,t,n){if(await this.ensureInitialized(),!this.redisClient)throw new gi("Redis client not initialized","STORAGE_ERROR",{sessionId:e});try{let o=xc(this.redisConfig,e,n),s=await this.redisClient.get(o),i=bc(s||null);if(!i)throw new gi(`Session ${e} not found`,"STORAGE_ERROR",{sessionId:e});i.messages=t,i.updatedAt=new Date().toISOString(),i.summarizedUpToMessageId=void 0,i.summarizedMessage=void 0,i.lastTokenCount=void 0,i.lastCountedAt=void 0;let a=fT(i);await this.redisClient.set(o,a),this.redisConfig.ttl>0&&await this.redisClient.expire(o,this.redisConfig.ttl),g.debug("[RedisConversationMemoryManager] Session messages replaced",{sessionId:e,userId:n,messageCount:t.length})}catch(o){throw o instanceof gi?o:new gi(`Failed to set session messages for session ${e}`,"STORAGE_ERROR",{sessionId:e,error:o instanceof Error?o.message:String(o)})}}async close(){this.redisClient&&(await tAe(this.redisConfig),this.redisClient=null,this.isInitialized=!1,g.info("Redis connection closed"))}async getStats(){if(await this.ensureInitialized(),!this.redisClient)return{totalSessions:0,totalTurns:0};let e=`${this.redisConfig.keyPrefix}*`,t=await LF(this.redisClient,e);g.debug("[RedisConversationMemoryManager] Got session keys with SCAN",{pattern:e,keyCount:t.length});let n=0;for(let o of t){let s=await this.redisClient.get(o),i=bc(s);i?.messages&&(n+=i.messages.length/2)}return{totalSessions:t.length,totalTurns:n}}async clearSession(e,t){if(await this.ensureInitialized(),!this.redisClient)return!1;let n=this.redisClient;return J0.startActiveSpan("neurolink.memory.clear",{kind:jt.CLIENT,attributes:{"session.id":e,...t&&{"user.id":t}}},async o=>{try{let s=xc(this.redisConfig,e,t),i=await Be(n.del(s),fX);return Number(i)>0?(t&&await this.removeUserSession(t,e),o.setAttribute("session.deleted",!0),o.setStatus({code:ke.OK}),g.info("Redis session cleared",{sessionId:e}),!0):(o.setAttribute("session.deleted",!1),o.setStatus({code:ke.OK}),!1)}catch(s){throw o.setStatus({code:ke.ERROR,message:s instanceof Error?s.message:String(s)}),o.recordException(s instanceof Error?s:new Error(String(s))),s}finally{o.end()}})}async clearAllSessions(){if(await this.ensureInitialized(),!this.redisClient)return;let e=`${this.redisConfig.keyPrefix}*`,t=`${this.redisConfig.userSessionsKeyPrefix}*`,n=await LF(this.redisClient,e),o=await LF(this.redisClient,t),s=[...n,...o];if(g.debug("[RedisConversationMemoryManager] Got all keys with SCAN for clearing",{conversationPattern:e,userSessionsPattern:t,conversationKeyCount:n.length,userSessionsKeyCount:o.length,totalKeyCount:s.length}),s.length>0){for(let a=0;a<s.length;a+=100){let l=s.slice(a,a+100);await this.redisClient.del(l),g.debug("[RedisConversationMemoryManager] Cleared batch of sessions and user mappings",{batchIndex:Math.floor(a/100)+1,batchSize:l.length,totalProcessed:a+l.length,totalKeys:s.length})}g.info("All Redis sessions and user session mappings cleared",{clearedCount:s.length,conversationSessions:n.length,userSessionMappings:o.length})}}async ensureInitialized(){g.debug("[RedisConversationMemoryManager] Ensuring initialization"),this.isInitialized?g.debug("[RedisConversationMemoryManager] Already initialized"):(g.debug("[RedisConversationMemoryManager] Not initialized, initializing now"),await this.initialize())}async getUserAllSessionsHistory(e){if(await this.ensureInitialized(),!this.redisClient)return g.warn("[RedisConversationMemoryManager] Redis client not available",{userId:e}),[];let t=[];try{let n=await this.getUserSessions(e);if(n.length===0)return t;for(let o of n)try{let s=await this.getUserSessionMetadata(e,o);s?t.push(s):(g.debug("[RedisConversationMemoryManager] Empty or missing session metadata - removing from user history",{userId:e,sessionId:o}),await this.removeUserSession(e,o))}catch(s){g.error("[RedisConversationMemoryManager] Failed to get session metadata",{userId:e,sessionId:o,error:s instanceof Error?s.message:String(s)})}return t}catch(n){return g.error("[RedisConversationMemoryManager] Failed to get user all sessions metadata",{userId:e,error:n instanceof Error?n.message:String(n),stack:n instanceof Error?n.stack:void 0}),t}}cleanupStalePendingData(){let e=Date.now()-3e5,t=[];for(let[n,o]of this.pendingToolExecutions)o.timestamp<e&&t.push(n);t.length>0&&(g.debug("[RedisConversationMemoryManager] Cleaning up stale pending tool data",{stalePendingKeys:t.length,totalPendingKeys:this.pendingToolExecutions.size}),t.forEach(n=>this.pendingToolExecutions.delete(n)))}async flushPendingToolData(e,t,n){let o=`${t}:${n}`,s=this.pendingToolExecutions.get(o);if(!s){g.debug("[RedisConversationMemoryManager] No pending tool data to flush",{sessionId:t,userId:n,pendingKey:o});return}g.debug("[RedisConversationMemoryManager] Flushing pending tool data",{sessionId:t,userId:n,toolCallsCount:s.toolCalls.length,toolResultsCount:s.toolResults.length});try{let i=new Map;for(let a of s.toolCalls){let l=a.toolCallId??"",u=a.toolName??"";i.set(l,u);let d={id:Ne(),timestamp:a.timestamp?.toISOString()||this.generateTimestamp(),role:"tool_call",content:"",tool:u,args:a.args||a.arguments||a.parameters||{}};e.messages.push(d)}for(let a of s.toolResults){let l=String(a.toolCallId||a.id||"unknown"),u=i.get(l)||"unknown",d;if(typeof a.result=="string")d=a.result;else if(a.result===void 0||a.result===null)d=String(a.result??"null");else try{d=JSON.stringify(a.result,null,2)}catch(T){d=`[Serialization failed: ${T instanceof Error?T.message:String(T)}]`}let{preview:p,truncated:m,originalSize:f}=YIe(d,{maxBytes:this.config?.contextCompaction?.maxToolOutputBytes,maxLines:this.config?.contextCompaction?.maxToolOutputLines}),h={truncated:m,...m&&{toolOutputPreview:p},...m&&{originalSize:f}},y={success:!a.error,error:a.error?String(a.error):void 0},v={id:Ne(),timestamp:a.timestamp?.toISOString()||this.generateTimestamp(),role:"tool_result",content:d,tool:u,result:y,metadata:h};e.messages.push(v)}g.debug("[RedisConversationMemoryManager] Successfully flushed pending tool data",{sessionId:t,userId:n,toolMessagesAdded:s.toolCalls.length+s.toolResults.length,totalMessages:e.messages.length})}finally{this.pendingToolExecutions.delete(o)}}async updateAgenticLoopReport(e,t,n){if(g.debug("[RedisConversationMemoryManager] Updating agentic loop report",{sessionId:e,userId:t,reportId:n.reportId,reportType:n.reportType,reportStatus:n.reportStatus}),await this.ensureInitialized(),!this.redisClient){g.warn("[RedisConversationMemoryManager] Redis client not available for report update",{sessionId:e,userId:t});return}try{let o=xc(this.redisConfig,e,t||void 0),s=await Be(this.redisClient.get(o),5e3);if(!s){g.warn("[RedisConversationMemoryManager] No conversation found for report update",{sessionId:e,userId:t});return}let i=bc(s);if(!i){g.warn("[RedisConversationMemoryManager] Failed to deserialize conversation for report update",{sessionId:e,userId:t});return}i.additionalMetadata||(i.additionalMetadata={}),i.additionalMetadata.agenticLoopReports||(i.additionalMetadata.agenticLoopReports=[]);let a=i.additionalMetadata.agenticLoopReports.findIndex(u=>u.reportId===n.reportId);a>=0?(i.additionalMetadata.agenticLoopReports[a]=n,g.debug("[RedisConversationMemoryManager] Updated existing agentic loop report",{sessionId:e,reportId:n.reportId})):(i.additionalMetadata.agenticLoopReports.push(n),g.debug("[RedisConversationMemoryManager] Added new agentic loop report",{sessionId:e,reportId:n.reportId})),i.updatedAt=new Date().toISOString();let l=fT(i);await Be(this.redisClient.set(o,l),5e3),this.redisConfig.ttl>0&&await Be(this.redisClient.expire(o,this.redisConfig.ttl),5e3),g.info("[RedisConversationMemoryManager] Successfully updated agentic loop report",{sessionId:e,userId:t,reportId:n.reportId,reportStatus:n.reportStatus})}catch(o){throw g.error("[RedisConversationMemoryManager] Failed to update agentic loop report",{sessionId:e,userId:t,reportId:n.reportId,error:o instanceof Error?o.message:String(o)}),new gi("Failed to update agentic loop report","STORAGE_ERROR",{sessionId:e,userId:t,reportId:n.reportId,error:o instanceof Error?o.message:String(o)})}}}});function gX(r,e="memory",t){if(g.debug("[conversationMemoryFactory] Creating conversation memory manager",{storageType:e,config:{enabled:r.enabled,maxSessions:r.maxSessions,enableSummarization:r.enableSummarization,summarizationProvider:r.summarizationProvider,summarizationModel:r.summarizationModel},hasRedisConfig:!!t}),e==="memory"||!e){g.debug("[conversationMemoryFactory] Creating in-memory conversation manager");let o=new hh(r);return g.debug("[conversationMemoryFactory] In-memory conversation manager created successfully",{managerType:o.constructor.name}),o}if(e==="redis"){g.debug("[conversationMemoryFactory] Creating Redis conversation manager",{host:t?.host||"localhost",port:t?.port||6379,keyPrefix:t?.keyPrefix||"neurolink:conversation:",ttl:t?.ttl||86400,hasConnectionOptions:!!t?.connectionOptions});let o=new UF(r,t);return g.debug("[conversationMemoryFactory] Redis conversation manager created successfully",{managerType:o.constructor.name,config:{maxSessions:r.maxSessions,maxTurnsPerSession:r.maxTurnsPerSession}}),o}g.warn(`[conversationMemoryFactory] Unknown storage type: ${e}, falling back to memory storage`);let n=new hh(r);return g.debug("[conversationMemoryFactory] Fallback memory manager created successfully",{managerType:n.constructor.name}),n}function oAe(){let r=process.env.STORAGE_TYPE;if(!r)return g.debug("[conversationMemoryFactory] No storage type configured, using default",{storageType:"memory",fromEnv:!1}),"memory";let e=r.trim().toLowerCase(),t=["memory","redis"];return t.includes(e)?(g.debug("[conversationMemoryFactory] Determined storage type",{storageType:e,fromEnv:!0,envValue:r,normalized:e!==r}),e):(g.warn(`[conversationMemoryFactory] Unrecognized storage type in environment: "${r}", falling back to "memory"`,{providedValue:r,normalizedValue:e,validValues:t,usingDefault:!0}),"memory")}function sAe(){g.debug("[conversationMemoryFactory] Reading Redis configuration from environment",{REDIS_HOST:process.env.REDIS_HOST||"(not set)",REDIS_PORT:process.env.REDIS_PORT||"(not set)",REDIS_PASSWORD:process.env.REDIS_PASSWORD?"******":"(not set)",REDIS_DB:process.env.REDIS_DB||"(not set)",REDIS_KEY_PREFIX:process.env.REDIS_KEY_PREFIX||"(not set)",REDIS_TTL:process.env.REDIS_TTL||"(not set)",REDIS_CONNECT_TIMEOUT:process.env.REDIS_CONNECT_TIMEOUT||"(not set)",REDIS_MAX_RETRIES:process.env.REDIS_MAX_RETRIES||"(not set)",REDIS_RETRY_DELAY:process.env.REDIS_RETRY_DELAY||"(not set)"});let r={host:process.env.REDIS_HOST,port:process.env.REDIS_PORT?Number(process.env.REDIS_PORT):void 0,password:process.env.REDIS_PASSWORD,db:process.env.REDIS_DB?Number(process.env.REDIS_DB):void 0,keyPrefix:process.env.REDIS_KEY_PREFIX,ttl:process.env.REDIS_TTL?Number(process.env.REDIS_TTL):void 0,connectionOptions:{connectTimeout:process.env.REDIS_CONNECT_TIMEOUT?Number(process.env.REDIS_CONNECT_TIMEOUT):void 0,maxRetriesPerRequest:process.env.REDIS_MAX_RETRIES?Number(process.env.REDIS_MAX_RETRIES):void 0,retryDelayOnFailover:process.env.REDIS_RETRY_DELAY?Number(process.env.REDIS_RETRY_DELAY):void 0}};return g.debug("[conversationMemoryFactory] Redis configuration normalized",{host:r.host||"localhost",port:r.port||6379,hasPassword:!!r.password,db:r.db||0,keyPrefix:r.keyPrefix||"neurolink:conversation:",ttl:r.ttl||86400,hasConnectionOptions:!!r.connectionOptions}),r}var iAe=I(async()=>{"use strict";N8();await nAe();te()});var aAe={};ue(aAe,{initializeConversationMemory:()=>u4t});async function u4t(r){if(g.debug("[conversationMemoryInitializer] Initialize conversation memory called",{hasConfig:!!r,hasMemoryConfig:!!r?.conversationMemory,memoryEnabled:r?.conversationMemory?.enabled||!1,storageType:process.env.STORAGE_TYPE||"memory"}),!r?.conversationMemory?.enabled)return g.debug("[conversationMemoryInitializer] Conversation memory not enabled - skipping initialization"),null;try{g.debug("[conversationMemoryInitializer] Applying conversation memory defaults");let e=_Ee(r.conversationMemory);g.debug("[conversationMemoryInitializer] Memory configuration processed",{enabled:e.enabled,maxSessions:e.maxSessions,maxTurnsPerSession:e.maxTurnsPerSession,enableSummarization:e.enableSummarization});let t=!!r.conversationMemory?.redisConfig,n=t?"redis":oAe();if(g.debug("[conversationMemoryInitializer] Storage type determined",{storageType:n,fromConfig:t,fromEnv:!t&&!!process.env.STORAGE_TYPE}),n==="redis"){g.info("[conversationMemoryInitializer] Initializing Redis-based conversation memory manager"),g.debug("[conversationMemoryInitializer] Getting Redis configuration");let o=r.conversationMemory?.redisConfig||sAe(),s=r.conversationMemory?.redisConfig?"SDK input (from Lighthouse)":"environment variables (NeuroLink)";g.debug("[conversationMemoryInitializer] Redis configuration retrieved",{configSource:s,host:o.host||"localhost",port:o.port||6379,hasPassword:!!o.password,db:o.db||0,keyPrefix:o.keyPrefix||"neurolink:conversation:",ttl:o.ttl||86400}),g.debug("[conversationMemoryInitializer] Creating Redis conversation memory manager");let i=gX(e,"redis",o);return g.debug("[conversationMemoryInitializer] Checking Redis manager creation result",{managerType:i?.constructor?.name||"unknown",isRedisType:i?.constructor?.name==="RedisConversationMemoryManager",hasConfig:!!i?.config}),g.info("[conversationMemoryInitializer] Redis conversation memory manager created successfully"),i?.constructor?.name!=="RedisConversationMemoryManager"&&g.warn("[conversationMemoryInitializer] Created manager is not of RedisConversationMemoryManager type",{actualType:i?.constructor?.name}),i}else{g.info("[conversationMemoryInitializer] Initializing in-memory conversation memory manager"),g.debug("[conversationMemoryInitializer] Creating in-memory conversation memory manager");let o=gX(e);return g.debug("[conversationMemoryInitializer] Checking memory manager creation result",{managerType:o?.constructor?.name||"unknown",isInMemoryType:o?.constructor?.name==="ConversationMemoryManager",hasConfig:!!o?.config}),g.info("[conversationMemoryInitializer] In-memory conversation memory manager created successfully",{maxSessions:e.maxSessions,maxTurnsPerSession:e.maxTurnsPerSession,managerType:o?.constructor?.name}),o}}catch(e){throw g.error("[conversationMemoryInitializer] Failed to initialize conversation memory",{error:e instanceof Error?e.message:String(e),errorName:e instanceof Error?e.name:"UnknownError",errorStack:e instanceof Error?e.stack:void 0,storageType:process.env.STORAGE_TYPE||"memory",memoryConfig:{enabled:r?.conversationMemory?.enabled,maxSessions:r?.conversationMemory?.maxSessions,maxTurnsPerSession:r?.conversationMemory?.maxTurnsPerSession},redisConfig:{host:process.env.REDIS_HOST||"(not set)",port:process.env.REDIS_PORT||"(not set)",hasPassword:!!process.env.REDIS_PASSWORD,keyPrefix:process.env.REDIS_KEY_PREFIX||"(not set)"}}),process.env.STORAGE_TYPE==="redis"&&g.error("[conversationMemoryInitializer] Redis configuration error details",{REDIS_HOST:process.env.REDIS_HOST||"(not set)",REDIS_PORT:process.env.REDIS_PORT||"(not set)",REDIS_PASSWORD:process.env.REDIS_PASSWORD?"******":"(not set)",REDIS_DB:process.env.REDIS_DB||"(not set)",REDIS_KEY_PREFIX:process.env.REDIS_KEY_PREFIX||"(not set)",REDIS_TTL:process.env.REDIS_TTL||"(not set)",errorMessage:e instanceof Error?e.message:String(e)}),e}}var cAe=I(async()=>{"use strict";gh();te();await iAe()});var lAe={};ue(lAe,{AuthProviderFactory:()=>ro,createAuthProvider:()=>hX});async function hX(r,e){return ro.createProvider(r,e)}var ro,Z0=I(()=>{"use strict";te();qs();ro=class r{static providers=new Map;static aliasMap=new Map;static registerProvider(e,t,n=[],o){r.providers.set(e,{factory:t,aliases:n,metadata:o});for(let s of n)r.aliasMap.set(s.toLowerCase(),e);g.debug(`Registered auth provider: ${e}`)}static async createProvider(e,t){let n=r.resolveType(e),o=r.providers.get(n);if(!o)throw Ze.create("PROVIDER_NOT_FOUND",`Auth provider not found: ${e}. Available: ${r.getAvailableProviders().join(", ")}`);try{return await o.factory(t)}catch(s){throw Ze.create("CREATION_FAILED",`Failed to create auth provider ${e}: ${s instanceof Error?s.message:String(s)}`,{cause:s instanceof Error?s:void 0})}}static hasProvider(e){return r.providers.has(e)||r.aliasMap.has(e.toLowerCase())}static getAvailableProviders(){return Array.from(r.providers.keys())}static getProviderMetadata(e){let t=r.resolveType(e);return r.providers.get(t)?.metadata}static getAllProviderInfo(){return Array.from(r.providers.entries()).map(([e,t])=>({type:e,aliases:t.aliases,metadata:t.metadata}))}static clearRegistrations(){r.providers.clear(),r.aliasMap.clear()}static resolveType(e){return r.aliasMap.get(e.toLowerCase())||e}}});var wEe={};ue(wEe,{NeuroLink:()=>zh,default:()=>m4t,neurolink:()=>dAe});function d4t(r){let e=r.toLowerCase();return e.includes("not found")||e.includes("404")||e.includes("does not exist")||e.includes("no such")?"not_found":e.includes("permission")||e.includes("forbidden")||e.includes("403")||e.includes("unauthorized")||e.includes("401")||e.includes("access denied")?"permission_denied":e.includes("timeout")||e.includes("timed out")||e.includes("deadline exceeded")?"timeout":e.includes("rate limit")||e.includes("429")||e.includes("too many requests")||e.includes("throttl")?"rate_limited":e.includes("invalid")||e.includes("validation")||e.includes("bad request")||e.includes("400")?"validation_error":"unknown"}function p4t(r){switch(r){case"not_found":return"validation";case"permission_denied":return"permission";case"timeout":return"timeout";case"rate_limited":return"resource";case"validation_error":return"validation";case"unknown":return"execution"}}function uAe(r){if(r instanceof pc||r instanceof nn||r instanceof mC)return!0;if(r&&typeof r=="object"){let e=r,t=typeof e.status=="number"?e.status:typeof e.statusCode=="number"?e.statusCode:void 0;if(t&&Hke.includes(t))return!0}if(r instanceof Error){let e=r.message;if(e.includes("NOT_FOUND")||e.includes("Model Not Found")||e.includes("model not found")||e.includes("PERMISSION_DENIED")||e.includes("UNAUTHENTICATED"))return!0}return!1}var yX,zh,dAe,m4t,H1=I(async()=>{"use strict";Er();ZE();jo();cC();Twe();lEe();kEe();REe();MEe();OEe();NEe();Su();i6();N8();Ya();Tw();BEe();GEe();VEe();$8();j8();sU();T9();_9();Q0e();_b();HU();nke();ske();dM();Ev();Jd();_ke();Eke();N4();su();Pv();K1();Gc();gh();wt();Oke();te();G1();Nke();Vke();ep();Wke();Md();U9();xO();Hg();j9();iF();try{process.env.DOTENV_CONFIG_QUIET=process.env.DOTENV_CONFIG_QUIET??"true";let{config:r}=await Promise.resolve().then(()=>Ii(ORe(),1));r({quiet:!0})}catch{}yX=new Zg,zh=class{mcpInitialized=!1;mcpSkipped=!1;mcpInitPromise=null;emitter=new dr;_taskManager;_taskManagerConfig;toolRegistry;autoDiscoveredServerInfos=[];externalServerManager;toolCache=null;toolCacheDuration;modelAliasConfig;lastCompactionMessageCount=new Map;getCompactionSessionId(e){return e.context?.sessionId||"__default__"}mcpToolResultCache;mcpToolRouter;mcpToolBatcher;mcpEnhancedDiscovery;mcpToolMiddlewares=[];_disableToolCacheForCurrentRequest=!1;mcpEnhancementsConfig;toolCircuitBreakers=new Map;toolExecutionMetrics=new Map;currentStreamToolExecutions=[];toolExecutionHistory=[];activeToolExecutions=new Map;emitToolEndEvent(e,t,n,o,s){this.emitter.emit("tool:end",eh(e,{responseTime:Date.now()-t,success:n,timestamp:Date.now(),result:o,error:s?s.message:void 0}))}conversationMemory;conversationMemoryNeedsInit=!1;conversationMemoryConfig;enableOrchestration;authProvider;pendingAuthConfig;authInitPromise;hitlManager;_sessionCostUsd=0;fileRegistry;cachedFileTools=null;memoryInstance;memorySDKConfig;async setLangfuseContextFromOptions(e,t){if(e.context&&typeof e.context=="object"&&e.context!==null){let n=!1;try{let o=e.context;if(o.userId||o.sessionId||o.conversationId||o.requestId||o.traceName||o.metadata){let s;if(o.metadata&&typeof o.metadata=="object"){let i=o.metadata,a={};for(let[l,u]of Object.entries(i))(typeof u=="string"||typeof u=="number"||typeof u=="boolean")&&(a[l]=u);Object.keys(a).length>0&&(s=a)}return await new Promise((i,a)=>{Yg({userId:typeof o.userId=="string"?o.userId:null,sessionId:typeof o.sessionId=="string"?o.sessionId:null,conversationId:typeof o.conversationId=="string"?o.conversationId:null,requestId:typeof o.requestId=="string"?o.requestId:null,traceName:typeof o.traceName=="string"?o.traceName:null,metadata:o.metadata&&typeof o.metadata=="object"?o.metadata:null,...s!==void 0&&{customAttributes:s}},async()=>{try{n=!0;let l=await t();i(l)}catch(l){a(l)}})})}}catch(o){if(n)throw o;g.warn("Failed to set Langfuse context from options",{error:o instanceof Error?o.message:String(o)})}}return await t()}createMetricsTraceContext(){return{traceId:crypto.randomUUID().replace(/-/g,""),parentSpanId:crypto.randomUUID().replace(/-/g,"").substring(0,16)}}enforceSessionBudget(e){if(!(e===void 0||e<=0||this._sessionCostUsd<e))throw new He({code:"SESSION_BUDGET_EXCEEDED",message:`Session budget exceeded: spent $${this._sessionCostUsd.toFixed(4)} of $${e.toFixed(4)} limit`,category:"validation",severity:"high",retriable:!1,context:{spent:this._sessionCostUsd,limit:e}})}assertInputText(e,t){if(!e||typeof e!="string")throw new Error(t)}async applyAuthenticatedRequestContext(e){if(e.auth?.token){let{AuthError:n}=await Promise.resolve().then(()=>(qs(),W0e));if(await this.ensureAuthProvider(),!this.authProvider)throw n.create("PROVIDER_ERROR","No auth provider configured. Set auth in constructor or via setAuthProvider() before using auth: { token }.");let o;try{o=await Be(this.authProvider.authenticateToken(e.auth.token),5e3,n.create("PROVIDER_ERROR","Auth token validation timed out after 5000ms"))}catch(s){throw s instanceof Error&&"feature"in s&&s.feature==="Auth"?s:n.create("PROVIDER_ERROR",`Auth token validation failed: ${s instanceof Error?s.message:String(s)}`)}if(!o.valid)throw n.create("INVALID_TOKEN",o.error||"Token validation failed");if(!o.user)throw n.create("INVALID_TOKEN","Token validated but no user identity returned");if(!o.user.id)throw n.create("INVALID_TOKEN","Token validated but user identity missing required 'id' field");e.context={...e.context||{},userId:o.user.id,userEmail:o.user.email,userRoles:o.user.roles}}if(!e.requestContext)return;let t=e.auth?.token&&this.authProvider?{userId:e.context?.userId,userEmail:e.context?.userEmail,userRoles:e.context?.userRoles}:{};e.context={...e.context||{},...e.requestContext,...t}}applyGenerateLifecycleMiddleware(e){!e.onFinish&&!e.onError||(e.middleware={...e.middleware,middlewareConfig:{...e.middleware?.middlewareConfig,lifecycle:{...e.middleware?.middlewareConfig?.lifecycle,enabled:!0,config:{...e.middleware?.middlewareConfig?.lifecycle?.config,...e.onFinish!==void 0?{onFinish:e.onFinish}:{},...e.onError!==void 0?{onError:e.onError}:{}}}}})}applyStreamLifecycleMiddleware(e){!e.onFinish&&!e.onError&&!e.onChunk||(e.middleware={...e.middleware,middlewareConfig:{...e.middleware?.middlewareConfig,lifecycle:{...e.middleware?.middlewareConfig?.lifecycle,enabled:!0,config:{...e.middleware?.middlewareConfig?.lifecycle?.config,...e.onFinish!==void 0?{onFinish:e.onFinish}:{},...e.onError!==void 0?{onError:e.onError}:{},...e.onChunk!==void 0?{onChunk:e.onChunk}:{}}}}})}initializeMemoryConfig(){let e=this.conversationMemoryConfig?.conversationMemory?.memory;return e?.enabled?(this.memorySDKConfig=e,!0):!1}ensureMemoryReady(){return this.memoryInstance!==void 0?this.memoryInstance:this.initializeMemoryConfig()?this.memorySDKConfig?(this.memoryInstance=rke(this.memorySDKConfig),this.memoryInstance):(this.memoryInstance=null,null):(this.memoryInstance=null,null)}toolExecutionContext;observabilityConfig;metricsAggregator=new tg;get _metricsTraceContext(){return yX.getStore()??null}constructor(e){this.toolRegistry=e?.toolRegistry||new Mh,this.fileRegistry=new Y1,this.observabilityConfig=e?.observability,this.enableOrchestration=e?.enableOrchestration??!1,e?.modelAliasConfig&&(this.modelAliasConfig=e.modelAliasConfig),g.setEventEmitter(this.emitter);let t=process.env.NEUROLINK_TOOL_CACHE_DURATION;this.toolCacheDuration=t?parseInt(t,10):2e4;let n=Date.now(),o=process.hrtime.bigint(),s=`neurolink-constructor-${Date.now()}-${Math.random().toString(36).substr(2,9)}`;this.initializeProviderRegistry(s,n,o),this.initializeConversationMemory(e,s,n,o),this.initializeExternalServerManager(s,n,o),this.initializeHITL(e,s,n,o),this.initializeMCPEnhancements(e),this.registerFileTools(),this.registerMemoryRetrievalTools(),this.initializeLangfuse(s,n,o),this.initializeMetricsListeners(),this.logConstructorComplete(s,n,o),e?.auth&&(this.pendingAuthConfig=e.auth),this._taskManagerConfig=e?.tasks,this._taskManagerConfig&&(this._taskManager=new M0(this,this._taskManagerConfig),this._taskManager.setEmitter(this.emitter),this.registerTaskTools(this._taskManager))}get tasks(){return this._taskManager||(this._taskManager=new M0(this,this._taskManagerConfig),this._taskManager.setEmitter(this.emitter),this.registerTaskTools(this._taskManager)),this._taskManager}initializeProviderRegistry(e,t,n){let o=process.hrtime.bigint();g.debug("[NeuroLink] \u{1F3D7}\uFE0F LOG_POINT_C002_PROVIDER_REGISTRY_SETUP_START",{logPoint:"C002_PROVIDER_REGISTRY_SETUP_START",constructorId:e,timestamp:new Date().toISOString(),elapsedMs:Date.now()-t,elapsedNs:(process.hrtime.bigint()-n).toString(),registrySetupStartTimeNs:o.toString(),message:"Starting ProviderRegistry configuration for security"}),Tl.setOptions({enableManualMCP:!1})}initializeConversationMemory(e,t,n,o){if(e?.conversationMemory?.enabled){let s=process.hrtime.bigint();this.conversationMemoryConfig=e,this.conversationMemoryNeedsInit=!0;let a=process.hrtime.bigint()-s;g.debug("[NeuroLink] \u2705 LOG_POINT_C006_MEMORY_INIT_FLAG_SET_SUCCESS",{logPoint:"C006_MEMORY_INIT_FLAG_SET_SUCCESS",constructorId:t,timestamp:new Date().toISOString(),elapsedMs:Date.now()-n,elapsedNs:(process.hrtime.bigint()-o).toString(),memoryInitDurationNs:a.toString(),memoryInitDurationMs:Number(a)/Vl,message:"Conversation memory initialization flag set successfully for lazy loading"})}else g.debug("[NeuroLink] \u{1F6AB} LOG_POINT_C008_MEMORY_DISABLED",{logPoint:"C008_MEMORY_DISABLED",constructorId:t,timestamp:new Date().toISOString(),elapsedMs:Date.now()-n,elapsedNs:(process.hrtime.bigint()-o).toString(),hasConfig:!!e,hasMemoryConfig:!!e?.conversationMemory,memoryEnabled:e?.conversationMemory?.enabled||!1,reason:e?e.conversationMemory?e.conversationMemory.enabled?"UNKNOWN":"MEMORY_DISABLED":"NO_MEMORY_CONFIG":"NO_CONFIG",message:"Conversation memory not enabled - skipping initialization"})}initializeHITL(e,t,n,o){if(e?.hitl?.enabled){let s=process.hrtime.bigint();g.debug("[NeuroLink] \u{1F6E1}\uFE0F LOG_POINT_C015_HITL_INIT_START",{logPoint:"C015_HITL_INIT_START",constructorId:t,timestamp:new Date().toISOString(),elapsedMs:Date.now()-n,elapsedNs:(process.hrtime.bigint()-o).toString(),hitlInitStartTimeNs:s.toString(),hitlConfig:{enabled:e.hitl.enabled,dangerousActions:e.hitl.dangerousActions||[],timeout:e.hitl.timeout||3e4,allowArgumentModification:e.hitl.allowArgumentModification??!0,auditLogging:e.hitl.auditLogging??!1},message:"Starting HITL (Human-in-the-Loop) initialization"});try{this.hitlManager=new eU(e.hitl),this.toolRegistry.setHITLManager(this.hitlManager),this.externalServerManager.setHITLManager(this.hitlManager),this.setupHITLEventForwarding();let a=process.hrtime.bigint()-s;g.debug("[NeuroLink] \u2705 LOG_POINT_C016_HITL_INIT_SUCCESS",{logPoint:"C016_HITL_INIT_SUCCESS",constructorId:t,timestamp:new Date().toISOString(),elapsedMs:Date.now()-n,elapsedNs:(process.hrtime.bigint()-o).toString(),hitlInitDurationNs:a.toString(),hitlInitDurationMs:Number(a)/Vl,hasHitlManager:!!this.hitlManager,message:"HITL (Human-in-the-Loop) initialized successfully"}),g.info("[NeuroLink] HITL safety features enabled",{dangerousActions:e.hitl.dangerousActions?.length||0,timeout:e.hitl.timeout||3e4,allowArgumentModification:e.hitl.allowArgumentModification??!0,auditLogging:e.hitl.auditLogging??!1})}catch(i){let l=process.hrtime.bigint()-s;throw g.error("[NeuroLink] \u274C LOG_POINT_C017_HITL_INIT_ERROR",{logPoint:"C017_HITL_INIT_ERROR",constructorId:t,timestamp:new Date().toISOString(),elapsedMs:Date.now()-n,elapsedNs:(process.hrtime.bigint()-o).toString(),hitlInitDurationNs:l.toString(),hitlInitDurationMs:Number(l)/Vl,error:i instanceof Error?i.message:String(i),errorName:i instanceof Error?i.name:"UnknownError",errorStack:i instanceof Error?i.stack:void 0,message:"HITL (Human-in-the-Loop) initialization failed"}),i}}else g.debug("[NeuroLink] \u{1F6AB} LOG_POINT_C018_HITL_DISABLED",{logPoint:"C018_HITL_DISABLED",constructorId:t,timestamp:new Date().toISOString(),elapsedMs:Date.now()-n,elapsedNs:(process.hrtime.bigint()-o).toString(),hasConfig:!!e,hasHitlConfig:!!e?.hitl,hitlEnabled:e?.hitl?.enabled||!1,reason:e?e.hitl?e.hitl.enabled?"UNKNOWN":"HITL_DISABLED":"NO_HITL_CONFIG":"NO_CONFIG",message:"HITL (Human-in-the-Loop) not enabled - skipping initialization"})}initializeMCPEnhancements(e){let t=e?.mcp;this.mcpEnhancementsConfig=t,t?.cache?.enabled&&(this.mcpToolResultCache=new Fm({ttl:t.cache.ttl??3e5,maxSize:t.cache.maxSize??500,strategy:t.cache.strategy??"lru"}),g.debug("[NeuroLink] MCP tool result cache initialized",{ttl:t.cache.ttl??3e5,maxSize:t.cache.maxSize??500,strategy:t.cache.strategy??"lru"})),t?.batcher?.enabled&&(this.mcpToolBatcher=new Um({maxBatchSize:t.batcher.maxBatchSize??10,maxWaitMs:t.batcher.maxWaitMs??100}),this.mcpToolBatcher.setToolExecutor(async(n,o)=>this.executeToolInternal(n,o,{timeout:ju.EXECUTION_DEFAULT_MS,maxRetries:jn.DEFAULT,retryDelayMs:Zr.BASE_MS})),g.debug("[NeuroLink] MCP tool call batcher initialized")),t?.discovery?.enabled!==!1&&(this.mcpEnhancedDiscovery=new bh,g.debug("[NeuroLink] Enhanced tool discovery initialized")),t?.middleware?.length&&(this.mcpToolMiddlewares=[...t.middleware],g.debug("[NeuroLink] MCP tool middlewares registered",{count:this.mcpToolMiddlewares.length}))}registerFileTools(){let e=L8(this.fileRegistry),t=Object.entries(e).map(async([n,o])=>{let s=`direct.${n}`,i={name:n,description:o.description||`File tool: ${n}`,inputSchema:{},serverId:"direct",category:"built-in"};await this.toolRegistry.registerTool(s,i,{execute:async a=>{try{return{success:!0,data:await o.execute(a,{toolCallId:"file-tool",messages:[]}),metadata:{toolName:n,serverId:"direct",executionTime:0}}}catch(l){return{success:!1,error:l instanceof Error?l.message:String(l),metadata:{toolName:n,serverId:"direct",executionTime:0}}}},description:o.description,inputSchema:{}})});Promise.all(t).then(()=>{g.debug(`[NeuroLink] Registered ${Object.keys(e).length} file reference tools`)})}registerTaskTools(e){let t=wke(e);for(let[n,o]of Object.entries(t)){let s=`direct.${n}`,i={name:n,description:o.description||`Task tool: ${n}`,inputSchema:{},serverId:"direct",category:"built-in"};this.toolRegistry.registerTool(s,i,{execute:async a=>{try{return{success:!0,data:await o.execute(a,{toolCallId:"task-tool",messages:[]}),metadata:{toolName:n,serverId:"direct",executionTime:0}}}catch(l){return{success:!1,error:l instanceof Error?l.message:String(l),metadata:{toolName:n,serverId:"direct",executionTime:0}}}},description:o.description,inputSchema:{}})}g.debug(`[NeuroLink] Registered ${Object.keys(t).length} task tools`)}registerMemoryRetrievalTools(){let e=this.conversationMemoryConfig?.conversationMemory,t=!!e?.redisConfig||e&&"redis"in e&&!!e.redis||process.env.STORAGE_TYPE==="redis";if(!e?.enabled||!t){g.debug("[NeuroLink] Skipping memory retrieval tools \u2014 requires Redis conversation memory");return}let o=Object.entries({retrieve_context:{description:"Retrieve messages from conversation memory. Use this to access full tool outputs when a result was truncated, review previous assistant responses, or search through conversation history.",execute:async s=>{let i=this.conversationMemory;if(!i||!("getSessionRaw"in i))return{success:!1,error:"Memory retrieval not available \u2014 Redis memory manager not initialized",metadata:{toolName:"retrieve_context",serverId:"direct",executionTime:0}};let l=await oke(i).retrieve_context.execute(s,{toolCallId:"memory-retrieval",messages:[]}),u=l&&typeof l=="object"&&"error"in l&&!("messages"in l),d=u?l.error:void 0;return{success:!u,data:l,...d?{error:d}:{},metadata:{toolName:"retrieve_context",serverId:"direct",executionTime:0}}}}}).map(async([s,i])=>{let a=`direct.${s}`,l={name:s,description:i.description,inputSchema:{},serverId:"direct",category:"built-in"};await this.toolRegistry.registerTool(a,l,{execute:async u=>{try{return await i.execute(u)}catch(d){return{success:!1,error:d instanceof Error?d.message:String(d),metadata:{toolName:s,serverId:"direct",executionTime:0}}}},description:i.description,inputSchema:{}})});Promise.all(o).then(()=>{g.info("[NeuroLink] Memory retrieval tools registered")})}formatMemoryContext(e,t){return`Context from previous conversations:
2027
+ ${e}`,timestamp:new Date().toISOString(),metadata:{isSummary:!0,summarizesFrom:t,summarizesTo:n}}}async getSessionMessages(e,t){if(await this.ensureInitialized(),!this.redisClient)return[];try{let n=xc(this.redisConfig,e,t),o=await this.redisClient.get(n);return bc(o||null)?.messages??[]}catch(n){return g.error("[RedisConversationMemoryManager] Failed to get session messages",{sessionId:e,userId:t,error:n instanceof Error?n.message:String(n)}),[]}}async setSessionMessages(e,t,n){if(await this.ensureInitialized(),!this.redisClient)throw new gi("Redis client not initialized","STORAGE_ERROR",{sessionId:e});try{let o=xc(this.redisConfig,e,n),s=await this.redisClient.get(o),i=bc(s||null);if(!i)throw new gi(`Session ${e} not found`,"STORAGE_ERROR",{sessionId:e});i.messages=t,i.updatedAt=new Date().toISOString(),i.summarizedUpToMessageId=void 0,i.summarizedMessage=void 0,i.lastTokenCount=void 0,i.lastCountedAt=void 0;let a=fT(i);await this.redisClient.set(o,a),this.redisConfig.ttl>0&&await this.redisClient.expire(o,this.redisConfig.ttl),g.debug("[RedisConversationMemoryManager] Session messages replaced",{sessionId:e,userId:n,messageCount:t.length})}catch(o){throw o instanceof gi?o:new gi(`Failed to set session messages for session ${e}`,"STORAGE_ERROR",{sessionId:e,error:o instanceof Error?o.message:String(o)})}}async close(){this.redisClient&&(await tAe(this.redisConfig),this.redisClient=null,this.isInitialized=!1,g.info("Redis connection closed"))}async getStats(){if(await this.ensureInitialized(),!this.redisClient)return{totalSessions:0,totalTurns:0};let e=`${this.redisConfig.keyPrefix}*`,t=await LF(this.redisClient,e);g.debug("[RedisConversationMemoryManager] Got session keys with SCAN",{pattern:e,keyCount:t.length});let n=0;for(let o of t){let s=await this.redisClient.get(o),i=bc(s);i?.messages&&(n+=i.messages.length/2)}return{totalSessions:t.length,totalTurns:n}}async clearSession(e,t){if(await this.ensureInitialized(),!this.redisClient)return!1;let n=this.redisClient;return J0.startActiveSpan("neurolink.memory.clear",{kind:jt.CLIENT,attributes:{"session.id":e,...t&&{"user.id":t}}},async o=>{try{let s=xc(this.redisConfig,e,t),i=await Be(n.del(s),fX);return Number(i)>0?(t&&await this.removeUserSession(t,e),o.setAttribute("session.deleted",!0),o.setStatus({code:ke.OK}),g.info("Redis session cleared",{sessionId:e}),!0):(o.setAttribute("session.deleted",!1),o.setStatus({code:ke.OK}),!1)}catch(s){throw o.setStatus({code:ke.ERROR,message:s instanceof Error?s.message:String(s)}),o.recordException(s instanceof Error?s:new Error(String(s))),s}finally{o.end()}})}async clearAllSessions(){if(await this.ensureInitialized(),!this.redisClient)return;let e=`${this.redisConfig.keyPrefix}*`,t=`${this.redisConfig.userSessionsKeyPrefix}*`,n=await LF(this.redisClient,e),o=await LF(this.redisClient,t),s=[...n,...o];if(g.debug("[RedisConversationMemoryManager] Got all keys with SCAN for clearing",{conversationPattern:e,userSessionsPattern:t,conversationKeyCount:n.length,userSessionsKeyCount:o.length,totalKeyCount:s.length}),s.length>0){for(let a=0;a<s.length;a+=100){let l=s.slice(a,a+100);await this.redisClient.del(l),g.debug("[RedisConversationMemoryManager] Cleared batch of sessions and user mappings",{batchIndex:Math.floor(a/100)+1,batchSize:l.length,totalProcessed:a+l.length,totalKeys:s.length})}g.info("All Redis sessions and user session mappings cleared",{clearedCount:s.length,conversationSessions:n.length,userSessionMappings:o.length})}}async ensureInitialized(){g.debug("[RedisConversationMemoryManager] Ensuring initialization"),this.isInitialized?g.debug("[RedisConversationMemoryManager] Already initialized"):(g.debug("[RedisConversationMemoryManager] Not initialized, initializing now"),await this.initialize())}async getUserAllSessionsHistory(e){if(await this.ensureInitialized(),!this.redisClient)return g.warn("[RedisConversationMemoryManager] Redis client not available",{userId:e}),[];let t=[];try{let n=await this.getUserSessions(e);if(n.length===0)return t;for(let o of n)try{let s=await this.getUserSessionMetadata(e,o);s?t.push(s):(g.debug("[RedisConversationMemoryManager] Empty or missing session metadata - removing from user history",{userId:e,sessionId:o}),await this.removeUserSession(e,o))}catch(s){g.error("[RedisConversationMemoryManager] Failed to get session metadata",{userId:e,sessionId:o,error:s instanceof Error?s.message:String(s)})}return t}catch(n){return g.error("[RedisConversationMemoryManager] Failed to get user all sessions metadata",{userId:e,error:n instanceof Error?n.message:String(n),stack:n instanceof Error?n.stack:void 0}),t}}cleanupStalePendingData(){let e=Date.now()-3e5,t=[];for(let[n,o]of this.pendingToolExecutions)o.timestamp<e&&t.push(n);t.length>0&&(g.debug("[RedisConversationMemoryManager] Cleaning up stale pending tool data",{stalePendingKeys:t.length,totalPendingKeys:this.pendingToolExecutions.size}),t.forEach(n=>this.pendingToolExecutions.delete(n)))}async flushPendingToolData(e,t,n){let o=`${t}:${n}`,s=this.pendingToolExecutions.get(o);if(!s){g.debug("[RedisConversationMemoryManager] No pending tool data to flush",{sessionId:t,userId:n,pendingKey:o});return}g.debug("[RedisConversationMemoryManager] Flushing pending tool data",{sessionId:t,userId:n,toolCallsCount:s.toolCalls.length,toolResultsCount:s.toolResults.length});try{let i=new Map;for(let a of s.toolCalls){let l=a.toolCallId??"",u=a.toolName??"";i.set(l,u);let d={id:Ne(),timestamp:a.timestamp?.toISOString()||this.generateTimestamp(),role:"tool_call",content:"",tool:u,args:a.args||a.arguments||a.parameters||{}};e.messages.push(d)}for(let a of s.toolResults){let l=String(a.toolCallId||a.id||"unknown"),u=i.get(l)||"unknown",d;if(typeof a.result=="string")d=a.result;else if(a.result===void 0||a.result===null)d=String(a.result??"null");else try{d=JSON.stringify(a.result,null,2)}catch(T){d=`[Serialization failed: ${T instanceof Error?T.message:String(T)}]`}let{preview:p,truncated:m,originalSize:f}=YIe(d,{maxBytes:this.config?.contextCompaction?.maxToolOutputBytes,maxLines:this.config?.contextCompaction?.maxToolOutputLines}),h={truncated:m,...m&&{toolOutputPreview:p},...m&&{originalSize:f}},y={success:!a.error,error:a.error?String(a.error):void 0},v={id:Ne(),timestamp:a.timestamp?.toISOString()||this.generateTimestamp(),role:"tool_result",content:d,tool:u,result:y,metadata:h};e.messages.push(v)}g.debug("[RedisConversationMemoryManager] Successfully flushed pending tool data",{sessionId:t,userId:n,toolMessagesAdded:s.toolCalls.length+s.toolResults.length,totalMessages:e.messages.length})}finally{this.pendingToolExecutions.delete(o)}}async updateAgenticLoopReport(e,t,n){if(g.debug("[RedisConversationMemoryManager] Updating agentic loop report",{sessionId:e,userId:t,reportId:n.reportId,reportType:n.reportType,reportStatus:n.reportStatus}),await this.ensureInitialized(),!this.redisClient){g.warn("[RedisConversationMemoryManager] Redis client not available for report update",{sessionId:e,userId:t});return}try{let o=xc(this.redisConfig,e,t||void 0),s=await Be(this.redisClient.get(o),5e3);if(!s){g.warn("[RedisConversationMemoryManager] No conversation found for report update",{sessionId:e,userId:t});return}let i=bc(s);if(!i){g.warn("[RedisConversationMemoryManager] Failed to deserialize conversation for report update",{sessionId:e,userId:t});return}i.additionalMetadata||(i.additionalMetadata={}),i.additionalMetadata.agenticLoopReports||(i.additionalMetadata.agenticLoopReports=[]);let a=i.additionalMetadata.agenticLoopReports.findIndex(u=>u.reportId===n.reportId);a>=0?(i.additionalMetadata.agenticLoopReports[a]=n,g.debug("[RedisConversationMemoryManager] Updated existing agentic loop report",{sessionId:e,reportId:n.reportId})):(i.additionalMetadata.agenticLoopReports.push(n),g.debug("[RedisConversationMemoryManager] Added new agentic loop report",{sessionId:e,reportId:n.reportId})),i.updatedAt=new Date().toISOString();let l=fT(i);await Be(this.redisClient.set(o,l),5e3),this.redisConfig.ttl>0&&await Be(this.redisClient.expire(o,this.redisConfig.ttl),5e3),g.info("[RedisConversationMemoryManager] Successfully updated agentic loop report",{sessionId:e,userId:t,reportId:n.reportId,reportStatus:n.reportStatus})}catch(o){throw g.error("[RedisConversationMemoryManager] Failed to update agentic loop report",{sessionId:e,userId:t,reportId:n.reportId,error:o instanceof Error?o.message:String(o)}),new gi("Failed to update agentic loop report","STORAGE_ERROR",{sessionId:e,userId:t,reportId:n.reportId,error:o instanceof Error?o.message:String(o)})}}}});function gX(r,e="memory",t){if(g.debug("[conversationMemoryFactory] Creating conversation memory manager",{storageType:e,config:{enabled:r.enabled,maxSessions:r.maxSessions,enableSummarization:r.enableSummarization,summarizationProvider:r.summarizationProvider,summarizationModel:r.summarizationModel},hasRedisConfig:!!t}),e==="memory"||!e){g.debug("[conversationMemoryFactory] Creating in-memory conversation manager");let o=new hh(r);return g.debug("[conversationMemoryFactory] In-memory conversation manager created successfully",{managerType:o.constructor.name}),o}if(e==="redis"){g.debug("[conversationMemoryFactory] Creating Redis conversation manager",{host:t?.host||"localhost",port:t?.port||6379,keyPrefix:t?.keyPrefix||"neurolink:conversation:",ttl:t?.ttl||86400,hasConnectionOptions:!!t?.connectionOptions});let o=new UF(r,t);return g.debug("[conversationMemoryFactory] Redis conversation manager created successfully",{managerType:o.constructor.name,config:{maxSessions:r.maxSessions,maxTurnsPerSession:r.maxTurnsPerSession}}),o}g.warn(`[conversationMemoryFactory] Unknown storage type: ${e}, falling back to memory storage`);let n=new hh(r);return g.debug("[conversationMemoryFactory] Fallback memory manager created successfully",{managerType:n.constructor.name}),n}function oAe(){let r=process.env.STORAGE_TYPE;if(!r)return g.debug("[conversationMemoryFactory] No storage type configured, using default",{storageType:"memory",fromEnv:!1}),"memory";let e=r.trim().toLowerCase(),t=["memory","redis"];return t.includes(e)?(g.debug("[conversationMemoryFactory] Determined storage type",{storageType:e,fromEnv:!0,envValue:r,normalized:e!==r}),e):(g.warn(`[conversationMemoryFactory] Unrecognized storage type in environment: "${r}", falling back to "memory"`,{providedValue:r,normalizedValue:e,validValues:t,usingDefault:!0}),"memory")}function sAe(){g.debug("[conversationMemoryFactory] Reading Redis configuration from environment",{REDIS_URL:process.env.REDIS_URL?"******":"(not set)",REDIS_HOST:process.env.REDIS_HOST||"(not set)",REDIS_PORT:process.env.REDIS_PORT||"(not set)",REDIS_PASSWORD:process.env.REDIS_PASSWORD?"******":"(not set)",REDIS_DB:process.env.REDIS_DB||"(not set)",REDIS_KEY_PREFIX:process.env.REDIS_KEY_PREFIX||"(not set)",REDIS_TTL:process.env.REDIS_TTL||"(not set)",REDIS_CONNECT_TIMEOUT:process.env.REDIS_CONNECT_TIMEOUT||"(not set)",REDIS_MAX_RETRIES:process.env.REDIS_MAX_RETRIES||"(not set)",REDIS_RETRY_DELAY:process.env.REDIS_RETRY_DELAY||"(not set)"});let r={host:process.env.REDIS_HOST,port:process.env.REDIS_PORT?Number(process.env.REDIS_PORT):void 0,password:process.env.REDIS_PASSWORD,db:process.env.REDIS_DB?Number(process.env.REDIS_DB):void 0,keyPrefix:process.env.REDIS_KEY_PREFIX,ttl:process.env.REDIS_TTL?Number(process.env.REDIS_TTL):void 0,connectionOptions:{connectTimeout:process.env.REDIS_CONNECT_TIMEOUT?Number(process.env.REDIS_CONNECT_TIMEOUT):void 0,maxRetriesPerRequest:process.env.REDIS_MAX_RETRIES?Number(process.env.REDIS_MAX_RETRIES):void 0,retryDelayOnFailover:process.env.REDIS_RETRY_DELAY?Number(process.env.REDIS_RETRY_DELAY):void 0}};return process.env.REDIS_URL&&(g.debug("[conversationMemoryFactory] Using REDIS_URL for connection"),r.url=process.env.REDIS_URL),g.debug("[conversationMemoryFactory] Redis configuration normalized",{host:r.host||"localhost",port:r.port||6379,hasPassword:!!r.password,db:r.db||0,keyPrefix:r.keyPrefix||"neurolink:conversation:",ttl:r.ttl||86400,hasConnectionOptions:!!r.connectionOptions}),r}var iAe=I(async()=>{"use strict";te();N8();await nAe()});var aAe={};ue(aAe,{initializeConversationMemory:()=>u4t});async function u4t(r){if(g.debug("[conversationMemoryInitializer] Initialize conversation memory called",{hasConfig:!!r,hasMemoryConfig:!!r?.conversationMemory,memoryEnabled:r?.conversationMemory?.enabled||!1,storageType:process.env.STORAGE_TYPE||"memory"}),!r?.conversationMemory?.enabled)return g.debug("[conversationMemoryInitializer] Conversation memory not enabled - skipping initialization"),null;try{g.debug("[conversationMemoryInitializer] Applying conversation memory defaults");let e=_Ee(r.conversationMemory);g.debug("[conversationMemoryInitializer] Memory configuration processed",{enabled:e.enabled,maxSessions:e.maxSessions,maxTurnsPerSession:e.maxTurnsPerSession,enableSummarization:e.enableSummarization});let t=!!r.conversationMemory?.redisConfig,n=t?"redis":oAe();if(g.debug("[conversationMemoryInitializer] Storage type determined",{storageType:n,fromConfig:t,fromEnv:!t&&!!process.env.STORAGE_TYPE}),n==="redis"){g.info("[conversationMemoryInitializer] Initializing Redis-based conversation memory manager"),g.debug("[conversationMemoryInitializer] Getting Redis configuration");let o=r.conversationMemory?.redisConfig||sAe(),s=r.conversationMemory?.redisConfig?"SDK input (from Lighthouse)":"environment variables (NeuroLink)";g.debug("[conversationMemoryInitializer] Redis configuration retrieved",{configSource:s,host:o.host||"localhost",port:o.port||6379,hasPassword:!!o.password,db:o.db||0,keyPrefix:o.keyPrefix||"neurolink:conversation:",ttl:o.ttl||86400}),g.debug("[conversationMemoryInitializer] Creating Redis conversation memory manager");let i=gX(e,"redis",o);return g.debug("[conversationMemoryInitializer] Checking Redis manager creation result",{managerType:i?.constructor?.name||"unknown",isRedisType:i?.constructor?.name==="RedisConversationMemoryManager",hasConfig:!!i?.config}),g.info("[conversationMemoryInitializer] Redis conversation memory manager created successfully"),i?.constructor?.name!=="RedisConversationMemoryManager"&&g.warn("[conversationMemoryInitializer] Created manager is not of RedisConversationMemoryManager type",{actualType:i?.constructor?.name}),i}else{g.info("[conversationMemoryInitializer] Initializing in-memory conversation memory manager"),g.debug("[conversationMemoryInitializer] Creating in-memory conversation memory manager");let o=gX(e);return g.debug("[conversationMemoryInitializer] Checking memory manager creation result",{managerType:o?.constructor?.name||"unknown",isInMemoryType:o?.constructor?.name==="ConversationMemoryManager",hasConfig:!!o?.config}),g.info("[conversationMemoryInitializer] In-memory conversation memory manager created successfully",{maxSessions:e.maxSessions,maxTurnsPerSession:e.maxTurnsPerSession,managerType:o?.constructor?.name}),o}}catch(e){throw g.error("[conversationMemoryInitializer] Failed to initialize conversation memory",{error:e instanceof Error?e.message:String(e),errorName:e instanceof Error?e.name:"UnknownError",errorStack:e instanceof Error?e.stack:void 0,storageType:process.env.STORAGE_TYPE||"memory",memoryConfig:{enabled:r?.conversationMemory?.enabled,maxSessions:r?.conversationMemory?.maxSessions,maxTurnsPerSession:r?.conversationMemory?.maxTurnsPerSession},redisConfig:{host:process.env.REDIS_HOST||"(not set)",port:process.env.REDIS_PORT||"(not set)",hasPassword:!!process.env.REDIS_PASSWORD,keyPrefix:process.env.REDIS_KEY_PREFIX||"(not set)"}}),process.env.STORAGE_TYPE==="redis"&&g.error("[conversationMemoryInitializer] Redis configuration error details",{REDIS_HOST:process.env.REDIS_HOST||"(not set)",REDIS_PORT:process.env.REDIS_PORT||"(not set)",REDIS_PASSWORD:process.env.REDIS_PASSWORD?"******":"(not set)",REDIS_DB:process.env.REDIS_DB||"(not set)",REDIS_KEY_PREFIX:process.env.REDIS_KEY_PREFIX||"(not set)",REDIS_TTL:process.env.REDIS_TTL||"(not set)",errorMessage:e instanceof Error?e.message:String(e)}),e}}var cAe=I(async()=>{"use strict";gh();te();await iAe()});var lAe={};ue(lAe,{AuthProviderFactory:()=>ro,createAuthProvider:()=>hX});async function hX(r,e){return ro.createProvider(r,e)}var ro,Z0=I(()=>{"use strict";te();qs();ro=class r{static providers=new Map;static aliasMap=new Map;static registerProvider(e,t,n=[],o){r.providers.set(e,{factory:t,aliases:n,metadata:o});for(let s of n)r.aliasMap.set(s.toLowerCase(),e);g.debug(`Registered auth provider: ${e}`)}static async createProvider(e,t){let n=r.resolveType(e),o=r.providers.get(n);if(!o)throw Ze.create("PROVIDER_NOT_FOUND",`Auth provider not found: ${e}. Available: ${r.getAvailableProviders().join(", ")}`);try{return await o.factory(t)}catch(s){throw Ze.create("CREATION_FAILED",`Failed to create auth provider ${e}: ${s instanceof Error?s.message:String(s)}`,{cause:s instanceof Error?s:void 0})}}static hasProvider(e){return r.providers.has(e)||r.aliasMap.has(e.toLowerCase())}static getAvailableProviders(){return Array.from(r.providers.keys())}static getProviderMetadata(e){let t=r.resolveType(e);return r.providers.get(t)?.metadata}static getAllProviderInfo(){return Array.from(r.providers.entries()).map(([e,t])=>({type:e,aliases:t.aliases,metadata:t.metadata}))}static clearRegistrations(){r.providers.clear(),r.aliasMap.clear()}static resolveType(e){return r.aliasMap.get(e.toLowerCase())||e}}});var wEe={};ue(wEe,{NeuroLink:()=>zh,default:()=>m4t,neurolink:()=>dAe});function d4t(r){let e=r.toLowerCase();return e.includes("not found")||e.includes("404")||e.includes("does not exist")||e.includes("no such")?"not_found":e.includes("permission")||e.includes("forbidden")||e.includes("403")||e.includes("unauthorized")||e.includes("401")||e.includes("access denied")?"permission_denied":e.includes("timeout")||e.includes("timed out")||e.includes("deadline exceeded")?"timeout":e.includes("rate limit")||e.includes("429")||e.includes("too many requests")||e.includes("throttl")?"rate_limited":e.includes("invalid")||e.includes("validation")||e.includes("bad request")||e.includes("400")?"validation_error":"unknown"}function p4t(r){switch(r){case"not_found":return"validation";case"permission_denied":return"permission";case"timeout":return"timeout";case"rate_limited":return"resource";case"validation_error":return"validation";case"unknown":return"execution"}}function uAe(r){if(r instanceof pc||r instanceof nn||r instanceof mC)return!0;if(r&&typeof r=="object"){let e=r,t=typeof e.status=="number"?e.status:typeof e.statusCode=="number"?e.statusCode:void 0;if(t&&Hke.includes(t))return!0}if(r instanceof Error){let e=r.message;if(e.includes("NOT_FOUND")||e.includes("Model Not Found")||e.includes("model not found")||e.includes("PERMISSION_DENIED")||e.includes("UNAUTHENTICATED"))return!0}return!1}var yX,zh,dAe,m4t,H1=I(async()=>{"use strict";Er();ZE();jo();cC();Twe();lEe();kEe();REe();MEe();OEe();NEe();Su();i6();N8();Ya();Tw();BEe();GEe();VEe();$8();j8();sU();T9();_9();Q0e();_b();HU();nke();ske();dM();Ev();Jd();_ke();Eke();N4();su();Pv();K1();Gc();gh();wt();Oke();te();G1();Nke();Vke();ep();Wke();Md();U9();xO();Hg();j9();iF();try{process.env.DOTENV_CONFIG_QUIET=process.env.DOTENV_CONFIG_QUIET??"true";let{config:r}=await Promise.resolve().then(()=>Ii(ORe(),1));r({quiet:!0})}catch{}yX=new Zg,zh=class{mcpInitialized=!1;mcpSkipped=!1;mcpInitPromise=null;emitter=new dr;_taskManager;_taskManagerConfig;toolRegistry;autoDiscoveredServerInfos=[];externalServerManager;toolCache=null;toolCacheDuration;modelAliasConfig;lastCompactionMessageCount=new Map;getCompactionSessionId(e){return e.context?.sessionId||"__default__"}mcpToolResultCache;mcpToolRouter;mcpToolBatcher;mcpEnhancedDiscovery;mcpToolMiddlewares=[];_disableToolCacheForCurrentRequest=!1;mcpEnhancementsConfig;toolCircuitBreakers=new Map;toolExecutionMetrics=new Map;currentStreamToolExecutions=[];toolExecutionHistory=[];activeToolExecutions=new Map;emitToolEndEvent(e,t,n,o,s){this.emitter.emit("tool:end",eh(e,{responseTime:Date.now()-t,success:n,timestamp:Date.now(),result:o,error:s?s.message:void 0}))}conversationMemory;conversationMemoryNeedsInit=!1;conversationMemoryConfig;enableOrchestration;authProvider;pendingAuthConfig;authInitPromise;hitlManager;_sessionCostUsd=0;fileRegistry;cachedFileTools=null;memoryInstance;memorySDKConfig;async setLangfuseContextFromOptions(e,t){if(e.context&&typeof e.context=="object"&&e.context!==null){let n=!1;try{let o=e.context;if(o.userId||o.sessionId||o.conversationId||o.requestId||o.traceName||o.metadata){let s;if(o.metadata&&typeof o.metadata=="object"){let i=o.metadata,a={};for(let[l,u]of Object.entries(i))(typeof u=="string"||typeof u=="number"||typeof u=="boolean")&&(a[l]=u);Object.keys(a).length>0&&(s=a)}return await new Promise((i,a)=>{Yg({userId:typeof o.userId=="string"?o.userId:null,sessionId:typeof o.sessionId=="string"?o.sessionId:null,conversationId:typeof o.conversationId=="string"?o.conversationId:null,requestId:typeof o.requestId=="string"?o.requestId:null,traceName:typeof o.traceName=="string"?o.traceName:null,metadata:o.metadata&&typeof o.metadata=="object"?o.metadata:null,...s!==void 0&&{customAttributes:s}},async()=>{try{n=!0;let l=await t();i(l)}catch(l){a(l)}})})}}catch(o){if(n)throw o;g.warn("Failed to set Langfuse context from options",{error:o instanceof Error?o.message:String(o)})}}return await t()}createMetricsTraceContext(){return{traceId:crypto.randomUUID().replace(/-/g,""),parentSpanId:crypto.randomUUID().replace(/-/g,"").substring(0,16)}}enforceSessionBudget(e){if(!(e===void 0||e<=0||this._sessionCostUsd<e))throw new He({code:"SESSION_BUDGET_EXCEEDED",message:`Session budget exceeded: spent $${this._sessionCostUsd.toFixed(4)} of $${e.toFixed(4)} limit`,category:"validation",severity:"high",retriable:!1,context:{spent:this._sessionCostUsd,limit:e}})}assertInputText(e,t){if(!e||typeof e!="string")throw new Error(t)}async applyAuthenticatedRequestContext(e){if(e.auth?.token){let{AuthError:n}=await Promise.resolve().then(()=>(qs(),W0e));if(await this.ensureAuthProvider(),!this.authProvider)throw n.create("PROVIDER_ERROR","No auth provider configured. Set auth in constructor or via setAuthProvider() before using auth: { token }.");let o;try{o=await Be(this.authProvider.authenticateToken(e.auth.token),5e3,n.create("PROVIDER_ERROR","Auth token validation timed out after 5000ms"))}catch(s){throw s instanceof Error&&"feature"in s&&s.feature==="Auth"?s:n.create("PROVIDER_ERROR",`Auth token validation failed: ${s instanceof Error?s.message:String(s)}`)}if(!o.valid)throw n.create("INVALID_TOKEN",o.error||"Token validation failed");if(!o.user)throw n.create("INVALID_TOKEN","Token validated but no user identity returned");if(!o.user.id)throw n.create("INVALID_TOKEN","Token validated but user identity missing required 'id' field");e.context={...e.context||{},userId:o.user.id,userEmail:o.user.email,userRoles:o.user.roles}}if(!e.requestContext)return;let t=e.auth?.token&&this.authProvider?{userId:e.context?.userId,userEmail:e.context?.userEmail,userRoles:e.context?.userRoles}:{};e.context={...e.context||{},...e.requestContext,...t}}applyGenerateLifecycleMiddleware(e){!e.onFinish&&!e.onError||(e.middleware={...e.middleware,middlewareConfig:{...e.middleware?.middlewareConfig,lifecycle:{...e.middleware?.middlewareConfig?.lifecycle,enabled:!0,config:{...e.middleware?.middlewareConfig?.lifecycle?.config,...e.onFinish!==void 0?{onFinish:e.onFinish}:{},...e.onError!==void 0?{onError:e.onError}:{}}}}})}applyStreamLifecycleMiddleware(e){!e.onFinish&&!e.onError&&!e.onChunk||(e.middleware={...e.middleware,middlewareConfig:{...e.middleware?.middlewareConfig,lifecycle:{...e.middleware?.middlewareConfig?.lifecycle,enabled:!0,config:{...e.middleware?.middlewareConfig?.lifecycle?.config,...e.onFinish!==void 0?{onFinish:e.onFinish}:{},...e.onError!==void 0?{onError:e.onError}:{},...e.onChunk!==void 0?{onChunk:e.onChunk}:{}}}}})}initializeMemoryConfig(){let e=this.conversationMemoryConfig?.conversationMemory?.memory;return e?.enabled?(this.memorySDKConfig=e,!0):!1}ensureMemoryReady(){return this.memoryInstance!==void 0?this.memoryInstance:this.initializeMemoryConfig()?this.memorySDKConfig?(this.memoryInstance=rke(this.memorySDKConfig),this.memoryInstance):(this.memoryInstance=null,null):(this.memoryInstance=null,null)}toolExecutionContext;observabilityConfig;metricsAggregator=new tg;get _metricsTraceContext(){return yX.getStore()??null}constructor(e){this.toolRegistry=e?.toolRegistry||new Mh,this.fileRegistry=new Y1,this.observabilityConfig=e?.observability,this.enableOrchestration=e?.enableOrchestration??!1,e?.modelAliasConfig&&(this.modelAliasConfig=e.modelAliasConfig),g.setEventEmitter(this.emitter);let t=process.env.NEUROLINK_TOOL_CACHE_DURATION;this.toolCacheDuration=t?parseInt(t,10):2e4;let n=Date.now(),o=process.hrtime.bigint(),s=`neurolink-constructor-${Date.now()}-${Math.random().toString(36).substr(2,9)}`;this.initializeProviderRegistry(s,n,o),this.initializeConversationMemory(e,s,n,o),this.initializeExternalServerManager(s,n,o),this.initializeHITL(e,s,n,o),this.initializeMCPEnhancements(e),this.registerFileTools(),this.registerMemoryRetrievalTools(),this.initializeLangfuse(s,n,o),this.initializeMetricsListeners(),this.logConstructorComplete(s,n,o),e?.auth&&(this.pendingAuthConfig=e.auth),this._taskManagerConfig=e?.tasks,this._taskManagerConfig&&(this._taskManager=new M0(this,this._taskManagerConfig),this._taskManager.setEmitter(this.emitter),this.registerTaskTools(this._taskManager))}get tasks(){return this._taskManager||(this._taskManager=new M0(this,this._taskManagerConfig),this._taskManager.setEmitter(this.emitter),this.registerTaskTools(this._taskManager)),this._taskManager}initializeProviderRegistry(e,t,n){let o=process.hrtime.bigint();g.debug("[NeuroLink] \u{1F3D7}\uFE0F LOG_POINT_C002_PROVIDER_REGISTRY_SETUP_START",{logPoint:"C002_PROVIDER_REGISTRY_SETUP_START",constructorId:e,timestamp:new Date().toISOString(),elapsedMs:Date.now()-t,elapsedNs:(process.hrtime.bigint()-n).toString(),registrySetupStartTimeNs:o.toString(),message:"Starting ProviderRegistry configuration for security"}),Tl.setOptions({enableManualMCP:!1})}initializeConversationMemory(e,t,n,o){if(e?.conversationMemory?.enabled){let s=process.hrtime.bigint();this.conversationMemoryConfig=e,this.conversationMemoryNeedsInit=!0;let a=process.hrtime.bigint()-s;g.debug("[NeuroLink] \u2705 LOG_POINT_C006_MEMORY_INIT_FLAG_SET_SUCCESS",{logPoint:"C006_MEMORY_INIT_FLAG_SET_SUCCESS",constructorId:t,timestamp:new Date().toISOString(),elapsedMs:Date.now()-n,elapsedNs:(process.hrtime.bigint()-o).toString(),memoryInitDurationNs:a.toString(),memoryInitDurationMs:Number(a)/Vl,message:"Conversation memory initialization flag set successfully for lazy loading"})}else g.debug("[NeuroLink] \u{1F6AB} LOG_POINT_C008_MEMORY_DISABLED",{logPoint:"C008_MEMORY_DISABLED",constructorId:t,timestamp:new Date().toISOString(),elapsedMs:Date.now()-n,elapsedNs:(process.hrtime.bigint()-o).toString(),hasConfig:!!e,hasMemoryConfig:!!e?.conversationMemory,memoryEnabled:e?.conversationMemory?.enabled||!1,reason:e?e.conversationMemory?e.conversationMemory.enabled?"UNKNOWN":"MEMORY_DISABLED":"NO_MEMORY_CONFIG":"NO_CONFIG",message:"Conversation memory not enabled - skipping initialization"})}initializeHITL(e,t,n,o){if(e?.hitl?.enabled){let s=process.hrtime.bigint();g.debug("[NeuroLink] \u{1F6E1}\uFE0F LOG_POINT_C015_HITL_INIT_START",{logPoint:"C015_HITL_INIT_START",constructorId:t,timestamp:new Date().toISOString(),elapsedMs:Date.now()-n,elapsedNs:(process.hrtime.bigint()-o).toString(),hitlInitStartTimeNs:s.toString(),hitlConfig:{enabled:e.hitl.enabled,dangerousActions:e.hitl.dangerousActions||[],timeout:e.hitl.timeout||3e4,allowArgumentModification:e.hitl.allowArgumentModification??!0,auditLogging:e.hitl.auditLogging??!1},message:"Starting HITL (Human-in-the-Loop) initialization"});try{this.hitlManager=new eU(e.hitl),this.toolRegistry.setHITLManager(this.hitlManager),this.externalServerManager.setHITLManager(this.hitlManager),this.setupHITLEventForwarding();let a=process.hrtime.bigint()-s;g.debug("[NeuroLink] \u2705 LOG_POINT_C016_HITL_INIT_SUCCESS",{logPoint:"C016_HITL_INIT_SUCCESS",constructorId:t,timestamp:new Date().toISOString(),elapsedMs:Date.now()-n,elapsedNs:(process.hrtime.bigint()-o).toString(),hitlInitDurationNs:a.toString(),hitlInitDurationMs:Number(a)/Vl,hasHitlManager:!!this.hitlManager,message:"HITL (Human-in-the-Loop) initialized successfully"}),g.info("[NeuroLink] HITL safety features enabled",{dangerousActions:e.hitl.dangerousActions?.length||0,timeout:e.hitl.timeout||3e4,allowArgumentModification:e.hitl.allowArgumentModification??!0,auditLogging:e.hitl.auditLogging??!1})}catch(i){let l=process.hrtime.bigint()-s;throw g.error("[NeuroLink] \u274C LOG_POINT_C017_HITL_INIT_ERROR",{logPoint:"C017_HITL_INIT_ERROR",constructorId:t,timestamp:new Date().toISOString(),elapsedMs:Date.now()-n,elapsedNs:(process.hrtime.bigint()-o).toString(),hitlInitDurationNs:l.toString(),hitlInitDurationMs:Number(l)/Vl,error:i instanceof Error?i.message:String(i),errorName:i instanceof Error?i.name:"UnknownError",errorStack:i instanceof Error?i.stack:void 0,message:"HITL (Human-in-the-Loop) initialization failed"}),i}}else g.debug("[NeuroLink] \u{1F6AB} LOG_POINT_C018_HITL_DISABLED",{logPoint:"C018_HITL_DISABLED",constructorId:t,timestamp:new Date().toISOString(),elapsedMs:Date.now()-n,elapsedNs:(process.hrtime.bigint()-o).toString(),hasConfig:!!e,hasHitlConfig:!!e?.hitl,hitlEnabled:e?.hitl?.enabled||!1,reason:e?e.hitl?e.hitl.enabled?"UNKNOWN":"HITL_DISABLED":"NO_HITL_CONFIG":"NO_CONFIG",message:"HITL (Human-in-the-Loop) not enabled - skipping initialization"})}initializeMCPEnhancements(e){let t=e?.mcp;this.mcpEnhancementsConfig=t,t?.cache?.enabled&&(this.mcpToolResultCache=new Fm({ttl:t.cache.ttl??3e5,maxSize:t.cache.maxSize??500,strategy:t.cache.strategy??"lru"}),g.debug("[NeuroLink] MCP tool result cache initialized",{ttl:t.cache.ttl??3e5,maxSize:t.cache.maxSize??500,strategy:t.cache.strategy??"lru"})),t?.batcher?.enabled&&(this.mcpToolBatcher=new Um({maxBatchSize:t.batcher.maxBatchSize??10,maxWaitMs:t.batcher.maxWaitMs??100}),this.mcpToolBatcher.setToolExecutor(async(n,o)=>this.executeToolInternal(n,o,{timeout:ju.EXECUTION_DEFAULT_MS,maxRetries:jn.DEFAULT,retryDelayMs:Zr.BASE_MS})),g.debug("[NeuroLink] MCP tool call batcher initialized")),t?.discovery?.enabled!==!1&&(this.mcpEnhancedDiscovery=new bh,g.debug("[NeuroLink] Enhanced tool discovery initialized")),t?.middleware?.length&&(this.mcpToolMiddlewares=[...t.middleware],g.debug("[NeuroLink] MCP tool middlewares registered",{count:this.mcpToolMiddlewares.length}))}registerFileTools(){let e=L8(this.fileRegistry),t=Object.entries(e).map(async([n,o])=>{let s=`direct.${n}`,i={name:n,description:o.description||`File tool: ${n}`,inputSchema:{},serverId:"direct",category:"built-in"};await this.toolRegistry.registerTool(s,i,{execute:async a=>{try{return{success:!0,data:await o.execute(a,{toolCallId:"file-tool",messages:[]}),metadata:{toolName:n,serverId:"direct",executionTime:0}}}catch(l){return{success:!1,error:l instanceof Error?l.message:String(l),metadata:{toolName:n,serverId:"direct",executionTime:0}}}},description:o.description,inputSchema:{}})});Promise.all(t).then(()=>{g.debug(`[NeuroLink] Registered ${Object.keys(e).length} file reference tools`)})}registerTaskTools(e){let t=wke(e);for(let[n,o]of Object.entries(t)){let s=`direct.${n}`,i={name:n,description:o.description||`Task tool: ${n}`,inputSchema:{},serverId:"direct",category:"built-in"};this.toolRegistry.registerTool(s,i,{execute:async a=>{try{return{success:!0,data:await o.execute(a,{toolCallId:"task-tool",messages:[]}),metadata:{toolName:n,serverId:"direct",executionTime:0}}}catch(l){return{success:!1,error:l instanceof Error?l.message:String(l),metadata:{toolName:n,serverId:"direct",executionTime:0}}}},description:o.description,inputSchema:{}})}g.debug(`[NeuroLink] Registered ${Object.keys(t).length} task tools`)}registerMemoryRetrievalTools(){let e=this.conversationMemoryConfig?.conversationMemory,t=!!e?.redisConfig||e&&"redis"in e&&!!e.redis||process.env.STORAGE_TYPE==="redis";if(!e?.enabled||!t){g.debug("[NeuroLink] Skipping memory retrieval tools \u2014 requires Redis conversation memory");return}let o=Object.entries({retrieve_context:{description:"Retrieve messages from conversation memory. Use this to access full tool outputs when a result was truncated, review previous assistant responses, or search through conversation history.",execute:async s=>{let i=this.conversationMemory;if(!i||!("getSessionRaw"in i))return{success:!1,error:"Memory retrieval not available \u2014 Redis memory manager not initialized",metadata:{toolName:"retrieve_context",serverId:"direct",executionTime:0}};let l=await oke(i).retrieve_context.execute(s,{toolCallId:"memory-retrieval",messages:[]}),u=l&&typeof l=="object"&&"error"in l&&!("messages"in l),d=u?l.error:void 0;return{success:!u,data:l,...d?{error:d}:{},metadata:{toolName:"retrieve_context",serverId:"direct",executionTime:0}}}}}).map(async([s,i])=>{let a=`direct.${s}`,l={name:s,description:i.description,inputSchema:{},serverId:"direct",category:"built-in"};await this.toolRegistry.registerTool(a,l,{execute:async u=>{try{return await i.execute(u)}catch(d){return{success:!1,error:d instanceof Error?d.message:String(d),metadata:{toolName:s,serverId:"direct",executionTime:0}}}},description:i.description,inputSchema:{}})});Promise.all(o).then(()=>{g.info("[NeuroLink] Memory retrieval tools registered")})}formatMemoryContext(e,t){return`Context from previous conversations:
2028
2028
 
2029
2029
  ${e}
2030
2030
 
@@ -2,8 +2,8 @@
2
2
  * Conversation Memory Factory for NeuroLink
3
3
  * Creates appropriate conversation memory manager based on configuration
4
4
  */
5
- import type { ConversationMemoryConfig, RedisStorageConfig } from "../types/conversation.js";
6
5
  import type { StorageType } from "../types/common.js";
6
+ import type { ConversationMemoryConfig, RedisStorageConfig } from "../types/conversation.js";
7
7
  import { ConversationMemoryManager } from "./conversationMemoryManager.js";
8
8
  import { RedisConversationMemoryManager } from "./redisConversationMemoryManager.js";
9
9
  /**
@@ -2,9 +2,9 @@
2
2
  * Conversation Memory Factory for NeuroLink
3
3
  * Creates appropriate conversation memory manager based on configuration
4
4
  */
5
+ import { logger } from "../utils/logger.js";
5
6
  import { ConversationMemoryManager } from "./conversationMemoryManager.js";
6
7
  import { RedisConversationMemoryManager } from "./redisConversationMemoryManager.js";
7
- import { logger } from "../utils/logger.js";
8
8
  /**
9
9
  * Creates a conversation memory manager based on configuration
10
10
  */
@@ -99,6 +99,7 @@ export function getStorageType() {
99
99
  */
100
100
  export function getRedisConfigFromEnv() {
101
101
  logger.debug("[conversationMemoryFactory] Reading Redis configuration from environment", {
102
+ REDIS_URL: process.env.REDIS_URL ? "******" : "(not set)",
102
103
  REDIS_HOST: process.env.REDIS_HOST || "(not set)",
103
104
  REDIS_PORT: process.env.REDIS_PORT || "(not set)",
104
105
  REDIS_PASSWORD: process.env.REDIS_PASSWORD ? "******" : "(not set)",
@@ -128,6 +129,10 @@ export function getRedisConfigFromEnv() {
128
129
  : undefined,
129
130
  },
130
131
  };
132
+ if (process.env.REDIS_URL) {
133
+ logger.debug("[conversationMemoryFactory] Using REDIS_URL for connection");
134
+ config.url = process.env.REDIS_URL;
135
+ }
131
136
  logger.debug("[conversationMemoryFactory] Redis configuration normalized", {
132
137
  host: config.host || "localhost",
133
138
  port: config.port || 6379,
@@ -2,8 +2,8 @@
2
2
  * Conversation Memory Factory for NeuroLink
3
3
  * Creates appropriate conversation memory manager based on configuration
4
4
  */
5
- import type { ConversationMemoryConfig, RedisStorageConfig } from "../types/conversation.js";
6
5
  import type { StorageType } from "../types/common.js";
6
+ import type { ConversationMemoryConfig, RedisStorageConfig } from "../types/conversation.js";
7
7
  import { ConversationMemoryManager } from "./conversationMemoryManager.js";
8
8
  import { RedisConversationMemoryManager } from "./redisConversationMemoryManager.js";
9
9
  /**
@@ -2,9 +2,9 @@
2
2
  * Conversation Memory Factory for NeuroLink
3
3
  * Creates appropriate conversation memory manager based on configuration
4
4
  */
5
+ import { logger } from "../utils/logger.js";
5
6
  import { ConversationMemoryManager } from "./conversationMemoryManager.js";
6
7
  import { RedisConversationMemoryManager } from "./redisConversationMemoryManager.js";
7
- import { logger } from "../utils/logger.js";
8
8
  /**
9
9
  * Creates a conversation memory manager based on configuration
10
10
  */
@@ -99,6 +99,7 @@ export function getStorageType() {
99
99
  */
100
100
  export function getRedisConfigFromEnv() {
101
101
  logger.debug("[conversationMemoryFactory] Reading Redis configuration from environment", {
102
+ REDIS_URL: process.env.REDIS_URL ? "******" : "(not set)",
102
103
  REDIS_HOST: process.env.REDIS_HOST || "(not set)",
103
104
  REDIS_PORT: process.env.REDIS_PORT || "(not set)",
104
105
  REDIS_PASSWORD: process.env.REDIS_PASSWORD ? "******" : "(not set)",
@@ -128,6 +129,10 @@ export function getRedisConfigFromEnv() {
128
129
  : undefined,
129
130
  },
130
131
  };
132
+ if (process.env.REDIS_URL) {
133
+ logger.debug("[conversationMemoryFactory] Using REDIS_URL for connection");
134
+ config.url = process.env.REDIS_URL;
135
+ }
131
136
  logger.debug("[conversationMemoryFactory] Redis configuration normalized", {
132
137
  host: config.host || "localhost",
133
138
  port: config.port || 6379,
@@ -493,6 +493,8 @@ export type ConversationSummary = ConversationBase & {
493
493
  * Redis storage configuration
494
494
  */
495
495
  export type RedisStorageConfig = {
496
+ /** Redis connection URL (e.g., 'rediss://host:6379' for TLS) */
497
+ url?: string;
496
498
  /** Redis host (default: 'localhost') */
497
499
  host?: string;
498
500
  /** Redis port (default: 6379) */
@@ -0,0 +1,45 @@
1
+ import type { ProviderName } from "../types/providers.js";
2
+ declare const SUPPORTED_FORMATS: readonly ["jpeg", "png", "webp"];
3
+ type SupportedFormat = (typeof SUPPORTED_FORMATS)[number];
4
+ /**
5
+ * Provider-specific image size limits in bytes
6
+ */
7
+ export declare const PROVIDER_IMAGE_LIMITS: Record<ProviderName, number>;
8
+ export interface CompressionOptions {
9
+ provider: ProviderName;
10
+ quality?: number;
11
+ maxDimension?: number;
12
+ format?: SupportedFormat;
13
+ }
14
+ export interface CompressionResult {
15
+ buffer: Buffer;
16
+ originalSize: number;
17
+ compressedSize: number;
18
+ compressionRatio: number;
19
+ metadata: {
20
+ width: number;
21
+ height: number;
22
+ format: string;
23
+ };
24
+ }
25
+ /**
26
+ * Compress an image to meet provider-specific size limits
27
+ * @param imageBuffer - Input image buffer
28
+ * @param options - Compression options including provider name
29
+ * @returns Compressed image buffer with metadata
30
+ */
31
+ export declare function compressImage(imageBuffer: Buffer, options: CompressionOptions): Promise<CompressionResult>;
32
+ /**
33
+ * Check if an image needs compression for a specific provider
34
+ * @param imageBuffer - Input image buffer
35
+ * @param provider - AI provider name
36
+ * @returns True if compression is needed
37
+ */
38
+ export declare function needsCompression(imageBuffer: Buffer, provider: ProviderName): boolean;
39
+ /**
40
+ * Get the size limit for a specific provider
41
+ * @param provider - AI provider name
42
+ * @returns Size limit in bytes
43
+ */
44
+ export declare function getProviderSizeLimit(provider: ProviderName): number;
45
+ export {};
@@ -0,0 +1,137 @@
1
+ import sharp from "sharp";
2
+ import { withTimeout } from "./async/index.js";
3
+ const SUPPORTED_FORMATS = ["jpeg", "png", "webp"];
4
+ const IMAGE_COMPRESSION_TIMEOUT_MS = 30_000;
5
+ /**
6
+ * Provider-specific image size limits in bytes
7
+ */
8
+ export const PROVIDER_IMAGE_LIMITS = {
9
+ openai: 20 * 1024 * 1024, // 20MB
10
+ "openai-compatible": 20 * 1024 * 1024, // 20MB (same as OpenAI)
11
+ anthropic: 5 * 1024 * 1024, // 5MB
12
+ "google-ai": 4 * 1024 * 1024, // 4MB
13
+ vertex: 4 * 1024 * 1024, // 4MB
14
+ bedrock: 5 * 1024 * 1024, // 5MB
15
+ azure: 20 * 1024 * 1024, // 20MB
16
+ mistral: 5 * 1024 * 1024, // 5MB
17
+ huggingface: 10 * 1024 * 1024, // 10MB
18
+ ollama: 100 * 1024 * 1024, // 100MB (local, no strict limit)
19
+ openrouter: 20 * 1024 * 1024, // 20MB
20
+ sagemaker: 5 * 1024 * 1024, // 5MB
21
+ litellm: 20 * 1024 * 1024, // 20MB (proxy, use OpenAI default)
22
+ auto: 5 * 1024 * 1024, // 5MB (conservative fallback)
23
+ };
24
+ /**
25
+ * Compress an image to meet provider-specific size limits
26
+ * @param imageBuffer - Input image buffer
27
+ * @param options - Compression options including provider name
28
+ * @returns Compressed image buffer with metadata
29
+ */
30
+ export async function compressImage(imageBuffer, options) {
31
+ const { provider, quality = 80, maxDimension, format } = options;
32
+ const sizeLimit = PROVIDER_IMAGE_LIMITS[provider];
33
+ const originalSize = imageBuffer.length;
34
+ // Get original metadata
35
+ const image = sharp(imageBuffer);
36
+ const metadata = await withTimeout(image.metadata(), IMAGE_COMPRESSION_TIMEOUT_MS, "Timed out reading image metadata");
37
+ if (!metadata.width || !metadata.height) {
38
+ throw new Error("Unable to read image dimensions");
39
+ }
40
+ // If image is already under limit and no format conversion needed, return as-is
41
+ if (originalSize <= sizeLimit && !format && !maxDimension) {
42
+ return {
43
+ buffer: imageBuffer,
44
+ originalSize,
45
+ compressedSize: originalSize,
46
+ compressionRatio: 1,
47
+ metadata: {
48
+ width: metadata.width,
49
+ height: metadata.height,
50
+ format: metadata.format ?? "unknown",
51
+ },
52
+ };
53
+ }
54
+ // Prepare compression pipeline
55
+ let pipeline = sharp(imageBuffer);
56
+ // Resize if needed
57
+ if (maxDimension) {
58
+ const needsResize = metadata.width > maxDimension || metadata.height > maxDimension;
59
+ if (needsResize) {
60
+ pipeline = pipeline.resize(maxDimension, maxDimension, {
61
+ fit: "inside",
62
+ withoutEnlargement: true,
63
+ });
64
+ }
65
+ }
66
+ // Resolve target format — validate metadata.format against supported set
67
+ const rawFormat = metadata.format;
68
+ const targetFormat = format ??
69
+ (SUPPORTED_FORMATS.includes(rawFormat)
70
+ ? rawFormat
71
+ : "jpeg");
72
+ const applyFormat = (p, q) => {
73
+ switch (targetFormat) {
74
+ case "jpeg":
75
+ return p.jpeg({ quality: q, mozjpeg: true });
76
+ case "png":
77
+ return p.png({ quality: q, compressionLevel: 9 });
78
+ case "webp":
79
+ return p.webp({ quality: q });
80
+ }
81
+ };
82
+ // Compress
83
+ let compressedBuffer = await withTimeout(applyFormat(pipeline, quality).toBuffer(), IMAGE_COMPRESSION_TIMEOUT_MS, "Timed out compressing image");
84
+ let currentQuality = quality;
85
+ // Iteratively reduce quality if still over limit
86
+ // Note: the sharp pipeline must be rebuilt on each iteration because
87
+ // sharp does not support modifying quality settings after creation.
88
+ while (compressedBuffer.length > sizeLimit && currentQuality > 10) {
89
+ currentQuality -= 10;
90
+ let p = sharp(imageBuffer);
91
+ if (maxDimension) {
92
+ p = p.resize(maxDimension, maxDimension, {
93
+ fit: "inside",
94
+ withoutEnlargement: true,
95
+ });
96
+ }
97
+ compressedBuffer = await withTimeout(applyFormat(p, currentQuality).toBuffer(), IMAGE_COMPRESSION_TIMEOUT_MS, "Timed out compressing image");
98
+ }
99
+ // Final check
100
+ if (compressedBuffer.length > sizeLimit) {
101
+ throw new Error(`Unable to compress image to ${sizeLimit} bytes for provider ${provider}. ` +
102
+ `Final size: ${compressedBuffer.length} bytes. ` +
103
+ `Try using a smaller image or lower maxDimension.`);
104
+ }
105
+ // Get final metadata
106
+ const finalMetadata = await withTimeout(sharp(compressedBuffer).metadata(), IMAGE_COMPRESSION_TIMEOUT_MS, "Timed out reading compressed image metadata");
107
+ return {
108
+ buffer: compressedBuffer,
109
+ originalSize,
110
+ compressedSize: compressedBuffer.length,
111
+ compressionRatio: originalSize / compressedBuffer.length,
112
+ metadata: {
113
+ width: finalMetadata.width ?? 0,
114
+ height: finalMetadata.height ?? 0,
115
+ format: targetFormat,
116
+ },
117
+ };
118
+ }
119
+ /**
120
+ * Check if an image needs compression for a specific provider
121
+ * @param imageBuffer - Input image buffer
122
+ * @param provider - AI provider name
123
+ * @returns True if compression is needed
124
+ */
125
+ export function needsCompression(imageBuffer, provider) {
126
+ const sizeLimit = PROVIDER_IMAGE_LIMITS[provider];
127
+ return imageBuffer.length > sizeLimit;
128
+ }
129
+ /**
130
+ * Get the size limit for a specific provider
131
+ * @param provider - AI provider name
132
+ * @returns Size limit in bytes
133
+ */
134
+ export function getProviderSizeLimit(provider) {
135
+ return PROVIDER_IMAGE_LIMITS[provider];
136
+ }
137
+ //# sourceMappingURL=imageCompressor.js.map
@@ -3,7 +3,7 @@
3
3
  * Helper functions for Redis storage operations
4
4
  */
5
5
  import { createClient } from "redis";
6
- import type { RedisStorageConfig, RedisConversationObject } from "../types/conversation.js";
6
+ import type { RedisConversationObject, RedisStorageConfig } from "../types/conversation.js";
7
7
  type RedisClient = ReturnType<typeof createClient>;
8
8
  /**
9
9
  * Get a pooled Redis connection. Multiple callers with the same host:port:db
@@ -13,7 +13,9 @@ const pendingConnections = new Map();
13
13
  * share a single connection, reducing connection count.
14
14
  */
15
15
  export async function getPooledRedisClient(config) {
16
- const key = `${config.host}:${config.port}:${config.db}:${config.password ? "auth" : "noauth"}`;
16
+ const key = config.url
17
+ ? `url:${config.url.replace(/:\/\/[^:]+:[^@]+@/, "://[redacted]@")}`
18
+ : `${config.host}:${config.port}:${config.db}:${config.password ? "auth" : "noauth"}`;
17
19
  const existing = connectionPool.get(key);
18
20
  if (existing && existing.client.isOpen) {
19
21
  existing.refCount++;
@@ -114,7 +116,7 @@ export function getPoolStats() {
114
116
  * Creates a Redis client with the provided configuration
115
117
  */
116
118
  export async function createRedisClient(config) {
117
- const url = `redis://${config.host}:${config.port}/${config.db}`;
119
+ const url = config.url || `redis://${config.host}:${config.port}/${config.db}`;
118
120
  // Create client options
119
121
  const clientOptions = {
120
122
  url,
@@ -126,12 +128,12 @@ export async function createRedisClient(config) {
126
128
  return new Error("Redis connection retries exhausted");
127
129
  }
128
130
  const delay = Math.min((config.connectionOptions?.retryDelayOnFailover || 100) *
129
- Math.pow(2, retries), 10000);
131
+ 2 ** retries, 10000);
130
132
  return delay;
131
133
  },
132
134
  },
133
135
  };
134
- if (config.password) {
136
+ if (config.password && !config.url) {
135
137
  clientOptions.password = config.password;
136
138
  }
137
139
  // Create client with secured options
@@ -349,11 +351,36 @@ export function getNormalizedConfig(config) {
349
351
  const keyPrefix = config.keyPrefix || "neurolink:conversation:";
350
352
  // Intelligent default: derive user sessions prefix from conversation prefix
351
353
  const defaultUserSessionsPrefix = keyPrefix.replace(/conversation:?$/, "user:sessions:");
354
+ let host = config.host || "localhost";
355
+ let port = config.port || 6379;
356
+ let password = config.password || "";
357
+ let db = config.db || 0;
358
+ let url = config.url;
359
+ if (url) {
360
+ try {
361
+ const parsedUrl = new URL(url);
362
+ host = parsedUrl.hostname;
363
+ port = parsedUrl.port ? parseInt(parsedUrl.port) : 6379;
364
+ password = parsedUrl.password || password;
365
+ db = parsedUrl.pathname
366
+ ? parseInt(parsedUrl.pathname.replace("/", "")) || 0
367
+ : 0;
368
+ }
369
+ catch (e) {
370
+ const sanitizedUrl = url.replace(/:\/\/[^:]+:[^@]+@/, "://[redacted]@");
371
+ logger.warn("[redisUtils] Failed to parse Redis URL, falling back to component-based connection", {
372
+ url: sanitizedUrl,
373
+ error: e instanceof Error ? e.message : String(e),
374
+ });
375
+ url = undefined;
376
+ }
377
+ }
352
378
  return {
353
- host: config.host || "localhost",
354
- port: config.port || 6379,
355
- password: config.password || "",
356
- db: config.db || 0,
379
+ url: url || "",
380
+ host,
381
+ port,
382
+ password,
383
+ db,
357
384
  keyPrefix,
358
385
  userSessionsKeyPrefix: config.userSessionsKeyPrefix || defaultUserSessionsPrefix,
359
386
  ttl: config.ttl || 86400,
@@ -493,6 +493,8 @@ export type ConversationSummary = ConversationBase & {
493
493
  * Redis storage configuration
494
494
  */
495
495
  export type RedisStorageConfig = {
496
+ /** Redis connection URL (e.g., 'rediss://host:6379' for TLS) */
497
+ url?: string;
496
498
  /** Redis host (default: 'localhost') */
497
499
  host?: string;
498
500
  /** Redis port (default: 6379) */
@@ -0,0 +1,45 @@
1
+ import type { ProviderName } from "../types/providers.js";
2
+ declare const SUPPORTED_FORMATS: readonly ["jpeg", "png", "webp"];
3
+ type SupportedFormat = (typeof SUPPORTED_FORMATS)[number];
4
+ /**
5
+ * Provider-specific image size limits in bytes
6
+ */
7
+ export declare const PROVIDER_IMAGE_LIMITS: Record<ProviderName, number>;
8
+ export interface CompressionOptions {
9
+ provider: ProviderName;
10
+ quality?: number;
11
+ maxDimension?: number;
12
+ format?: SupportedFormat;
13
+ }
14
+ export interface CompressionResult {
15
+ buffer: Buffer;
16
+ originalSize: number;
17
+ compressedSize: number;
18
+ compressionRatio: number;
19
+ metadata: {
20
+ width: number;
21
+ height: number;
22
+ format: string;
23
+ };
24
+ }
25
+ /**
26
+ * Compress an image to meet provider-specific size limits
27
+ * @param imageBuffer - Input image buffer
28
+ * @param options - Compression options including provider name
29
+ * @returns Compressed image buffer with metadata
30
+ */
31
+ export declare function compressImage(imageBuffer: Buffer, options: CompressionOptions): Promise<CompressionResult>;
32
+ /**
33
+ * Check if an image needs compression for a specific provider
34
+ * @param imageBuffer - Input image buffer
35
+ * @param provider - AI provider name
36
+ * @returns True if compression is needed
37
+ */
38
+ export declare function needsCompression(imageBuffer: Buffer, provider: ProviderName): boolean;
39
+ /**
40
+ * Get the size limit for a specific provider
41
+ * @param provider - AI provider name
42
+ * @returns Size limit in bytes
43
+ */
44
+ export declare function getProviderSizeLimit(provider: ProviderName): number;
45
+ export {};
@@ -0,0 +1,136 @@
1
+ import sharp from "sharp";
2
+ import { withTimeout } from "./async/index.js";
3
+ const SUPPORTED_FORMATS = ["jpeg", "png", "webp"];
4
+ const IMAGE_COMPRESSION_TIMEOUT_MS = 30_000;
5
+ /**
6
+ * Provider-specific image size limits in bytes
7
+ */
8
+ export const PROVIDER_IMAGE_LIMITS = {
9
+ openai: 20 * 1024 * 1024, // 20MB
10
+ "openai-compatible": 20 * 1024 * 1024, // 20MB (same as OpenAI)
11
+ anthropic: 5 * 1024 * 1024, // 5MB
12
+ "google-ai": 4 * 1024 * 1024, // 4MB
13
+ vertex: 4 * 1024 * 1024, // 4MB
14
+ bedrock: 5 * 1024 * 1024, // 5MB
15
+ azure: 20 * 1024 * 1024, // 20MB
16
+ mistral: 5 * 1024 * 1024, // 5MB
17
+ huggingface: 10 * 1024 * 1024, // 10MB
18
+ ollama: 100 * 1024 * 1024, // 100MB (local, no strict limit)
19
+ openrouter: 20 * 1024 * 1024, // 20MB
20
+ sagemaker: 5 * 1024 * 1024, // 5MB
21
+ litellm: 20 * 1024 * 1024, // 20MB (proxy, use OpenAI default)
22
+ auto: 5 * 1024 * 1024, // 5MB (conservative fallback)
23
+ };
24
+ /**
25
+ * Compress an image to meet provider-specific size limits
26
+ * @param imageBuffer - Input image buffer
27
+ * @param options - Compression options including provider name
28
+ * @returns Compressed image buffer with metadata
29
+ */
30
+ export async function compressImage(imageBuffer, options) {
31
+ const { provider, quality = 80, maxDimension, format } = options;
32
+ const sizeLimit = PROVIDER_IMAGE_LIMITS[provider];
33
+ const originalSize = imageBuffer.length;
34
+ // Get original metadata
35
+ const image = sharp(imageBuffer);
36
+ const metadata = await withTimeout(image.metadata(), IMAGE_COMPRESSION_TIMEOUT_MS, "Timed out reading image metadata");
37
+ if (!metadata.width || !metadata.height) {
38
+ throw new Error("Unable to read image dimensions");
39
+ }
40
+ // If image is already under limit and no format conversion needed, return as-is
41
+ if (originalSize <= sizeLimit && !format && !maxDimension) {
42
+ return {
43
+ buffer: imageBuffer,
44
+ originalSize,
45
+ compressedSize: originalSize,
46
+ compressionRatio: 1,
47
+ metadata: {
48
+ width: metadata.width,
49
+ height: metadata.height,
50
+ format: metadata.format ?? "unknown",
51
+ },
52
+ };
53
+ }
54
+ // Prepare compression pipeline
55
+ let pipeline = sharp(imageBuffer);
56
+ // Resize if needed
57
+ if (maxDimension) {
58
+ const needsResize = metadata.width > maxDimension || metadata.height > maxDimension;
59
+ if (needsResize) {
60
+ pipeline = pipeline.resize(maxDimension, maxDimension, {
61
+ fit: "inside",
62
+ withoutEnlargement: true,
63
+ });
64
+ }
65
+ }
66
+ // Resolve target format — validate metadata.format against supported set
67
+ const rawFormat = metadata.format;
68
+ const targetFormat = format ??
69
+ (SUPPORTED_FORMATS.includes(rawFormat)
70
+ ? rawFormat
71
+ : "jpeg");
72
+ const applyFormat = (p, q) => {
73
+ switch (targetFormat) {
74
+ case "jpeg":
75
+ return p.jpeg({ quality: q, mozjpeg: true });
76
+ case "png":
77
+ return p.png({ quality: q, compressionLevel: 9 });
78
+ case "webp":
79
+ return p.webp({ quality: q });
80
+ }
81
+ };
82
+ // Compress
83
+ let compressedBuffer = await withTimeout(applyFormat(pipeline, quality).toBuffer(), IMAGE_COMPRESSION_TIMEOUT_MS, "Timed out compressing image");
84
+ let currentQuality = quality;
85
+ // Iteratively reduce quality if still over limit
86
+ // Note: the sharp pipeline must be rebuilt on each iteration because
87
+ // sharp does not support modifying quality settings after creation.
88
+ while (compressedBuffer.length > sizeLimit && currentQuality > 10) {
89
+ currentQuality -= 10;
90
+ let p = sharp(imageBuffer);
91
+ if (maxDimension) {
92
+ p = p.resize(maxDimension, maxDimension, {
93
+ fit: "inside",
94
+ withoutEnlargement: true,
95
+ });
96
+ }
97
+ compressedBuffer = await withTimeout(applyFormat(p, currentQuality).toBuffer(), IMAGE_COMPRESSION_TIMEOUT_MS, "Timed out compressing image");
98
+ }
99
+ // Final check
100
+ if (compressedBuffer.length > sizeLimit) {
101
+ throw new Error(`Unable to compress image to ${sizeLimit} bytes for provider ${provider}. ` +
102
+ `Final size: ${compressedBuffer.length} bytes. ` +
103
+ `Try using a smaller image or lower maxDimension.`);
104
+ }
105
+ // Get final metadata
106
+ const finalMetadata = await withTimeout(sharp(compressedBuffer).metadata(), IMAGE_COMPRESSION_TIMEOUT_MS, "Timed out reading compressed image metadata");
107
+ return {
108
+ buffer: compressedBuffer,
109
+ originalSize,
110
+ compressedSize: compressedBuffer.length,
111
+ compressionRatio: originalSize / compressedBuffer.length,
112
+ metadata: {
113
+ width: finalMetadata.width ?? 0,
114
+ height: finalMetadata.height ?? 0,
115
+ format: targetFormat,
116
+ },
117
+ };
118
+ }
119
+ /**
120
+ * Check if an image needs compression for a specific provider
121
+ * @param imageBuffer - Input image buffer
122
+ * @param provider - AI provider name
123
+ * @returns True if compression is needed
124
+ */
125
+ export function needsCompression(imageBuffer, provider) {
126
+ const sizeLimit = PROVIDER_IMAGE_LIMITS[provider];
127
+ return imageBuffer.length > sizeLimit;
128
+ }
129
+ /**
130
+ * Get the size limit for a specific provider
131
+ * @param provider - AI provider name
132
+ * @returns Size limit in bytes
133
+ */
134
+ export function getProviderSizeLimit(provider) {
135
+ return PROVIDER_IMAGE_LIMITS[provider];
136
+ }
@@ -3,7 +3,7 @@
3
3
  * Helper functions for Redis storage operations
4
4
  */
5
5
  import { createClient } from "redis";
6
- import type { RedisStorageConfig, RedisConversationObject } from "../types/conversation.js";
6
+ import type { RedisConversationObject, RedisStorageConfig } from "../types/conversation.js";
7
7
  type RedisClient = ReturnType<typeof createClient>;
8
8
  /**
9
9
  * Get a pooled Redis connection. Multiple callers with the same host:port:db
@@ -13,7 +13,9 @@ const pendingConnections = new Map();
13
13
  * share a single connection, reducing connection count.
14
14
  */
15
15
  export async function getPooledRedisClient(config) {
16
- const key = `${config.host}:${config.port}:${config.db}:${config.password ? "auth" : "noauth"}`;
16
+ const key = config.url
17
+ ? `url:${config.url.replace(/:\/\/[^:]+:[^@]+@/, "://[redacted]@")}`
18
+ : `${config.host}:${config.port}:${config.db}:${config.password ? "auth" : "noauth"}`;
17
19
  const existing = connectionPool.get(key);
18
20
  if (existing && existing.client.isOpen) {
19
21
  existing.refCount++;
@@ -114,7 +116,7 @@ export function getPoolStats() {
114
116
  * Creates a Redis client with the provided configuration
115
117
  */
116
118
  export async function createRedisClient(config) {
117
- const url = `redis://${config.host}:${config.port}/${config.db}`;
119
+ const url = config.url || `redis://${config.host}:${config.port}/${config.db}`;
118
120
  // Create client options
119
121
  const clientOptions = {
120
122
  url,
@@ -126,12 +128,12 @@ export async function createRedisClient(config) {
126
128
  return new Error("Redis connection retries exhausted");
127
129
  }
128
130
  const delay = Math.min((config.connectionOptions?.retryDelayOnFailover || 100) *
129
- Math.pow(2, retries), 10000);
131
+ 2 ** retries, 10000);
130
132
  return delay;
131
133
  },
132
134
  },
133
135
  };
134
- if (config.password) {
136
+ if (config.password && !config.url) {
135
137
  clientOptions.password = config.password;
136
138
  }
137
139
  // Create client with secured options
@@ -349,11 +351,36 @@ export function getNormalizedConfig(config) {
349
351
  const keyPrefix = config.keyPrefix || "neurolink:conversation:";
350
352
  // Intelligent default: derive user sessions prefix from conversation prefix
351
353
  const defaultUserSessionsPrefix = keyPrefix.replace(/conversation:?$/, "user:sessions:");
354
+ let host = config.host || "localhost";
355
+ let port = config.port || 6379;
356
+ let password = config.password || "";
357
+ let db = config.db || 0;
358
+ let url = config.url;
359
+ if (url) {
360
+ try {
361
+ const parsedUrl = new URL(url);
362
+ host = parsedUrl.hostname;
363
+ port = parsedUrl.port ? parseInt(parsedUrl.port) : 6379;
364
+ password = parsedUrl.password || password;
365
+ db = parsedUrl.pathname
366
+ ? parseInt(parsedUrl.pathname.replace("/", "")) || 0
367
+ : 0;
368
+ }
369
+ catch (e) {
370
+ const sanitizedUrl = url.replace(/:\/\/[^:]+:[^@]+@/, "://[redacted]@");
371
+ logger.warn("[redisUtils] Failed to parse Redis URL, falling back to component-based connection", {
372
+ url: sanitizedUrl,
373
+ error: e instanceof Error ? e.message : String(e),
374
+ });
375
+ url = undefined;
376
+ }
377
+ }
352
378
  return {
353
- host: config.host || "localhost",
354
- port: config.port || 6379,
355
- password: config.password || "",
356
- db: config.db || 0,
379
+ url: url || "",
380
+ host,
381
+ port,
382
+ password,
383
+ db,
357
384
  keyPrefix,
358
385
  userSessionsKeyPrefix: config.userSessionsKeyPrefix || defaultUserSessionsPrefix,
359
386
  ttl: config.ttl || 86400,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "9.48.3",
3
+ "version": "9.50.0",
4
4
  "packageManager": "pnpm@10.15.1",
5
5
  "description": "Universal AI Development Platform with working MCP integration, multi-provider support, and professional CLI. Built-in tools operational, 58+ external MCP servers discoverable. Connect to filesystem, GitHub, database operations, and more. Build, test, and deploy AI applications with 13 providers: OpenAI, Anthropic, Google AI, AWS Bedrock, Azure, Hugging Face, Ollama, and Mistral AI.",
6
6
  "author": {