diff --git a/plugin/scripts/worker-service.cjs b/plugin/scripts/worker-service.cjs index 4909670f..4f04a6fc 100755 --- a/plugin/scripts/worker-service.cjs +++ b/plugin/scripts/worker-service.cjs @@ -974,7 +974,7 @@ ${n.prompts.header_memory_continued}`}Dr();kr();ha();var dm=require("path"),uT=r `):typeof l=="string"?l:"",p=d.length,m=e.cumulativeInputTokens+e.cumulativeOutputTokens,g=u.message.usage;g&&(e.cumulativeInputTokens+=g.input_tokens||0,e.cumulativeOutputTokens+=g.output_tokens||0,g.cache_creation_input_tokens&&(e.cumulativeInputTokens+=g.cache_creation_input_tokens),q.debug("SDK","Token usage captured",{sessionId:e.sessionDbId,inputTokens:g.input_tokens,outputTokens:g.output_tokens,cacheCreation:g.cache_creation_input_tokens||0,cacheRead:g.cache_read_input_tokens||0,cumulativeInput:e.cumulativeInputTokens,cumulativeOutput:e.cumulativeOutputTokens}));let b=e.cumulativeInputTokens+e.cumulativeOutputTokens-m,f=e.earliestPendingTimestamp;if(p>0){let h=p>100?d.substring(0,100)+"...":d;q.dataOut("SDK",`Response received (${p} chars)`,{sessionId:e.sessionDbId,promptNumber:e.lastPromptNumber},h),await this.processSDKResponse(e,d,r,b,f)}else await this.markMessagesProcessed(e,r)}u.type==="result"&&u.subtype}let c=Date.now()-e.startTime;q.success("SDK","Agent completed",{sessionId:e.sessionDbId,duration:`${(c/1e3).toFixed(1)}s`})}catch(n){throw n.name==="AbortError"?q.warn("SDK","Agent aborted",{sessionId:e.sessionDbId}):q.failure("SDK","Agent error",{sessionDbId:e.sessionDbId},n),n}finally{this.sessionManager.deleteSession(e.sessionDbId).catch(()=>{})}}async*createMessageGenerator(e){let r=ht.getInstance().getActiveMode(),n=e.lastPromptNumber===1?Bu(e.project,e.claudeSessionId,e.userPrompt,r):Zu(e.userPrompt,e.lastPromptNumber,e.claudeSessionId,r);e.conversationHistory.push({role:"user",content:n}),yield{type:"user",message:{role:"user",content:n},session_id:e.claudeSessionId,parent_tool_use_id:null,isSynthetic:!0};for await(let a of this.sessionManager.getMessageIterator(e.sessionDbId))if(a.type==="observation"){a.prompt_number!==void 0&&(e.lastPromptNumber=a.prompt_number);let s=Vu({id:0,tool_name:a.tool_name,tool_input:JSON.stringify(a.tool_input),tool_output:JSON.stringify(a.tool_response),created_at_epoch:Date.now(),cwd:a.cwd});e.conversationHistory.push({role:"user",content:s}),yield{type:"user",message:{role:"user",content:s},session_id:e.claudeSessionId,parent_tool_use_id:null,isSynthetic:!0}}else if(a.type==="summarize"){let s=Gu({id:e.sessionDbId,sdk_session_id:e.sdkSessionId,project:e.project,user_prompt:e.userPrompt,last_user_message:a.last_user_message||"",last_assistant_message:a.last_assistant_message||""},r);e.conversationHistory.push({role:"user",content:s}),yield{type:"user",message:{role:"user",content:s},session_id:e.claudeSessionId,parent_tool_use_id:null,isSynthetic:!0}}}async processSDKResponse(e,r,n,a,s){r&&e.conversationHistory.push({role:"assistant",content:r});let i=zu(r,e.claudeSessionId);for(let c of i){let{id:u,createdAtEpoch:l}=this.dbManager.getSessionStore().storeObservation(e.claudeSessionId,e.project,c,e.lastPromptNumber,a,s??void 0);q.info("SDK","Observation saved",{sessionId:e.sessionDbId,obsId:u,type:c.type,title:c.title||"(untitled)",filesRead:c.files_read?.length??0,filesModified:c.files_modified?.length??0,concepts:c.concepts?.length??0});let d=Date.now(),p=c.type,m=c.title||"(untitled)";this.dbManager.getChromaSync().syncObservation(u,e.claudeSessionId,e.project,c,e.lastPromptNumber,l,a).then(()=>{let g=Date.now()-d;q.debug("CHROMA","Observation synced",{obsId:u,duration:`${g}ms`,type:p,title:m})}).catch(g=>{q.warn("CHROMA","Observation sync failed, continuing without vector search",{obsId:u,type:p,title:m},g)}),n&&n.sseBroadcaster&&n.sseBroadcaster.broadcast({type:"new_observation",observation:{id:u,sdk_session_id:e.sdkSessionId,session_id:e.claudeSessionId,type:c.type,title:c.title,subtitle:c.subtitle,text:c.text||null,narrative:c.narrative||null,facts:JSON.stringify(c.facts||[]),concepts:JSON.stringify(c.concepts||[]),files_read:JSON.stringify(c.files||[]),files_modified:JSON.stringify([]),project:e.project,prompt_number:e.lastPromptNumber,created_at_epoch:l}})}let o=Hu(r,e.sessionDbId);if(o){let{id:c,createdAtEpoch:u}=this.dbManager.getSessionStore().storeSummary(e.claudeSessionId,e.project,o,e.lastPromptNumber,a,s??void 0);q.info("SDK","Summary saved",{sessionId:e.sessionDbId,summaryId:c,request:o.request||"(no request)",hasCompleted:!!o.completed,hasNextSteps:!!o.next_steps});let l=Date.now(),d=o.request||"(no request)";this.dbManager.getChromaSync().syncSummary(c,e.claudeSessionId,e.project,o,e.lastPromptNumber,u,a).then(()=>{let p=Date.now()-l;q.debug("CHROMA","Summary synced",{summaryId:c,duration:`${p}ms`,request:d})}).catch(p=>{q.warn("CHROMA","Summary sync failed, continuing without vector search",{summaryId:c,request:d},p)}),n&&n.sseBroadcaster&&n.sseBroadcaster.broadcast({type:"new_summary",summary:{id:c,session_id:e.claudeSessionId,request:o.request,investigated:o.investigated,learned:o.learned,completed:o.completed,next_steps:o.next_steps,notes:o.notes,project:e.project,prompt_number:e.lastPromptNumber,created_at_epoch:u}})}await this.markMessagesProcessed(e,n)}async markMessagesProcessed(e,r){let n=this.sessionManager.getPendingMessageStore();if(e.pendingProcessingIds.size>0){for(let s of e.pendingProcessingIds)n.markProcessed(s);q.debug("SDK","Messages marked as processed",{sessionId:e.sessionDbId,messageIds:Array.from(e.pendingProcessingIds),count:e.pendingProcessingIds.size}),e.pendingProcessingIds.clear(),e.earliestPendingTimestamp=null;let a=n.cleanupProcessed(100);a>0&&q.debug("SDK","Cleaned up old processed messages",{deletedCount:a})}r&&typeof r.broadcastProcessingStatus=="function"&&r.broadcastProcessingStatus()}findClaudeExecutable(){let e=nt.loadFromFile(An);if(e.CLAUDE_CODE_PATH){let{existsSync:r}=require("fs");if(!r(e.CLAUDE_CODE_PATH))throw new Error(`CLAUDE_CODE_PATH is set to "${e.CLAUDE_CODE_PATH}" but the file does not exist.`);return e.CLAUDE_CODE_PATH}try{let r=(0,Gk.execSync)(process.platform==="win32"?"where claude":"which claude",{encoding:"utf8",windowsHide:!0,stdio:["ignore","pipe","ignore"]}).trim().split(` `)[0].trim();if(r)return r}catch(r){q.debug("SDK","Claude executable auto-detection failed",r)}throw new Error(`Claude executable not found. Please either: 1. Add "claude" to your system PATH, or -2. Set CLAUDE_CODE_PATH in ~/.claude-mem/settings.json`)}getModelId(){let e=Wk.default.join((0,Zk.homedir)(),".claude-mem","settings.json");return nt.loadFromFile(e).CLAUDE_MEM_MODEL}};var Ol=St(require("path"),1),Cl=require("os");mt();Dr();ha();var O7="https://generativelanguage.googleapis.com/v1beta/models",C7={"gemini-2.5-flash-lite":10,"gemini-2.5-flash":5,"gemini-3-flash":5},Kk=0;async function I7(t,e){if(e)return;let r=C7[t]||5,n=Math.ceil(6e4/r)+100,s=Date.now()-Kk;if(ssetTimeout(o,i))}Kk=Date.now()}var Pl=class{dbManager;sessionManager;fallbackAgent=null;constructor(e,r){this.dbManager=e,this.sessionManager=r}setFallbackAgent(e){this.fallbackAgent=e}shouldFallbackToClaude(e){let r=e?.message||"";return r.includes("429")||r.includes("500")||r.includes("502")||r.includes("503")||r.includes("ECONNREFUSED")||r.includes("ETIMEDOUT")||r.includes("fetch failed")}async startSession(e,r){try{let{apiKey:n,model:a,billingEnabled:s}=this.getGeminiConfig();if(!n)throw new Error("Gemini API key not configured. Set CLAUDE_MEM_GEMINI_API_KEY in settings or GEMINI_API_KEY environment variable.");let i=ht.getInstance().getActiveMode(),o=e.lastPromptNumber===1?Bu(e.project,e.claudeSessionId,e.userPrompt,i):Zu(e.userPrompt,e.lastPromptNumber,e.claudeSessionId,i);e.conversationHistory.push({role:"user",content:o});let c=await this.queryGeminiMultiTurn(e.conversationHistory,n,a,s);if(c.content){e.conversationHistory.push({role:"assistant",content:c.content});let l=c.tokensUsed||0;e.cumulativeInputTokens+=Math.floor(l*.7),e.cumulativeOutputTokens+=Math.floor(l*.3),await this.processGeminiResponse(e,c.content,r,l)}for await(let l of this.sessionManager.getMessageIterator(e.sessionDbId))if(l.type==="observation"){l.prompt_number!==void 0&&(e.lastPromptNumber=l.prompt_number);let d=Vu({id:0,tool_name:l.tool_name,tool_input:JSON.stringify(l.tool_input),tool_output:JSON.stringify(l.tool_response),created_at_epoch:Date.now(),cwd:l.cwd});e.conversationHistory.push({role:"user",content:d});let p=await this.queryGeminiMultiTurn(e.conversationHistory,n,a,s);if(p.content){e.conversationHistory.push({role:"assistant",content:p.content});let m=p.tokensUsed||0;e.cumulativeInputTokens+=Math.floor(m*.7),e.cumulativeOutputTokens+=Math.floor(m*.3),await this.processGeminiResponse(e,p.content,r,m)}}else if(l.type==="summarize"){let d=Gu({id:e.sessionDbId,sdk_session_id:e.sdkSessionId,project:e.project,user_prompt:e.userPrompt,last_user_message:l.last_user_message||"",last_assistant_message:l.last_assistant_message||""},i);e.conversationHistory.push({role:"user",content:d});let p=await this.queryGeminiMultiTurn(e.conversationHistory,n,a,s);if(p.content){e.conversationHistory.push({role:"assistant",content:p.content});let m=p.tokensUsed||0;e.cumulativeInputTokens+=Math.floor(m*.7),e.cumulativeOutputTokens+=Math.floor(m*.3),await this.processGeminiResponse(e,p.content,r,m)}}let u=Date.now()-e.startTime;q.success("SDK","Gemini agent completed",{sessionId:e.sessionDbId,duration:`${(u/1e3).toFixed(1)}s`,historyLength:e.conversationHistory.length}),this.dbManager.getSessionStore().markSessionCompleted(e.sessionDbId)}catch(n){if(n.name==="AbortError")throw q.warn("SDK","Gemini agent aborted",{sessionId:e.sessionDbId}),n;if(this.shouldFallbackToClaude(n)&&this.fallbackAgent){q.warn("SDK","Gemini API failed, falling back to Claude SDK",{sessionDbId:e.sessionDbId,error:n.message,historyLength:e.conversationHistory.length});let s=this.sessionManager.getPendingMessageStore().resetStuckMessages(0);return s>0&&q.info("SDK","Reset processing messages for fallback",{sessionDbId:e.sessionDbId,resetCount:s}),this.fallbackAgent.startSession(e,r)}throw q.failure("SDK","Gemini agent error",{sessionDbId:e.sessionDbId},n),n}}conversationToGeminiContents(e){return e.map(r=>({role:r.role==="assistant"?"model":"user",parts:[{text:r.content}]}))}async queryGeminiMultiTurn(e,r,n,a){let s=this.conversationToGeminiContents(e),i=e.reduce((p,m)=>p+m.content.length,0);q.debug("SDK",`Querying Gemini multi-turn (${n})`,{turns:e.length,totalChars:i});let o=`${O7}/${n}:generateContent?key=${r}`;await I7(n,a);let c=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({contents:s,generationConfig:{temperature:.3,maxOutputTokens:4096}})});if(!c.ok){let p=await c.text();throw new Error(`Gemini API error: ${c.status} - ${p}`)}let u=await c.json();if(!u.candidates?.[0]?.content?.parts?.[0]?.text)return q.warn("SDK","Empty response from Gemini"),{content:""};let l=u.candidates[0].content.parts[0].text,d=u.usageMetadata?.totalTokenCount;return{content:l,tokensUsed:d}}async processGeminiResponse(e,r,n,a){let s=zu(r,e.claudeSessionId);for(let o of s){let{id:c,createdAtEpoch:u}=this.dbManager.getSessionStore().storeObservation(e.claudeSessionId,e.project,o,e.lastPromptNumber,a);q.info("SDK","Gemini observation saved",{sessionId:e.sessionDbId,obsId:c,type:o.type,title:o.title||"(untitled)"}),this.dbManager.getChromaSync().syncObservation(c,e.claudeSessionId,e.project,o,e.lastPromptNumber,u,a).catch(l=>{q.warn("SDK","Gemini chroma sync failed",{obsId:c},l)}),n&&n.sseBroadcaster&&n.sseBroadcaster.broadcast({type:"new_observation",observation:{id:c,sdk_session_id:e.sdkSessionId,session_id:e.claudeSessionId,type:o.type,title:o.title,subtitle:o.subtitle,text:null,narrative:o.narrative||null,facts:JSON.stringify(o.facts||[]),concepts:JSON.stringify(o.concepts||[]),files_read:JSON.stringify(o.files_read||[]),files_modified:JSON.stringify(o.files_modified||[]),project:e.project,prompt_number:e.lastPromptNumber,created_at_epoch:u}})}let i=Hu(r,e.sessionDbId);if(i){let o={request:i.request||"",investigated:i.investigated||"",learned:i.learned||"",completed:i.completed||"",next_steps:i.next_steps||"",notes:i.notes},{id:c,createdAtEpoch:u}=this.dbManager.getSessionStore().storeSummary(e.claudeSessionId,e.project,o,e.lastPromptNumber,a);q.info("SDK","Gemini summary saved",{sessionId:e.sessionDbId,summaryId:c,request:i.request||"(no request)"}),this.dbManager.getChromaSync().syncSummary(c,e.claudeSessionId,e.project,o,e.lastPromptNumber,u,a).catch(l=>{q.warn("SDK","Gemini chroma sync failed",{summaryId:c},l)}),n&&n.sseBroadcaster&&n.sseBroadcaster.broadcast({type:"new_summary",summary:{id:c,session_id:e.claudeSessionId,request:i.request,investigated:i.investigated,learned:i.learned,completed:i.completed,next_steps:i.next_steps,notes:i.notes,project:e.project,prompt_number:e.lastPromptNumber,created_at_epoch:u}})}await this.markMessagesProcessed(e,n)}async markMessagesProcessed(e,r){let n=this.sessionManager.getPendingMessageStore();if(e.pendingProcessingIds.size>0){for(let s of e.pendingProcessingIds)n.markProcessed(s);q.debug("SDK","Gemini messages marked as processed",{sessionId:e.sessionDbId,count:e.pendingProcessingIds.size}),e.pendingProcessingIds.clear();let a=n.cleanupProcessed(100);a>0&&q.debug("SDK","Gemini cleaned up old processed messages",{deletedCount:a})}r&&typeof r.broadcastProcessingStatus=="function"&&r.broadcastProcessingStatus()}getGeminiConfig(){let e=Ol.default.join((0,Cl.homedir)(),".claude-mem","settings.json"),r=nt.loadFromFile(e),n=r.CLAUDE_MEM_GEMINI_API_KEY||process.env.GEMINI_API_KEY||"",a=r.CLAUDE_MEM_GEMINI_MODEL||"gemini-2.5-flash-lite",s=r.CLAUDE_MEM_GEMINI_BILLING_ENABLED==="true";return{apiKey:n,model:a,billingEnabled:s}}};function Ym(){let t=Ol.default.join((0,Cl.homedir)(),".claude-mem","settings.json");return!!(nt.loadFromFile(t).CLAUDE_MEM_GEMINI_API_KEY||process.env.GEMINI_API_KEY)}function Qm(){let t=Ol.default.join((0,Cl.homedir)(),".claude-mem","settings.json");return nt.loadFromFile(t).CLAUDE_MEM_PROVIDER==="gemini"}var Il=class{dbManager;constructor(e){this.dbManager=e}stripProjectPath(e,r){let n=`/${r}/`,a=e.indexOf(n);return a!==-1?e.substring(a+n.length):e}stripProjectPaths(e,r){if(!e)return e;try{let a=JSON.parse(e).map(s=>this.stripProjectPath(s,r));return JSON.stringify(a)}catch{return e}}sanitizeObservation(e){return{...e,files_read:this.stripProjectPaths(e.files_read,e.project),files_modified:this.stripProjectPaths(e.files_modified,e.project)}}getObservations(e,r,n){let a=this.paginate("observations","id, sdk_session_id, project, type, title, subtitle, narrative, text, facts, concepts, files_read, files_modified, prompt_number, created_at, created_at_epoch",e,r,n);return{...a,items:a.items.map(s=>this.sanitizeObservation(s))}}getSummaries(e,r,n){let a=this.dbManager.getSessionStore().db,s=` +2. Set CLAUDE_CODE_PATH in ~/.claude-mem/settings.json`)}getModelId(){let e=Wk.default.join((0,Zk.homedir)(),".claude-mem","settings.json");return nt.loadFromFile(e).CLAUDE_MEM_MODEL}};var Ol=St(require("path"),1),Cl=require("os");mt();Dr();ha();var O7="https://generativelanguage.googleapis.com/v1beta/models",C7={"gemini-2.5-flash-lite":10,"gemini-2.5-flash":5,"gemini-3-flash":5},Kk=0;async function I7(t,e){if(e)return;let r=C7[t]||5,n=Math.ceil(6e4/r)+100,s=Date.now()-Kk;if(ssetTimeout(o,i))}Kk=Date.now()}var Pl=class{dbManager;sessionManager;fallbackAgent=null;constructor(e,r){this.dbManager=e,this.sessionManager=r}setFallbackAgent(e){this.fallbackAgent=e}shouldFallbackToClaude(e){let r=e?.message||"";return r.includes("429")||r.includes("500")||r.includes("502")||r.includes("503")||r.includes("ECONNREFUSED")||r.includes("ETIMEDOUT")||r.includes("fetch failed")}async startSession(e,r){try{let{apiKey:n,model:a,billingEnabled:s}=this.getGeminiConfig();if(!n)throw new Error("Gemini API key not configured. Set CLAUDE_MEM_GEMINI_API_KEY in settings or GEMINI_API_KEY environment variable.");let i=ht.getInstance().getActiveMode(),o=e.lastPromptNumber===1?Bu(e.project,e.claudeSessionId,e.userPrompt,i):Zu(e.userPrompt,e.lastPromptNumber,e.claudeSessionId,i);e.conversationHistory.push({role:"user",content:o});let c=await this.queryGeminiMultiTurn(e.conversationHistory,n,a,s);if(c.content){e.conversationHistory.push({role:"assistant",content:c.content});let l=c.tokensUsed||0;e.cumulativeInputTokens+=Math.floor(l*.7),e.cumulativeOutputTokens+=Math.floor(l*.3),await this.processGeminiResponse(e,c.content,r,l)}for await(let l of this.sessionManager.getMessageIterator(e.sessionDbId))if(l.type==="observation"){l.prompt_number!==void 0&&(e.lastPromptNumber=l.prompt_number);let d=Vu({id:0,tool_name:l.tool_name,tool_input:JSON.stringify(l.tool_input),tool_output:JSON.stringify(l.tool_response),created_at_epoch:Date.now(),cwd:l.cwd});e.conversationHistory.push({role:"user",content:d});let p=await this.queryGeminiMultiTurn(e.conversationHistory,n,a,s);if(p.content){e.conversationHistory.push({role:"assistant",content:p.content});let m=p.tokensUsed||0;e.cumulativeInputTokens+=Math.floor(m*.7),e.cumulativeOutputTokens+=Math.floor(m*.3),await this.processGeminiResponse(e,p.content,r,m)}else q.warn("SDK","Empty Gemini response for observation, marking as processed",{sessionId:e.sessionDbId,toolName:l.tool_name}),await this.markMessagesProcessed(e,r)}else if(l.type==="summarize"){let d=Gu({id:e.sessionDbId,sdk_session_id:e.sdkSessionId,project:e.project,user_prompt:e.userPrompt,last_user_message:l.last_user_message||"",last_assistant_message:l.last_assistant_message||""},i);e.conversationHistory.push({role:"user",content:d});let p=await this.queryGeminiMultiTurn(e.conversationHistory,n,a,s);if(p.content){e.conversationHistory.push({role:"assistant",content:p.content});let m=p.tokensUsed||0;e.cumulativeInputTokens+=Math.floor(m*.7),e.cumulativeOutputTokens+=Math.floor(m*.3),await this.processGeminiResponse(e,p.content,r,m)}else q.warn("SDK","Empty Gemini response for summary, marking as processed",{sessionId:e.sessionDbId}),await this.markMessagesProcessed(e,r)}let u=Date.now()-e.startTime;q.success("SDK","Gemini agent completed",{sessionId:e.sessionDbId,duration:`${(u/1e3).toFixed(1)}s`,historyLength:e.conversationHistory.length}),this.dbManager.getSessionStore().markSessionCompleted(e.sessionDbId)}catch(n){if(n.name==="AbortError")throw q.warn("SDK","Gemini agent aborted",{sessionId:e.sessionDbId}),n;if(this.shouldFallbackToClaude(n)&&this.fallbackAgent){q.warn("SDK","Gemini API failed, falling back to Claude SDK",{sessionDbId:e.sessionDbId,error:n.message,historyLength:e.conversationHistory.length});let s=this.sessionManager.getPendingMessageStore().resetStuckMessages(0);return s>0&&q.info("SDK","Reset processing messages for fallback",{sessionDbId:e.sessionDbId,resetCount:s}),this.fallbackAgent.startSession(e,r)}throw q.failure("SDK","Gemini agent error",{sessionDbId:e.sessionDbId},n),n}}conversationToGeminiContents(e){return e.map(r=>({role:r.role==="assistant"?"model":"user",parts:[{text:r.content}]}))}async queryGeminiMultiTurn(e,r,n,a){let s=this.conversationToGeminiContents(e),i=e.reduce((p,m)=>p+m.content.length,0);q.debug("SDK",`Querying Gemini multi-turn (${n})`,{turns:e.length,totalChars:i});let o=`${O7}/${n}:generateContent?key=${r}`;await I7(n,a);let c=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({contents:s,generationConfig:{temperature:.3,maxOutputTokens:4096}})});if(!c.ok){let p=await c.text();throw new Error(`Gemini API error: ${c.status} - ${p}`)}let u=await c.json();if(!u.candidates?.[0]?.content?.parts?.[0]?.text)return q.warn("SDK","Empty response from Gemini"),{content:""};let l=u.candidates[0].content.parts[0].text,d=u.usageMetadata?.totalTokenCount;return{content:l,tokensUsed:d}}async processGeminiResponse(e,r,n,a){let s=zu(r,e.claudeSessionId);for(let o of s){let{id:c,createdAtEpoch:u}=this.dbManager.getSessionStore().storeObservation(e.claudeSessionId,e.project,o,e.lastPromptNumber,a);q.info("SDK","Gemini observation saved",{sessionId:e.sessionDbId,obsId:c,type:o.type,title:o.title||"(untitled)"}),this.dbManager.getChromaSync().syncObservation(c,e.claudeSessionId,e.project,o,e.lastPromptNumber,u,a).catch(l=>{q.warn("SDK","Gemini chroma sync failed",{obsId:c},l)}),n&&n.sseBroadcaster&&n.sseBroadcaster.broadcast({type:"new_observation",observation:{id:c,sdk_session_id:e.sdkSessionId,session_id:e.claudeSessionId,type:o.type,title:o.title,subtitle:o.subtitle,text:null,narrative:o.narrative||null,facts:JSON.stringify(o.facts||[]),concepts:JSON.stringify(o.concepts||[]),files_read:JSON.stringify(o.files_read||[]),files_modified:JSON.stringify(o.files_modified||[]),project:e.project,prompt_number:e.lastPromptNumber,created_at_epoch:u}})}let i=Hu(r,e.sessionDbId);if(i){let o={request:i.request||"",investigated:i.investigated||"",learned:i.learned||"",completed:i.completed||"",next_steps:i.next_steps||"",notes:i.notes},{id:c,createdAtEpoch:u}=this.dbManager.getSessionStore().storeSummary(e.claudeSessionId,e.project,o,e.lastPromptNumber,a);q.info("SDK","Gemini summary saved",{sessionId:e.sessionDbId,summaryId:c,request:i.request||"(no request)"}),this.dbManager.getChromaSync().syncSummary(c,e.claudeSessionId,e.project,o,e.lastPromptNumber,u,a).catch(l=>{q.warn("SDK","Gemini chroma sync failed",{summaryId:c},l)}),n&&n.sseBroadcaster&&n.sseBroadcaster.broadcast({type:"new_summary",summary:{id:c,session_id:e.claudeSessionId,request:i.request,investigated:i.investigated,learned:i.learned,completed:i.completed,next_steps:i.next_steps,notes:i.notes,project:e.project,prompt_number:e.lastPromptNumber,created_at_epoch:u}})}await this.markMessagesProcessed(e,n)}async markMessagesProcessed(e,r){let n=this.sessionManager.getPendingMessageStore();if(e.pendingProcessingIds.size>0){for(let s of e.pendingProcessingIds)n.markProcessed(s);q.debug("SDK","Gemini messages marked as processed",{sessionId:e.sessionDbId,count:e.pendingProcessingIds.size}),e.pendingProcessingIds.clear();let a=n.cleanupProcessed(100);a>0&&q.debug("SDK","Gemini cleaned up old processed messages",{deletedCount:a})}r&&typeof r.broadcastProcessingStatus=="function"&&r.broadcastProcessingStatus()}getGeminiConfig(){let e=Ol.default.join((0,Cl.homedir)(),".claude-mem","settings.json"),r=nt.loadFromFile(e),n=r.CLAUDE_MEM_GEMINI_API_KEY||process.env.GEMINI_API_KEY||"",a=r.CLAUDE_MEM_GEMINI_MODEL||"gemini-2.5-flash-lite",s=r.CLAUDE_MEM_GEMINI_BILLING_ENABLED==="true";return{apiKey:n,model:a,billingEnabled:s}}};function Ym(){let t=Ol.default.join((0,Cl.homedir)(),".claude-mem","settings.json");return!!(nt.loadFromFile(t).CLAUDE_MEM_GEMINI_API_KEY||process.env.GEMINI_API_KEY)}function Qm(){let t=Ol.default.join((0,Cl.homedir)(),".claude-mem","settings.json");return nt.loadFromFile(t).CLAUDE_MEM_PROVIDER==="gemini"}var Il=class{dbManager;constructor(e){this.dbManager=e}stripProjectPath(e,r){let n=`/${r}/`,a=e.indexOf(n);return a!==-1?e.substring(a+n.length):e}stripProjectPaths(e,r){if(!e)return e;try{let a=JSON.parse(e).map(s=>this.stripProjectPath(s,r));return JSON.stringify(a)}catch{return e}}sanitizeObservation(e){return{...e,files_read:this.stripProjectPaths(e.files_read,e.project),files_modified:this.stripProjectPaths(e.files_modified,e.project)}}getObservations(e,r,n){let a=this.paginate("observations","id, sdk_session_id, project, type, title, subtitle, narrative, text, facts, concepts, files_read, files_modified, prompt_number, created_at, created_at_epoch",e,r,n);return{...a,items:a.items.map(s=>this.sanitizeObservation(s))}}getSummaries(e,r,n){let a=this.dbManager.getSessionStore().db,s=` SELECT ss.id, s.claude_session_id as session_id, diff --git a/src/services/worker/GeminiAgent.ts b/src/services/worker/GeminiAgent.ts index b2707c51..1215718c 100644 --- a/src/services/worker/GeminiAgent.ts +++ b/src/services/worker/GeminiAgent.ts @@ -196,6 +196,13 @@ export class GeminiAgent { session.cumulativeInputTokens += Math.floor(tokensUsed * 0.7); session.cumulativeOutputTokens += Math.floor(tokensUsed * 0.3); await this.processGeminiResponse(session, obsResponse.content, worker, tokensUsed); + } else { + // Empty response - still mark messages as processed to avoid stuck state + logger.warn('SDK', 'Empty Gemini response for observation, marking as processed', { + sessionId: session.sessionDbId, + toolName: message.tool_name + }); + await this.markMessagesProcessed(session, worker); } } else if (message.type === 'summarize') { @@ -221,6 +228,12 @@ export class GeminiAgent { session.cumulativeInputTokens += Math.floor(tokensUsed * 0.7); session.cumulativeOutputTokens += Math.floor(tokensUsed * 0.3); await this.processGeminiResponse(session, summaryResponse.content, worker, tokensUsed); + } else { + // Empty response - still mark messages as processed to avoid stuck state + logger.warn('SDK', 'Empty Gemini response for summary, marking as processed', { + sessionId: session.sessionDbId + }); + await this.markMessagesProcessed(session, worker); } } }