diff --git a/plugin/scripts/save-hook.js b/plugin/scripts/save-hook.js index 5218af6c..4ef6362c 100755 --- a/plugin/scripts/save-hook.js +++ b/plugin/scripts/save-hook.js @@ -400,4 +400,4 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje JOIN sdk_sessions s ON up.claude_session_id = s.claude_session_id WHERE up.created_at_epoch >= ? AND up.created_at_epoch <= ? ${o.replace("project","s.project")} ORDER BY up.created_at_epoch ASC - `;try{let T=this.db.prepare(c).all(a,_,...i),g=this.db.prepare(m).all(a,_,...i),u=this.db.prepare(S).all(a,_,...i);return{observations:T,sessions:g.map(d=>({id:d.id,sdk_session_id:d.sdk_session_id,project:d.project,request:d.request,completed:d.completed,next_steps:d.next_steps,created_at:d.created_at,created_at_epoch:d.created_at_epoch})),prompts:u.map(d=>({id:d.id,claude_session_id:d.claude_session_id,project:d.project,prompt:d.prompt_text,created_at:d.created_at,created_at_epoch:d.created_at_epoch}))}}catch(T){return console.error("[SessionStore] Error querying timeline records:",T.message),{observations:[],sessions:[],prompts:[]}}}close(){this.db.close()}};function $(p,e,s){return p==="PreCompact"?e?{continue:!0,suppressOutput:!0}:{continue:!1,stopReason:s.reason||"Pre-compact operation failed",suppressOutput:!0}:p==="SessionStart"?e&&s.context?{continue:!0,suppressOutput:!0,hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:s.context}}:{continue:!0,suppressOutput:!0}:p==="UserPromptSubmit"||p==="PostToolUse"?{continue:!0,suppressOutput:!0}:p==="Stop"?{continue:!0,suppressOutput:!0}:{continue:e,suppressOutput:!0,...s.reason&&!e?{stopReason:s.reason}:{}}}function f(p,e,s={}){let t=$(p,e,s);return JSON.stringify(t)}import y from"path";import{spawn as D}from"child_process";var W=parseInt(process.env.CLAUDE_MEM_WORKER_PORT||"37777",10);async function k(p=100){try{return(await fetch(`http://127.0.0.1:${W}/health`,{signal:AbortSignal.timeout(p)})).ok}catch{return!1}}async function G(p=1e4){let e=Date.now(),s=100;for(;Date.now()-esetTimeout(t,s))}return!1}async function x(){if(await k())return;let p=v(),e=y.join(p,"node_modules",".bin","pm2"),s=y.join(p,"ecosystem.config.cjs"),t=D(e,["list","--no-color"],{cwd:p,stdio:["ignore","pipe","ignore"]}),r="";if(t.stdout?.on("data",i=>{r+=i.toString()}),await new Promise((i,a)=>{t.on("error",_=>a(_)),t.on("close",_=>{i()})}),!(r.includes("claude-mem-worker")&&r.includes("online"))){let i=D(e,["start",s],{cwd:p,stdio:"ignore"});await new Promise((a,_)=>{i.on("error",c=>_(c)),i.on("close",c=>{c!==0&&c!==null?_(new Error(`PM2 start command failed with exit code ${c}`)):a()})})}if(!await G(1e4))throw new Error("Worker failed to become healthy after starting")}var Y=new Set(["ListMcpResourcesTool"]);async function K(p){if(!p)throw new Error("saveHook requires input");let{session_id:e,tool_name:s,tool_input:t,tool_output:r}=p;if(Y.has(s)){console.log(f("PostToolUse",!0));return}await x();let n=new R,o=n.createSDKSession(e,"",""),i=n.getPromptCounter(o);n.close();let a=b.formatTool(s,t),_=parseInt(process.env.CLAUDE_MEM_WORKER_PORT||"37777",10);b.dataIn("HOOK",`PostToolUse: ${a}`,{sessionId:o,workerPort:_});try{let c=await fetch(`http://127.0.0.1:${_}/sessions/${o}/observations`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({tool_name:s,tool_input:t!==void 0?JSON.stringify(t):"{}",tool_output:r!==void 0?JSON.stringify(r):"{}",prompt_number:i}),signal:AbortSignal.timeout(2e3)});if(!c.ok){let m=await c.text();throw b.failure("HOOK","Failed to send observation",{sessionId:o,status:c.status},m),new Error(`Failed to send observation to worker: ${c.status} ${m}`)}b.debug("HOOK","Observation sent successfully",{sessionId:o,toolName:s})}catch(c){throw c.cause?.code==="ECONNREFUSED"||c.name==="TimeoutError"||c.message.includes("fetch failed")?new Error("There's a problem with the worker. If you just updated, type `pm2 restart claude-mem-worker` in your terminal to continue"):c}console.log(f("PostToolUse",!0))}var I="";U.on("data",p=>I+=p);U.on("end",async()=>{let p=I?JSON.parse(I):void 0;await K(p)}); + `;try{let T=this.db.prepare(c).all(a,_,...i),g=this.db.prepare(m).all(a,_,...i),u=this.db.prepare(S).all(a,_,...i);return{observations:T,sessions:g.map(d=>({id:d.id,sdk_session_id:d.sdk_session_id,project:d.project,request:d.request,completed:d.completed,next_steps:d.next_steps,created_at:d.created_at,created_at_epoch:d.created_at_epoch})),prompts:u.map(d=>({id:d.id,claude_session_id:d.claude_session_id,project:d.project,prompt:d.prompt_text,created_at:d.created_at,created_at_epoch:d.created_at_epoch}))}}catch(T){return console.error("[SessionStore] Error querying timeline records:",T.message),{observations:[],sessions:[],prompts:[]}}}close(){this.db.close()}};function $(p,e,s){return p==="PreCompact"?e?{continue:!0,suppressOutput:!0}:{continue:!1,stopReason:s.reason||"Pre-compact operation failed",suppressOutput:!0}:p==="SessionStart"?e&&s.context?{continue:!0,suppressOutput:!0,hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:s.context}}:{continue:!0,suppressOutput:!0}:p==="UserPromptSubmit"||p==="PostToolUse"?{continue:!0,suppressOutput:!0}:p==="Stop"?{continue:!0,suppressOutput:!0}:{continue:e,suppressOutput:!0,...s.reason&&!e?{stopReason:s.reason}:{}}}function f(p,e,s={}){let t=$(p,e,s);return JSON.stringify(t)}import y from"path";import{spawn as D}from"child_process";var W=parseInt(process.env.CLAUDE_MEM_WORKER_PORT||"37777",10);async function k(p=100){try{return(await fetch(`http://127.0.0.1:${W}/health`,{signal:AbortSignal.timeout(p)})).ok}catch{return!1}}async function G(p=1e4){let e=Date.now(),s=100;for(;Date.now()-esetTimeout(t,s))}return!1}async function x(){if(await k())return;let p=v(),e=y.join(p,"node_modules",".bin","pm2"),s=y.join(p,"ecosystem.config.cjs"),t=D(e,["list","--no-color"],{cwd:p,stdio:["ignore","pipe","ignore"]}),r="";if(t.stdout?.on("data",i=>{r+=i.toString()}),await new Promise((i,a)=>{t.on("error",_=>a(_)),t.on("close",_=>{i()})}),!(r.includes("claude-mem-worker")&&r.includes("online"))){let i=D(e,["start",s],{cwd:p,stdio:"ignore"});await new Promise((a,_)=>{i.on("error",c=>_(c)),i.on("close",c=>{c!==0&&c!==null?_(new Error(`PM2 start command failed with exit code ${c}`)):a()})})}if(!await G(1e4))throw new Error("Worker failed to become healthy after starting")}var Y=new Set(["ListMcpResourcesTool"]);async function K(p){if(!p)throw new Error("saveHook requires input");let{session_id:e,tool_name:s,tool_input:t,tool_response:r}=p;if(Y.has(s)){console.log(f("PostToolUse",!0));return}await x();let n=new R,o=n.createSDKSession(e,"",""),i=n.getPromptCounter(o);n.close();let a=b.formatTool(s,t),_=parseInt(process.env.CLAUDE_MEM_WORKER_PORT||"37777",10);b.dataIn("HOOK",`PostToolUse: ${a}`,{sessionId:o,workerPort:_});try{let c=await fetch(`http://127.0.0.1:${_}/sessions/${o}/observations`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({tool_name:s,tool_input:t!==void 0?JSON.stringify(t):"{}",tool_response:r!==void 0?JSON.stringify(r):"{}",prompt_number:i}),signal:AbortSignal.timeout(2e3)});if(!c.ok){let m=await c.text();throw b.failure("HOOK","Failed to send observation",{sessionId:o,status:c.status},m),new Error(`Failed to send observation to worker: ${c.status} ${m}`)}b.debug("HOOK","Observation sent successfully",{sessionId:o,toolName:s})}catch(c){throw c.cause?.code==="ECONNREFUSED"||c.name==="TimeoutError"||c.message.includes("fetch failed")?new Error("There's a problem with the worker. If you just updated, type `pm2 restart claude-mem-worker` in your terminal to continue"):c}console.log(f("PostToolUse",!0))}var I="";U.on("data",p=>I+=p);U.on("end",async()=>{let p=I?JSON.parse(I):void 0;await K(p)}); diff --git a/plugin/scripts/worker-service.cjs b/plugin/scripts/worker-service.cjs index cd2d3f64..f774e13d 100755 --- a/plugin/scripts/worker-service.cjs +++ b/plugin/scripts/worker-service.cjs @@ -648,8 +648,8 @@ IMPORTANT: This is not the end of the session. You will receive more requests to WHERE up.claude_session_id = ? ORDER BY up.created_at_epoch DESC LIMIT 1 - `).get(l);n.close(),u&&this.broadcastSSE({type:"new_prompt",prompt:{id:u.id,claude_session_id:u.claude_session_id,project:u.project,prompt_number:u.prompt_number,prompt_text:u.prompt_text,created_at_epoch:u.created_at_epoch}}),u&&this.chromaSync.syncUserPrompt(u.id,u.sdk_session_id,u.project,u.prompt_text,u.prompt_number,u.created_at_epoch).catch(p=>{Y.failure("WORKER","Failed to sync user_prompt to Chroma - continuing",{promptId:u.id},p)}),c.generatorPromise=this.runSDKAgent(c).catch(p=>{Y.failure("WORKER","SDK agent error",{sessionId:r},p);let f=new pr;f.markSessionFailed(r),f.close(),this.sessions.delete(r)}),Y.success("WORKER","Session initialized",{sessionId:r,port:this.port}),a.json({status:"initialized",sessionDbId:r,port:this.port})}handleObservation(e,a){let r=parseInt(e.params.sessionDbId,10),{tool_name:s,tool_input:i,tool_output:n,prompt_number:o}=e.body,l=this.sessions.get(r);if(!l){let u=new pr,p=u.getSessionById(r);u.close(),l={sessionDbId:r,claudeSessionId:p.claude_session_id,sdkSessionId:null,project:p.project,userPrompt:p.user_prompt,pendingMessages:[],abortController:new AbortController,generatorPromise:null,lastPromptNumber:0,startTime:Date.now()},this.sessions.set(r,l),l.generatorPromise=this.runSDKAgent(l).catch(f=>{Y.failure("WORKER","SDK agent error",{sessionId:r},f);let d=new pr;d.markSessionFailed(r),d.close(),this.sessions.delete(r)})}let c=Y.formatTool(s,i);Y.dataIn("WORKER",`Observation queued: ${c}`,{sessionId:r,queue:l.pendingMessages.length+1}),l.pendingMessages.push({type:"observation",tool_name:s,tool_input:i,tool_output:n,prompt_number:o}),a.json({status:"queued",queueLength:l.pendingMessages.length})}handleSummarize(e,a){let r=parseInt(e.params.sessionDbId,10),{prompt_number:s}=e.body,i=this.sessions.get(r);if(!i){let n=new pr,o=n.getSessionById(r);n.close(),i={sessionDbId:r,claudeSessionId:o.claude_session_id,sdkSessionId:null,project:o.project,userPrompt:o.user_prompt,pendingMessages:[],abortController:new AbortController,generatorPromise:null,lastPromptNumber:0,startTime:Date.now()},this.sessions.set(r,i),i.generatorPromise=this.runSDKAgent(i).catch(l=>{Y.failure("WORKER","SDK agent error",{sessionId:r},l);let c=new pr;c.markSessionFailed(r),c.close(),this.sessions.delete(r)})}Y.dataIn("WORKER","Summary requested",{sessionId:r,promptNumber:s,queue:i.pendingMessages.length+1}),i.pendingMessages.push({type:"summarize",prompt_number:s}),this.broadcastProcessingStatus(i.claudeSessionId,!0),a.json({status:"queued",queueLength:i.pendingMessages.length})}handleStatus(e,a){let r=parseInt(e.params.sessionDbId,10),s=this.sessions.get(r);if(!s){a.status(404).json({error:"Session not found"});return}a.json({sessionDbId:r,sdkSessionId:s.sdkSessionId,project:s.project,pendingMessages:s.pendingMessages.length})}async handleDelete(e,a){let r=parseInt(e.params.sessionDbId,10),s=this.sessions.get(r);if(!s){a.status(404).json({error:"Session not found"});return}Y.warn("WORKER","Session delete requested",{sessionId:r}),s.abortController.abort(),s.generatorPromise&&await Promise.race([s.generatorPromise,new Promise(n=>setTimeout(n,5e3))]);let i=new pr;i.markSessionFailed(r),i.close(),this.sessions.delete(r),Y.info("WORKER","Session deleted",{sessionId:r}),a.json({status:"deleted"})}async runSDKAgent(e){Y.info("SDK","Agent starting",{sessionId:e.sessionDbId});let a=K5();Y.info("SDK",`Using Claude executable: ${a}`,{sessionId:e.sessionDbId});try{let r=$b({prompt:this.createMessageGenerator(e),options:{model:W5,disallowedTools:Q5,abortController:e.abortController,pathToClaudeCodeExecutable:a}});for await(let n of r){if(n.type==="assistant"){let o=n.message.content,l=Array.isArray(o)?o.filter(u=>u.type==="text").map(u=>u.text).join(` -`):typeof o=="string"?o:"",c=l.length;Y.dataOut("SDK",`Response received (${c} chars)`,{sessionId:e.sessionDbId,promptNumber:e.lastPromptNumber}),Y.debug("SDK","Full response",{sessionId:e.sessionDbId},l),this.handleAgentMessage(e,l,e.lastPromptNumber)}n.type==="result"&&n.subtype}let s=Date.now()-e.startTime;Y.success("SDK","Agent completed",{sessionId:e.sessionDbId,duration:`${(s/1e3).toFixed(1)}s`});let i=new pr;i.markSessionCompleted(e.sessionDbId),i.close(),this.sessions.delete(e.sessionDbId)}catch(r){throw r.name==="AbortError"?Y.warn("SDK","Agent aborted",{sessionId:e.sessionDbId}):Y.failure("SDK","Agent error",{sessionId:e.sessionDbId},r),r}}async*createMessageGenerator(e){let a=dE(e.project,e.claudeSessionId,e.userPrompt);for(Y.dataIn("SDK",`Init prompt sent (${a.length} chars)`,{sessionId:e.sessionDbId,claudeSessionId:e.claudeSessionId,project:e.project}),Y.debug("SDK","Full init prompt",{sessionId:e.sessionDbId},a),yield{type:"user",session_id:e.claudeSessionId,parent_tool_use_id:null,message:{role:"user",content:a}};!e.abortController.signal.aborted;){if(e.pendingMessages.length===0){await new Promise(r=>setTimeout(r,100));continue}for(;e.pendingMessages.length>0;){let r=e.pendingMessages.shift();if(r.type==="summarize"){e.lastPromptNumber=r.prompt_number;let s=new pr,i=s.getSessionById(e.sessionDbId);s.close();let n=mE(i);Y.dataIn("SDK",`Summary prompt sent (${n.length} chars)`,{sessionId:e.sessionDbId,promptNumber:r.prompt_number}),Y.debug("SDK","Full summary prompt",{sessionId:e.sessionDbId},n),yield{type:"user",session_id:e.claudeSessionId,parent_tool_use_id:null,message:{role:"user",content:n}}}else if(r.type==="observation"){e.lastPromptNumber=r.prompt_number;let s=fE({id:0,tool_name:r.tool_name,tool_input:r.tool_input,tool_output:r.tool_output,created_at_epoch:Date.now()}),i=Y.formatTool(r.tool_name,r.tool_input);Y.dataIn("SDK",`Observation prompt: ${i}`,{sessionId:e.sessionDbId,promptNumber:r.prompt_number,size:`${s.length} chars`}),Y.debug("SDK","Full observation prompt",{sessionId:e.sessionDbId},s),yield{type:"user",session_id:e.claudeSessionId,parent_tool_use_id:null,message:{role:"user",content:s}}}}}}handleAgentMessage(e,a,r){Y.info("PARSER",`Processing response (${a.length} chars)`,{sessionId:e.sessionDbId,promptNumber:r,preview:a.substring(0,200)});let s=hE(a);s.length>0&&Y.info("PARSER",`Parsed ${s.length} observation(s)`,{sessionId:e.sessionDbId,promptNumber:r,types:s.map(o=>o.type).join(", ")});let i=new pr;for(let o of s){let{id:l,createdAtEpoch:c}=i.storeObservation(e.claudeSessionId,e.project,o,r);Y.success("DB","Observation stored",{sessionId:e.sessionDbId,type:o.type,title:o.title,id:l}),this.broadcastSSE({type:"new_observation",observation:{id:l,session_id:e.claudeSessionId,type:o.type,title:o.title,subtitle:o.subtitle,project:e.project,prompt_number:r,created_at_epoch:c}}),this.chromaSync.syncObservation(l,e.claudeSessionId,e.project,o,r,c).then(()=>{Y.success("WORKER","Observation synced to Chroma",{sessionId:e.sessionDbId,observationId:l})}).catch(u=>{Y.error("WORKER","Observation sync failed - continuing",{sessionId:e.sessionDbId,observationId:l},u)})}Y.info("PARSER","Looking for summary tags...",{sessionId:e.sessionDbId});let n=vE(a,e.sessionDbId);if(n){Y.success("PARSER","Summary parsed successfully!",{sessionId:e.sessionDbId,promptNumber:r,hasRequest:!!n.request,hasInvestigated:!!n.investigated,hasLearned:!!n.learned,hasCompleted:!!n.completed,hasNextSteps:!!n.next_steps});let{id:o,createdAtEpoch:l}=i.storeSummary(e.claudeSessionId,e.project,n,r);Y.success("DB","\u{1F4DD} SUMMARY STORED IN DATABASE",{sessionId:e.sessionDbId,promptNumber:r,id:o}),this.broadcastSSE({type:"new_summary",summary:{id:o,session_id:e.claudeSessionId,request:n.request,investigated:n.investigated,learned:n.learned,completed:n.completed,next_steps:n.next_steps,notes:n.notes,project:e.project,prompt_number:r,created_at_epoch:l}}),this.broadcastProcessingStatus(e.claudeSessionId,!1),this.chromaSync.syncSummary(o,e.claudeSessionId,e.project,n,r,l).then(()=>{Y.success("WORKER","Summary synced to Chroma",{sessionId:e.sessionDbId,summaryId:o})}).catch(c=>{Y.error("WORKER","Summary sync failed - continuing",{sessionId:e.sessionDbId,summaryId:o},c)})}else Y.warn("PARSER","NO SUMMARY TAGS FOUND in response",{sessionId:e.sessionDbId,promptNumber:r,contentSample:a.substring(0,500)}),this.broadcastProcessingStatus(e.claudeSessionId,!1);i.close()}};async function X5(){await new Gc().start(),process.on("SIGINT",()=>{Y.warn("SYSTEM","Shutting down (SIGINT)"),process.exit(0)}),process.on("SIGTERM",()=>{Y.warn("SYSTEM","Shutting down (SIGTERM)"),process.exit(0)})}X5().catch(t=>{Y.failure("SYSTEM","Fatal startup error",{},t),process.exit(1)});0&&(module.exports={WorkerService}); + `).get(l);n.close(),u&&this.broadcastSSE({type:"new_prompt",prompt:{id:u.id,claude_session_id:u.claude_session_id,project:u.project,prompt_number:u.prompt_number,prompt_text:u.prompt_text,created_at_epoch:u.created_at_epoch}}),u&&this.chromaSync.syncUserPrompt(u.id,u.sdk_session_id,u.project,u.prompt_text,u.prompt_number,u.created_at_epoch).catch(p=>{Y.failure("WORKER","Failed to sync user_prompt to Chroma - continuing",{promptId:u.id},p)}),c.generatorPromise=this.runSDKAgent(c).catch(p=>{Y.failure("WORKER","SDK agent error",{sessionId:r},p);let f=new pr;f.markSessionFailed(r),f.close(),this.sessions.delete(r)}),Y.success("WORKER","Session initialized",{sessionId:r,port:this.port}),a.json({status:"initialized",sessionDbId:r,port:this.port})}handleObservation(e,a){let r=parseInt(e.params.sessionDbId,10),{tool_name:s,tool_input:i,tool_response:n,prompt_number:o}=e.body,l=this.sessions.get(r);if(!l){let u=new pr,p=u.getSessionById(r);u.close(),l={sessionDbId:r,claudeSessionId:p.claude_session_id,sdkSessionId:null,project:p.project,userPrompt:p.user_prompt,pendingMessages:[],abortController:new AbortController,generatorPromise:null,lastPromptNumber:0,startTime:Date.now()},this.sessions.set(r,l),l.generatorPromise=this.runSDKAgent(l).catch(f=>{Y.failure("WORKER","SDK agent error",{sessionId:r},f);let d=new pr;d.markSessionFailed(r),d.close(),this.sessions.delete(r)})}let c=Y.formatTool(s,i);Y.dataIn("WORKER",`Observation queued: ${c}`,{sessionId:r,queue:l.pendingMessages.length+1}),l.pendingMessages.push({type:"observation",tool_name:s,tool_input:i,tool_response:n,prompt_number:o}),a.json({status:"queued",queueLength:l.pendingMessages.length})}handleSummarize(e,a){let r=parseInt(e.params.sessionDbId,10),{prompt_number:s}=e.body,i=this.sessions.get(r);if(!i){let n=new pr,o=n.getSessionById(r);n.close(),i={sessionDbId:r,claudeSessionId:o.claude_session_id,sdkSessionId:null,project:o.project,userPrompt:o.user_prompt,pendingMessages:[],abortController:new AbortController,generatorPromise:null,lastPromptNumber:0,startTime:Date.now()},this.sessions.set(r,i),i.generatorPromise=this.runSDKAgent(i).catch(l=>{Y.failure("WORKER","SDK agent error",{sessionId:r},l);let c=new pr;c.markSessionFailed(r),c.close(),this.sessions.delete(r)})}Y.dataIn("WORKER","Summary requested",{sessionId:r,promptNumber:s,queue:i.pendingMessages.length+1}),i.pendingMessages.push({type:"summarize",prompt_number:s}),this.broadcastProcessingStatus(i.claudeSessionId,!0),a.json({status:"queued",queueLength:i.pendingMessages.length})}handleStatus(e,a){let r=parseInt(e.params.sessionDbId,10),s=this.sessions.get(r);if(!s){a.status(404).json({error:"Session not found"});return}a.json({sessionDbId:r,sdkSessionId:s.sdkSessionId,project:s.project,pendingMessages:s.pendingMessages.length})}async handleDelete(e,a){let r=parseInt(e.params.sessionDbId,10),s=this.sessions.get(r);if(!s){a.status(404).json({error:"Session not found"});return}Y.warn("WORKER","Session delete requested",{sessionId:r}),s.abortController.abort(),s.generatorPromise&&await Promise.race([s.generatorPromise,new Promise(n=>setTimeout(n,5e3))]);let i=new pr;i.markSessionFailed(r),i.close(),this.sessions.delete(r),Y.info("WORKER","Session deleted",{sessionId:r}),a.json({status:"deleted"})}async runSDKAgent(e){Y.info("SDK","Agent starting",{sessionId:e.sessionDbId});let a=K5();Y.info("SDK",`Using Claude executable: ${a}`,{sessionId:e.sessionDbId});try{let r=$b({prompt:this.createMessageGenerator(e),options:{model:W5,disallowedTools:Q5,abortController:e.abortController,pathToClaudeCodeExecutable:a}});for await(let n of r){if(n.type==="assistant"){let o=n.message.content,l=Array.isArray(o)?o.filter(u=>u.type==="text").map(u=>u.text).join(` +`):typeof o=="string"?o:"",c=l.length;Y.dataOut("SDK",`Response received (${c} chars)`,{sessionId:e.sessionDbId,promptNumber:e.lastPromptNumber}),Y.debug("SDK","Full response",{sessionId:e.sessionDbId},l),this.handleAgentMessage(e,l,e.lastPromptNumber)}n.type==="result"&&n.subtype}let s=Date.now()-e.startTime;Y.success("SDK","Agent completed",{sessionId:e.sessionDbId,duration:`${(s/1e3).toFixed(1)}s`});let i=new pr;i.markSessionCompleted(e.sessionDbId),i.close(),this.sessions.delete(e.sessionDbId)}catch(r){throw r.name==="AbortError"?Y.warn("SDK","Agent aborted",{sessionId:e.sessionDbId}):Y.failure("SDK","Agent error",{sessionId:e.sessionDbId},r),r}}async*createMessageGenerator(e){let a=dE(e.project,e.claudeSessionId,e.userPrompt);for(Y.dataIn("SDK",`Init prompt sent (${a.length} chars)`,{sessionId:e.sessionDbId,claudeSessionId:e.claudeSessionId,project:e.project}),Y.debug("SDK","Full init prompt",{sessionId:e.sessionDbId},a),yield{type:"user",session_id:e.claudeSessionId,parent_tool_use_id:null,message:{role:"user",content:a}};!e.abortController.signal.aborted;){if(e.pendingMessages.length===0){await new Promise(r=>setTimeout(r,100));continue}for(;e.pendingMessages.length>0;){let r=e.pendingMessages.shift();if(r.type==="summarize"){e.lastPromptNumber=r.prompt_number;let s=new pr,i=s.getSessionById(e.sessionDbId);s.close();let n=mE(i);Y.dataIn("SDK",`Summary prompt sent (${n.length} chars)`,{sessionId:e.sessionDbId,promptNumber:r.prompt_number}),Y.debug("SDK","Full summary prompt",{sessionId:e.sessionDbId},n),yield{type:"user",session_id:e.claudeSessionId,parent_tool_use_id:null,message:{role:"user",content:n}}}else if(r.type==="observation"){e.lastPromptNumber=r.prompt_number;let s=fE({id:0,tool_name:r.tool_name,tool_input:r.tool_input,tool_response:r.tool_response,created_at_epoch:Date.now()}),i=Y.formatTool(r.tool_name,r.tool_input);Y.dataIn("SDK",`Observation prompt: ${i}`,{sessionId:e.sessionDbId,promptNumber:r.prompt_number,size:`${s.length} chars`}),Y.debug("SDK","Full observation prompt",{sessionId:e.sessionDbId},s),yield{type:"user",session_id:e.claudeSessionId,parent_tool_use_id:null,message:{role:"user",content:s}}}}}}handleAgentMessage(e,a,r){Y.info("PARSER",`Processing response (${a.length} chars)`,{sessionId:e.sessionDbId,promptNumber:r,preview:a.substring(0,200)});let s=hE(a);s.length>0&&Y.info("PARSER",`Parsed ${s.length} observation(s)`,{sessionId:e.sessionDbId,promptNumber:r,types:s.map(o=>o.type).join(", ")});let i=new pr;for(let o of s){let{id:l,createdAtEpoch:c}=i.storeObservation(e.claudeSessionId,e.project,o,r);Y.success("DB","Observation stored",{sessionId:e.sessionDbId,type:o.type,title:o.title,id:l}),this.broadcastSSE({type:"new_observation",observation:{id:l,session_id:e.claudeSessionId,type:o.type,title:o.title,subtitle:o.subtitle,project:e.project,prompt_number:r,created_at_epoch:c}}),this.chromaSync.syncObservation(l,e.claudeSessionId,e.project,o,r,c).then(()=>{Y.success("WORKER","Observation synced to Chroma",{sessionId:e.sessionDbId,observationId:l})}).catch(u=>{Y.error("WORKER","Observation sync failed - continuing",{sessionId:e.sessionDbId,observationId:l},u)})}Y.info("PARSER","Looking for summary tags...",{sessionId:e.sessionDbId});let n=vE(a,e.sessionDbId);if(n){Y.success("PARSER","Summary parsed successfully!",{sessionId:e.sessionDbId,promptNumber:r,hasRequest:!!n.request,hasInvestigated:!!n.investigated,hasLearned:!!n.learned,hasCompleted:!!n.completed,hasNextSteps:!!n.next_steps});let{id:o,createdAtEpoch:l}=i.storeSummary(e.claudeSessionId,e.project,n,r);Y.success("DB","\u{1F4DD} SUMMARY STORED IN DATABASE",{sessionId:e.sessionDbId,promptNumber:r,id:o}),this.broadcastSSE({type:"new_summary",summary:{id:o,session_id:e.claudeSessionId,request:n.request,investigated:n.investigated,learned:n.learned,completed:n.completed,next_steps:n.next_steps,notes:n.notes,project:e.project,prompt_number:r,created_at_epoch:l}}),this.broadcastProcessingStatus(e.claudeSessionId,!1),this.chromaSync.syncSummary(o,e.claudeSessionId,e.project,n,r,l).then(()=>{Y.success("WORKER","Summary synced to Chroma",{sessionId:e.sessionDbId,summaryId:o})}).catch(c=>{Y.error("WORKER","Summary sync failed - continuing",{sessionId:e.sessionDbId,summaryId:o},c)})}else Y.warn("PARSER","NO SUMMARY TAGS FOUND in response",{sessionId:e.sessionDbId,promptNumber:r,contentSample:a.substring(0,500)}),this.broadcastProcessingStatus(e.claudeSessionId,!1);i.close()}};async function X5(){await new Gc().start(),process.on("SIGINT",()=>{Y.warn("SYSTEM","Shutting down (SIGINT)"),process.exit(0)}),process.on("SIGTERM",()=>{Y.warn("SYSTEM","Shutting down (SIGTERM)"),process.exit(0)})}X5().catch(t=>{Y.failure("SYSTEM","Fatal startup error",{},t),process.exit(1)});0&&(module.exports={WorkerService}); /*! Bundled license information: depd/index.js: diff --git a/src/hooks/save-hook.ts b/src/hooks/save-hook.ts index ab79fe62..2bf46dc1 100644 --- a/src/hooks/save-hook.ts +++ b/src/hooks/save-hook.ts @@ -14,7 +14,7 @@ export interface PostToolUseInput { cwd: string; tool_name: string; tool_input: any; - tool_output: any; + tool_response: any; [key: string]: any; } @@ -31,7 +31,7 @@ async function saveHook(input?: PostToolUseInput): Promise { throw new Error('saveHook requires input'); } - const { session_id, tool_name, tool_input, tool_output } = input; + const { session_id, tool_name, tool_input, tool_response } = input; if (SKIP_TOOLS.has(tool_name)) { console.log(createHookResponse('PostToolUse', true)); @@ -65,7 +65,7 @@ async function saveHook(input?: PostToolUseInput): Promise { body: JSON.stringify({ tool_name, tool_input: tool_input !== undefined ? JSON.stringify(tool_input) : '{}', - tool_output: tool_output !== undefined ? JSON.stringify(tool_output) : '{}', + tool_response: tool_response !== undefined ? JSON.stringify(tool_response) : '{}', prompt_number: promptNumber }), signal: AbortSignal.timeout(2000) diff --git a/src/services/worker-service.ts b/src/services/worker-service.ts index e4cee762..c37b1f0f 100644 --- a/src/services/worker-service.ts +++ b/src/services/worker-service.ts @@ -68,7 +68,7 @@ interface ObservationMessage { type: 'observation'; tool_name: string; tool_input: string; - tool_output: string; + tool_response: string; prompt_number: number; } @@ -701,11 +701,11 @@ class WorkerService { /** * POST /sessions/:sessionDbId/observations - * Body: { tool_name, tool_input, tool_output, prompt_number } + * Body: { tool_name, tool_input, tool_response, prompt_number } */ private handleObservation(req: Request, res: Response): void { const sessionDbId = parseInt(req.params.sessionDbId, 10); - const { tool_name, tool_input, tool_output, prompt_number } = req.body; + const { tool_name, tool_input, tool_response, prompt_number } = req.body; let session = this.sessions.get(sessionDbId); if (!session) { @@ -751,7 +751,7 @@ class WorkerService { type: 'observation', tool_name, tool_input, - tool_output, + tool_response, prompt_number }); @@ -1015,7 +1015,7 @@ class WorkerService { id: 0, tool_name: message.tool_name, tool_input: message.tool_input, - tool_output: message.tool_output, + tool_response: message.tool_response, created_at_epoch: Date.now() });