diff --git a/plugin/scripts/worker-service.cjs b/plugin/scripts/worker-service.cjs index 09cfe199..db707b04 100755 --- a/plugin/scripts/worker-service.cjs +++ b/plugin/scripts/worker-service.cjs @@ -1274,7 +1274,7 @@ Tips: \u2022 Sort: orderBy="date_desc" or "date_asc"`}formatTime(e){return new Date(e).toLocaleString("en-US",{hour:"numeric",minute:"2-digit",hour12:!0})}estimateReadTokens(e){let r=(e.title?.length||0)+(e.subtitle?.length||0)+(e.narrative?.length||0)+(e.facts?.length||0);return Math.ceil(r/che)}formatObservationIndex(e,r){let n=`#${e.id}`,i=this.formatTime(e.created_at_epoch),a=Ve.getInstance().getTypeIcon(e.type),o=e.title||"Untitled",s=this.estimateReadTokens(e),c=Ve.getInstance().getWorkEmoji(e.type),u=e.discovery_tokens||0,l=u>0?`${c} ${u}`:"-";return`| ${n} | ${i} | ${a} | ${o} | ~${s} | ${l} |`}formatSessionIndex(e,r){let n=`#S${e.id}`,i=this.formatTime(e.created_at_epoch),a="\u{1F3AF}",o=e.request||`Session ${e.memory_session_id?.substring(0,8)||"unknown"}`;return`| ${n} | ${i} | ${a} | ${o} | - | - |`}formatUserPromptIndex(e,r){let n=`#P${e.id}`,i=this.formatTime(e.created_at_epoch),a="\u{1F4AC}",o=e.prompt_text.length>60?e.prompt_text.substring(0,57)+"...":e.prompt_text;return`| ${n} | ${i} | ${a} | ${o} | - | - |`}formatTableHeader(){return`| ID | Time | T | Title | Read | Work | |-----|------|---|-------|------|------|`}formatSearchTableHeader(){return`| ID | Time | T | Title | Read | |----|------|---|-------|------|`}formatObservationSearchRow(e,r){let n=`#${e.id}`,i=this.formatTime(e.created_at_epoch),a=Ve.getInstance().getTypeIcon(e.type),o=e.title||"Untitled",s=this.estimateReadTokens(e);return{row:`| ${n} | ${i===r?"\u2033":i} | ${a} | ${o} | ~${s} |`,time:i}}formatSessionSearchRow(e,r){let n=`#S${e.id}`,i=this.formatTime(e.created_at_epoch),a="\u{1F3AF}",o=e.request||`Session ${e.memory_session_id?.substring(0,8)||"unknown"}`;return{row:`| ${n} | ${i===r?"\u2033":i} | ${a} | ${o} | - |`,time:i}}formatUserPromptSearchRow(e,r){let n=`#P${e.id}`,i=this.formatTime(e.created_at_epoch),a="\u{1F4AC}",o=e.prompt_text.length>60?e.prompt_text.substring(0,57)+"...":e.prompt_text;return{row:`| ${n} | ${i===r?"\u2033":i} | ${a} | ${o} | - |`,time:i}}};Ur();var Ag=class{buildTimeline(e){let r=[...e.observations.map(n=>({type:"observation",data:n,epoch:n.created_at_epoch})),...e.sessions.map(n=>({type:"session",data:n,epoch:n.created_at_epoch})),...e.prompts.map(n=>({type:"prompt",data:n,epoch:n.created_at_epoch}))];return r.sort((n,i)=>n.epoch-i.epoch),r}filterByDepth(e,r,n,i,a){if(e.length===0)return e;let o=-1;if(typeof r=="number")o=e.findIndex(u=>u.type==="observation"&&u.data.id===r);else if(typeof r=="string"&&r.startsWith("S")){let u=parseInt(r.slice(1),10);o=e.findIndex(l=>l.type==="session"&&l.data.id===u)}else o=e.findIndex(u=>u.epoch>=n),o===-1&&(o=e.length-1);if(o===-1)return e;let s=Math.max(0,o-i),c=Math.min(e.length,o+a+1);return e.slice(s,c)}formatTimeline(e,r,n,i,a){if(e.length===0)return n?`Found observation matching "${n}", but no timeline context available.`:"No timeline items found";let o=[];if(n&&r){let u=e.find(d=>d.type==="observation"&&d.data.id===r),l=u?u.data.title||"Untitled":"Unknown";o.push(`# Timeline for query: "${n}"`),o.push(`**Anchor:** Observation #${r} - ${l}`)}else r?o.push(`# Timeline around anchor: ${r}`):o.push("# Timeline");i!==void 0&&a!==void 0?o.push(`**Window:** ${i} records before \u2192 ${a} records after | **Items:** ${e.length}`):o.push(`**Items:** ${e.length}`),o.push(""),o.push("**Legend:** \u{1F3AF} session-request | \u{1F534} bugfix | \u{1F7E3} feature | \u{1F504} refactor | \u2705 change | \u{1F535} discovery | \u{1F9E0} decision"),o.push("");let s=new Map;for(let u of e){let l=this.formatDate(u.epoch);s.has(l)||s.set(l,[]),s.get(l).push(u)}let c=Array.from(s.entries()).sort((u,l)=>{let d=new Date(u[0]).getTime(),p=new Date(l[0]).getTime();return d-p});for(let[u,l]of c){o.push(`### ${u}`),o.push("");let d=null,p="",f=!1;for(let h of l){let _=typeof r=="number"&&h.type==="observation"&&h.data.id===r||typeof r=="string"&&r.startsWith("S")&&h.type==="session"&&`S${h.data.id}`===r;if(h.type==="session"){f&&(o.push(""),f=!1,d=null,p="");let g=h.data,m=g.request||"Session summary",y=_?" \u2190 **ANCHOR**":"";o.push(`**\u{1F3AF} #S${g.id}** ${m} (${this.formatDateTime(h.epoch)})${y}`),o.push("")}else if(h.type==="prompt"){f&&(o.push(""),f=!1,d=null,p="");let g=h.data,m=g.prompt_text.length>100?g.prompt_text.substring(0,100)+"...":g.prompt_text;o.push(`**\u{1F4AC} User Prompt #${g.prompt_number}** (${this.formatDateTime(h.epoch)})`),o.push(`> ${m}`),o.push("")}else if(h.type==="observation"){let g=h.data,m="General";m!==d&&(f&&o.push(""),o.push(`**${m}**`),o.push("| ID | Time | T | Title | Tokens |"),o.push("|----|------|---|-------|--------|"),d=m,f=!0,p="");let y=this.getTypeIcon(g.type),v=this.formatTime(h.epoch),b=g.title||"Untitled",S=this.estimateTokens(g.narrative),w=v!==p?v:"\u2033";p=v;let k=_?" \u2190 **ANCHOR**":"";o.push(`| #${g.id} | ${w} | ${y} | ${b}${k} | ~${S} |`)}}f&&o.push("")}return o.join(` -`)}getTypeIcon(e){return Ve.getInstance().getTypeIcon(e)}formatDate(e){return new Date(e).toLocaleString("en-US",{month:"short",day:"numeric",year:"numeric"})}formatTime(e){return new Date(e).toLocaleString("en-US",{hour:"numeric",minute:"2-digit",hour12:!0})}formatDateTime(e){return new Date(e).toLocaleString("en-US",{month:"short",day:"numeric",hour:"numeric",minute:"2-digit",hour12:!0})}estimateTokens(e){return e?Math.ceil(e.length/4):0}};var jg=class{constructor(e,r){this.sseBroadcaster=e;this.workerService=r}broadcastNewPrompt(e){this.sseBroadcaster.broadcast({type:"new_prompt",prompt:e}),this.sseBroadcaster.broadcast({type:"processing_status",isProcessing:!0}),this.workerService.broadcastProcessingStatus()}broadcastSessionStarted(e,r){this.sseBroadcaster.broadcast({type:"session_started",sessionDbId:e,project:r}),this.workerService.broadcastProcessingStatus()}broadcastObservationQueued(e){this.sseBroadcaster.broadcast({type:"observation_queued",sessionDbId:e}),this.workerService.broadcastProcessingStatus()}broadcastSessionCompleted(e){this.sseBroadcaster.broadcast({type:"session_completed",timestamp:Date.now(),sessionDbId:e}),this.workerService.broadcastProcessingStatus()}broadcastSummarizeQueued(){this.workerService.broadcastProcessingStatus()}};var Q6=ut(Ih(),1),Mg=ut(require("path"),1),zg=require("fs");en();Se();var an=class{wrapHandler(e){return(r,n)=>{try{let i=e(r,n);i instanceof Promise&&i.catch(a=>this.handleError(n,a))}catch(i){E.error("HTTP","Route handler error",{path:r.path},i),this.handleError(n,i)}}}parseIntParam(e,r,n){let i=parseInt(e.params[n],10);return isNaN(i)?(this.badRequest(r,`Invalid ${n}`),null):i}validateRequired(e,r,n){for(let i of n)if(e.body[i]===void 0||e.body[i]===null)return this.badRequest(r,`Missing ${i}`),!1;return!0}badRequest(e,r){e.status(400).json({error:r})}notFound(e,r){e.status(404).json({error:r})}handleError(e,r,n){E.failure("WORKER",n||"Request failed",{},r),e.headersSent||e.status(500).json({error:r.message})}};var Dg=class extends an{constructor(r,n,i){super();this.sseBroadcaster=r;this.dbManager=n;this.sessionManager=i}setupRoutes(r){let n=Qr();r.use(Q6.default.static(Mg.default.join(n,"ui"))),r.get("/health",this.handleHealth.bind(this)),r.get("/",this.handleViewerUI.bind(this)),r.get("/stream",this.handleSSEStream.bind(this))}handleHealth=this.wrapHandler((r,n)=>{n.json({status:"ok",timestamp:Date.now()})});handleViewerUI=this.wrapHandler((r,n)=>{let i=Qr(),o=[Mg.default.join(i,"ui","viewer.html"),Mg.default.join(i,"plugin","ui","viewer.html")].find(c=>(0,zg.existsSync)(c));if(!o)throw new Error("Viewer UI not found at any expected location");let s=(0,zg.readFileSync)(o,"utf-8");n.setHeader("Content-Type","text/html"),n.send(s)});handleSSEStream=this.wrapHandler((r,n)=>{n.setHeader("Content-Type","text/event-stream"),n.setHeader("Cache-Control","no-cache"),n.setHeader("Connection","keep-alive"),this.sseBroadcaster.addClient(n);let i=this.dbManager.getSessionStore().getAllProjects();this.sseBroadcaster.broadcast({type:"initial_load",projects:i,timestamp:Date.now()});let a=this.sessionManager.isAnySessionProcessing(),o=this.sessionManager.getTotalActiveWork();this.sseBroadcaster.broadcast({type:"processing_status",isProcessing:a,queueDepth:o})})};Gr();Se();Se();var eL=100;function uhe(t){let e=(t.match(//g)||[]).length,r=(t.match(//g)||[]).length;return e+r}function tL(t){let e=uhe(t);return e>eL&&E.warn("SYSTEM","tag count exceeds limit",void 0,{tagCount:e,maxAllowed:eL,contentLength:t.length}),t.replace(/[\s\S]*?<\/claude-mem-context>/g,"").replace(/[\s\S]*?<\/private>/g,"").trim()}function wE(t){return tL(t)}function rL(t){return tL(t)}var Ug=class{constructor(e,r){this.sessionManager=e;this.eventBroadcaster=r}async completeByDbId(e){await this.sessionManager.deleteSession(e),this.eventBroadcaster.broadcastSessionCompleted(e)}};Se();var Kd=class{static checkUserPromptPrivacy(e,r,n,i,a,o){let s=e.getUserPrompt(r,n);return!s||s.trim()===""?(E.debug("HOOK",`Skipping ${i} - user prompt was entirely private`,{sessionId:a,promptNumber:n,...o}),null):s}};Mr();en();var Lg=class extends an{constructor(r,n,i,a,o,s,c){super();this.sessionManager=r;this.dbManager=n;this.sdkAgent=i;this.geminiAgent=a;this.openRouterAgent=o;this.eventBroadcaster=s;this.workerService=c;this.completionHandler=new Ug(r,s)}completionHandler;getActiveAgent(){if(SE()){if(Zd())return E.debug("SESSION","Using OpenRouter agent"),this.openRouterAgent;throw new Error("OpenRouter provider selected but no API key configured. Set CLAUDE_MEM_OPENROUTER_API_KEY in settings or OPENROUTER_API_KEY environment variable.")}if(xE()){if(Fd())return E.debug("SESSION","Using Gemini agent"),this.geminiAgent;throw new Error("Gemini provider selected but no API key configured. Set CLAUDE_MEM_GEMINI_API_KEY in settings or GEMINI_API_KEY environment variable.")}return this.sdkAgent}getSelectedProvider(){return SE()&&Zd()?"openrouter":xE()&&Fd()?"gemini":"claude"}ensureGeneratorRunning(r,n){let i=this.sessionManager.getSession(r);if(!i)return;let a=this.getSelectedProvider();if(!i.generatorPromise){this.startGeneratorWithProvider(i,a,n);return}i.currentProvider&&i.currentProvider!==a&&E.info("SESSION","Provider changed, will switch after current generator finishes",{sessionId:r,currentProvider:i.currentProvider,selectedProvider:a,historyLength:i.conversationHistory.length})}startGeneratorWithProvider(r,n,i){if(!r)return;let a=n==="openrouter"?this.openRouterAgent:n==="gemini"?this.geminiAgent:this.sdkAgent,o=n==="openrouter"?"OpenRouter":n==="gemini"?"Gemini":"Claude SDK";E.info("SESSION",`Generator auto-starting (${i}) using ${o}`,{sessionId:r.sessionDbId,queueDepth:r.pendingMessages.length,historyLength:r.conversationHistory.length}),r.currentProvider=n,r.generatorPromise=a.startSession(r,this.workerService).catch(s=>{if(r.abortController.signal.aborted)return;E.error("SESSION","Generator failed",{sessionId:r.sessionDbId,provider:n,error:s.message},s);let c=this.sessionManager.getPendingMessageStore();try{let u=c.markSessionMessagesFailed(r.sessionDbId);u>0&&E.error("SESSION","Marked messages as failed after generator error",{sessionId:r.sessionDbId,failedCount:u})}catch(u){E.error("SESSION","Failed to mark messages as failed",{sessionId:r.sessionDbId},u)}}).finally(()=>{let s=r.sessionDbId,c=r.abortController.signal.aborted;if(c?E.info("SESSION","Generator aborted",{sessionId:s}):E.error("SESSION","Generator exited unexpectedly",{sessionId:s}),r.generatorPromise=null,r.currentProvider=null,this.workerService.broadcastProcessingStatus(),!c)try{let l=this.sessionManager.getPendingMessageStore().getPendingCount(s),d=3;if(l>0){if(r.consecutiveRestarts=(r.consecutiveRestarts||0)+1,r.consecutiveRestarts>d){E.error("SESSION","CRITICAL: Generator restart limit exceeded - stopping to prevent runaway costs",{sessionId:s,pendingCount:l,consecutiveRestarts:r.consecutiveRestarts,maxRestarts:d,action:"Generator will NOT restart. Check logs for root cause. Messages remain in pending state."}),r.abortController.abort();return}E.info("SESSION","Restarting generator after crash/exit with pending work",{sessionId:s,pendingCount:l,consecutiveRestarts:r.consecutiveRestarts,maxRestarts:d});let p=r.abortController;r.abortController=new AbortController,p.abort();let f=Math.min(1e3*Math.pow(2,r.consecutiveRestarts-1),8e3);setTimeout(()=>{let h=this.sessionManager.getSession(s);h&&!h.generatorPromise&&this.startGeneratorWithProvider(h,this.getSelectedProvider(),"crash-recovery")},f)}else r.abortController.abort(),r.consecutiveRestarts=0,E.debug("SESSION","Aborted controller after natural completion",{sessionId:s})}catch(u){E.debug("SESSION","Error during recovery check, aborting to prevent leaks",{sessionId:s,error:u instanceof Error?u.message:String(u)}),r.abortController.abort()}})}setupRoutes(r){r.post("/sessions/:sessionDbId/init",this.handleSessionInit.bind(this)),r.post("/sessions/:sessionDbId/observations",this.handleObservations.bind(this)),r.post("/sessions/:sessionDbId/summarize",this.handleSummarize.bind(this)),r.get("/sessions/:sessionDbId/status",this.handleSessionStatus.bind(this)),r.delete("/sessions/:sessionDbId",this.handleSessionDelete.bind(this)),r.post("/sessions/:sessionDbId/complete",this.handleSessionComplete.bind(this)),r.post("/api/sessions/init",this.handleSessionInitByClaudeId.bind(this)),r.post("/api/sessions/observations",this.handleObservationsByClaudeId.bind(this)),r.post("/api/sessions/summarize",this.handleSummarizeByClaudeId.bind(this))}handleSessionInit=this.wrapHandler((r,n)=>{let i=this.parseIntParam(r,n,"sessionDbId");if(i===null)return;let{userPrompt:a,promptNumber:o}=r.body;E.info("HTTP","SessionRoutes: handleSessionInit called",{sessionDbId:i,promptNumber:o,has_userPrompt:!!a});let s=this.sessionManager.initializeSession(i,a,o),c=this.dbManager.getSessionStore().getLatestUserPrompt(s.contentSessionId);if(c){this.eventBroadcaster.broadcastNewPrompt({id:c.id,content_session_id:c.content_session_id,project:c.project,prompt_number:c.prompt_number,prompt_text:c.prompt_text,created_at_epoch:c.created_at_epoch});let u=Date.now(),l=c.prompt_text;this.dbManager.getChromaSync().syncUserPrompt(c.id,c.memory_session_id,c.project,l,c.prompt_number,c.created_at_epoch).then(()=>{let d=Date.now()-u,p=l.length>60?l.substring(0,60)+"...":l;E.debug("CHROMA","User prompt synced",{promptId:c.id,duration:`${d}ms`,prompt:p})}).catch(d=>{E.error("CHROMA","User prompt sync failed, continuing without vector search",{promptId:c.id,prompt:l.length>60?l.substring(0,60)+"...":l},d)})}this.ensureGeneratorRunning(i,"init"),this.eventBroadcaster.broadcastSessionStarted(i,s.project),n.json({status:"initialized",sessionDbId:i,port:Rt()})});handleObservations=this.wrapHandler((r,n)=>{let i=this.parseIntParam(r,n,"sessionDbId");if(i===null)return;let{tool_name:a,tool_input:o,tool_response:s,prompt_number:c,cwd:u}=r.body;this.sessionManager.queueObservation(i,{tool_name:a,tool_input:o,tool_response:s,prompt_number:c,cwd:u}),this.ensureGeneratorRunning(i,"observation"),this.eventBroadcaster.broadcastObservationQueued(i),n.json({status:"queued"})});handleSummarize=this.wrapHandler((r,n)=>{let i=this.parseIntParam(r,n,"sessionDbId");if(i===null)return;let{last_assistant_message:a}=r.body;this.sessionManager.queueSummarize(i,a),this.ensureGeneratorRunning(i,"summarize"),this.eventBroadcaster.broadcastSummarizeQueued(),n.json({status:"queued"})});handleSessionStatus=this.wrapHandler((r,n)=>{let i=this.parseIntParam(r,n,"sessionDbId");if(i===null)return;let a=this.sessionManager.getSession(i);if(!a){n.json({status:"not_found"});return}n.json({status:"active",sessionDbId:i,project:a.project,queueLength:a.pendingMessages.length,uptime:Date.now()-a.startTime})});handleSessionDelete=this.wrapHandler(async(r,n)=>{let i=this.parseIntParam(r,n,"sessionDbId");i!==null&&(await this.completionHandler.completeByDbId(i),n.json({status:"deleted"}))});handleSessionComplete=this.wrapHandler(async(r,n)=>{let i=this.parseIntParam(r,n,"sessionDbId");i!==null&&(await this.completionHandler.completeByDbId(i),n.json({success:!0}))});handleObservationsByClaudeId=this.wrapHandler((r,n)=>{let{contentSessionId:i,tool_name:a,tool_input:o,tool_response:s,cwd:c}=r.body;if(!i)return this.badRequest(n,"Missing contentSessionId");let u=Fe.loadFromFile(Yr);if(new Set(u.CLAUDE_MEM_SKIP_TOOLS.split(",").map(y=>y.trim()).filter(Boolean)).has(a)){E.debug("SESSION","Skipping observation for tool",{tool_name:a}),n.json({status:"skipped",reason:"tool_excluded"});return}if(new Set(["Edit","Write","Read","NotebookEdit"]).has(a)&&o){let y=o.file_path||o.notebook_path;if(y&&y.includes("session-memory")){E.debug("SESSION","Skipping meta-observation for session-memory file",{tool_name:a,file_path:y}),n.json({status:"skipped",reason:"session_memory_meta"});return}}let p=this.dbManager.getSessionStore(),f=p.createSDKSession(i,"",""),h=p.getPromptNumberFromUserPrompts(i);if(!Kd.checkUserPromptPrivacy(p,i,h,"observation",f,{tool_name:a})){n.json({status:"skipped",reason:"private"});return}let g=o!==void 0?wE(JSON.stringify(o)):"{}",m=s!==void 0?wE(JSON.stringify(s)):"{}";this.sessionManager.queueObservation(f,{tool_name:a,tool_input:g,tool_response:m,prompt_number:h,cwd:c||(E.error("SESSION","Missing cwd when queueing observation in SessionRoutes",{sessionId:f,tool_name:a}),"")}),this.ensureGeneratorRunning(f,"observation"),this.eventBroadcaster.broadcastObservationQueued(f),n.json({status:"queued"})});handleSummarizeByClaudeId=this.wrapHandler((r,n)=>{let{contentSessionId:i,last_assistant_message:a}=r.body;if(!i)return this.badRequest(n,"Missing contentSessionId");let o=this.dbManager.getSessionStore(),s=o.createSDKSession(i,"",""),c=o.getPromptNumberFromUserPrompts(i);if(!Kd.checkUserPromptPrivacy(o,i,c,"summarize",s)){n.json({status:"skipped",reason:"private"});return}this.sessionManager.queueSummarize(s,a),this.ensureGeneratorRunning(s,"summarize"),this.eventBroadcaster.broadcastSummarizeQueued(),n.json({status:"queued"})});handleSessionInitByClaudeId=this.wrapHandler((r,n)=>{let{contentSessionId:i,project:a,prompt:o}=r.body;if(E.info("HTTP","SessionRoutes: handleSessionInitByClaudeId called",{contentSessionId:i,project:a,prompt_length:o?.length}),!this.validateRequired(r,n,["contentSessionId","project","prompt"]))return;let s=this.dbManager.getSessionStore(),c=s.createSDKSession(i,a,o),u=s.getSessionById(c),l=!u?.memory_session_id;E.info("SESSION",`CREATED | contentSessionId=${i} \u2192 sessionDbId=${c} | isNew=${l} | project=${a}`,{sessionId:c});let p=s.getPromptNumberFromUserPrompts(i)+1,f=u?.memory_session_id||null;p>1?E.debug("HTTP",`[ALIGNMENT] DB Lookup Proof | contentSessionId=${i} \u2192 memorySessionId=${f||"(not yet captured)"} | prompt#=${p}`):E.debug("HTTP",`[ALIGNMENT] New Session | contentSessionId=${i} | prompt#=${p} | memorySessionId will be captured on first SDK response`);let h=rL(o);if(!h||h.trim()===""){E.debug("HOOK","Session init - prompt entirely private",{sessionId:c,promptNumber:p,originalLength:o.length}),n.json({sessionDbId:c,promptNumber:p,skipped:!0,reason:"private"});return}s.saveUserPrompt(i,p,h),E.debug("SESSION","User prompt saved",{sessionId:c,promptNumber:p}),n.json({sessionDbId:c,promptNumber:p,skipped:!1})})};var $E=ut(require("path"),1),Mc=require("fs");Se();var nL=require("os");en();Gr();var qg=class extends an{constructor(r,n,i,a,o,s){super();this.paginationHelper=r;this.dbManager=n;this.sessionManager=i;this.sseBroadcaster=a;this.workerService=o;this.startTime=s}setupRoutes(r){r.get("/api/observations",this.handleGetObservations.bind(this)),r.get("/api/summaries",this.handleGetSummaries.bind(this)),r.get("/api/prompts",this.handleGetPrompts.bind(this)),r.get("/api/observation/:id",this.handleGetObservationById.bind(this)),r.post("/api/observations/batch",this.handleGetObservationsByIds.bind(this)),r.get("/api/session/:id",this.handleGetSessionById.bind(this)),r.post("/api/sdk-sessions/batch",this.handleGetSdkSessionsByIds.bind(this)),r.get("/api/prompt/:id",this.handleGetPromptById.bind(this)),r.get("/api/stats",this.handleGetStats.bind(this)),r.get("/api/projects",this.handleGetProjects.bind(this)),r.get("/api/processing-status",this.handleGetProcessingStatus.bind(this)),r.post("/api/processing",this.handleSetProcessing.bind(this)),r.get("/api/pending-queue",this.handleGetPendingQueue.bind(this)),r.post("/api/pending-queue/process",this.handleProcessPendingQueue.bind(this)),r.delete("/api/pending-queue/failed",this.handleClearFailedQueue.bind(this)),r.delete("/api/pending-queue/all",this.handleClearAllQueue.bind(this)),r.post("/api/import",this.handleImport.bind(this))}handleGetObservations=this.wrapHandler((r,n)=>{let{offset:i,limit:a,project:o}=this.parsePaginationParams(r),s=this.paginationHelper.getObservations(i,a,o);n.json(s)});handleGetSummaries=this.wrapHandler((r,n)=>{let{offset:i,limit:a,project:o}=this.parsePaginationParams(r),s=this.paginationHelper.getSummaries(i,a,o);n.json(s)});handleGetPrompts=this.wrapHandler((r,n)=>{let{offset:i,limit:a,project:o}=this.parsePaginationParams(r),s=this.paginationHelper.getPrompts(i,a,o);n.json(s)});handleGetObservationById=this.wrapHandler((r,n)=>{let i=this.parseIntParam(r,n,"id");if(i===null)return;let o=this.dbManager.getSessionStore().getObservationById(i);if(!o){this.notFound(n,`Observation #${i} not found`);return}n.json(o)});handleGetObservationsByIds=this.wrapHandler((r,n)=>{let{ids:i,orderBy:a,limit:o,project:s}=r.body;if(!i||!Array.isArray(i)){this.badRequest(n,"ids must be an array of numbers");return}if(i.length===0){n.json([]);return}if(!i.every(l=>typeof l=="number"&&Number.isInteger(l))){this.badRequest(n,"All ids must be integers");return}let u=this.dbManager.getSessionStore().getObservationsByIds(i,{orderBy:a,limit:o,project:s});n.json(u)});handleGetSessionById=this.wrapHandler((r,n)=>{let i=this.parseIntParam(r,n,"id");if(i===null)return;let o=this.dbManager.getSessionStore().getSessionSummariesByIds([i]);if(o.length===0){this.notFound(n,`Session #${i} not found`);return}n.json(o[0])});handleGetSdkSessionsByIds=this.wrapHandler((r,n)=>{let{memorySessionIds:i}=r.body;if(!Array.isArray(i)){this.badRequest(n,"memorySessionIds must be an array");return}let o=this.dbManager.getSessionStore().getSdkSessionsBySessionIds(i);n.json(o)});handleGetPromptById=this.wrapHandler((r,n)=>{let i=this.parseIntParam(r,n,"id");if(i===null)return;let o=this.dbManager.getSessionStore().getUserPromptsByIds([i]);if(o.length===0){this.notFound(n,`Prompt #${i} not found`);return}n.json(o[0])});handleGetStats=this.wrapHandler((r,n)=>{let i=this.dbManager.getSessionStore().db,a=Qr(),o=$E.default.join(a,"package.json"),c=JSON.parse((0,Mc.readFileSync)(o,"utf-8")).version,u=i.prepare("SELECT COUNT(*) as count FROM observations").get(),l=i.prepare("SELECT COUNT(*) as count FROM sdk_sessions").get(),d=i.prepare("SELECT COUNT(*) as count FROM session_summaries").get(),p=$E.default.join((0,nL.homedir)(),".claude-mem","claude-mem.db"),f=0;(0,Mc.existsSync)(p)&&(f=(0,Mc.statSync)(p).size);let h=Math.floor((Date.now()-this.startTime)/1e3),_=this.sessionManager.getActiveSessionCount(),g=this.sseBroadcaster.getClientCount();n.json({worker:{version:c,uptime:h,activeSessions:_,sseClients:g,port:Rt()},database:{path:p,size:f,observations:u.count,sessions:l.count,summaries:d.count}})});handleGetProjects=this.wrapHandler((r,n)=>{let o=this.dbManager.getSessionStore().db.prepare(` +`)}getTypeIcon(e){return Ve.getInstance().getTypeIcon(e)}formatDate(e){return new Date(e).toLocaleString("en-US",{month:"short",day:"numeric",year:"numeric"})}formatTime(e){return new Date(e).toLocaleString("en-US",{hour:"numeric",minute:"2-digit",hour12:!0})}formatDateTime(e){return new Date(e).toLocaleString("en-US",{month:"short",day:"numeric",hour:"numeric",minute:"2-digit",hour12:!0})}estimateTokens(e){return e?Math.ceil(e.length/4):0}};var jg=class{constructor(e,r){this.sseBroadcaster=e;this.workerService=r}broadcastNewPrompt(e){this.sseBroadcaster.broadcast({type:"new_prompt",prompt:e}),this.sseBroadcaster.broadcast({type:"processing_status",isProcessing:!0}),this.workerService.broadcastProcessingStatus()}broadcastSessionStarted(e,r){this.sseBroadcaster.broadcast({type:"session_started",sessionDbId:e,project:r}),this.workerService.broadcastProcessingStatus()}broadcastObservationQueued(e){this.sseBroadcaster.broadcast({type:"observation_queued",sessionDbId:e}),this.workerService.broadcastProcessingStatus()}broadcastSessionCompleted(e){this.sseBroadcaster.broadcast({type:"session_completed",timestamp:Date.now(),sessionDbId:e}),this.workerService.broadcastProcessingStatus()}broadcastSummarizeQueued(){this.workerService.broadcastProcessingStatus()}};var Q6=ut(Ih(),1),Mg=ut(require("path"),1),zg=require("fs");en();Se();var an=class{wrapHandler(e){return(r,n)=>{try{let i=e(r,n);i instanceof Promise&&i.catch(a=>this.handleError(n,a))}catch(i){E.error("HTTP","Route handler error",{path:r.path},i),this.handleError(n,i)}}}parseIntParam(e,r,n){let i=parseInt(e.params[n],10);return isNaN(i)?(this.badRequest(r,`Invalid ${n}`),null):i}validateRequired(e,r,n){for(let i of n)if(e.body[i]===void 0||e.body[i]===null)return this.badRequest(r,`Missing ${i}`),!1;return!0}badRequest(e,r){e.status(400).json({error:r})}notFound(e,r){e.status(404).json({error:r})}handleError(e,r,n){E.failure("WORKER",n||"Request failed",{},r),e.headersSent||e.status(500).json({error:r.message})}};var Dg=class extends an{constructor(r,n,i){super();this.sseBroadcaster=r;this.dbManager=n;this.sessionManager=i}setupRoutes(r){let n=Qr();r.use(Q6.default.static(Mg.default.join(n,"ui"))),r.get("/health",this.handleHealth.bind(this)),r.get("/",this.handleViewerUI.bind(this)),r.get("/stream",this.handleSSEStream.bind(this))}handleHealth=this.wrapHandler((r,n)=>{n.json({status:"ok",timestamp:Date.now()})});handleViewerUI=this.wrapHandler((r,n)=>{let i=Qr(),o=[Mg.default.join(i,"ui","viewer.html"),Mg.default.join(i,"plugin","ui","viewer.html")].find(c=>(0,zg.existsSync)(c));if(!o)throw new Error("Viewer UI not found at any expected location");let s=(0,zg.readFileSync)(o,"utf-8");n.setHeader("Content-Type","text/html"),n.send(s)});handleSSEStream=this.wrapHandler((r,n)=>{n.setHeader("Content-Type","text/event-stream"),n.setHeader("Cache-Control","no-cache"),n.setHeader("Connection","keep-alive"),this.sseBroadcaster.addClient(n);let i=this.dbManager.getSessionStore().getAllProjects();this.sseBroadcaster.broadcast({type:"initial_load",projects:i,timestamp:Date.now()});let a=this.sessionManager.isAnySessionProcessing(),o=this.sessionManager.getTotalActiveWork();this.sseBroadcaster.broadcast({type:"processing_status",isProcessing:a,queueDepth:o})})};Gr();Se();Se();var eL=100;function uhe(t){let e=(t.match(//g)||[]).length,r=(t.match(//g)||[]).length;return e+r}function tL(t){let e=uhe(t);return e>eL&&E.warn("SYSTEM","tag count exceeds limit",void 0,{tagCount:e,maxAllowed:eL,contentLength:t.length}),t.replace(/[\s\S]*?<\/claude-mem-context>/g,"").replace(/[\s\S]*?<\/private>/g,"").trim()}function wE(t){return tL(t)}function rL(t){return tL(t)}var Ug=class{constructor(e,r){this.sessionManager=e;this.eventBroadcaster=r}async completeByDbId(e){await this.sessionManager.deleteSession(e),this.eventBroadcaster.broadcastSessionCompleted(e)}};Se();var Kd=class{static checkUserPromptPrivacy(e,r,n,i,a,o){let s=e.getUserPrompt(r,n);return!s||s.trim()===""?(E.debug("HOOK",`Skipping ${i} - user prompt was entirely private`,{sessionId:a,promptNumber:n,...o}),null):s}};Mr();en();var Lg=class extends an{constructor(r,n,i,a,o,s,c){super();this.sessionManager=r;this.dbManager=n;this.sdkAgent=i;this.geminiAgent=a;this.openRouterAgent=o;this.eventBroadcaster=s;this.workerService=c;this.completionHandler=new Ug(r,s)}completionHandler;getActiveAgent(){if(SE()){if(Zd())return E.debug("SESSION","Using OpenRouter agent"),this.openRouterAgent;throw new Error("OpenRouter provider selected but no API key configured. Set CLAUDE_MEM_OPENROUTER_API_KEY in settings or OPENROUTER_API_KEY environment variable.")}if(xE()){if(Fd())return E.debug("SESSION","Using Gemini agent"),this.geminiAgent;throw new Error("Gemini provider selected but no API key configured. Set CLAUDE_MEM_GEMINI_API_KEY in settings or GEMINI_API_KEY environment variable.")}return this.sdkAgent}getSelectedProvider(){return SE()&&Zd()?"openrouter":xE()&&Fd()?"gemini":"claude"}ensureGeneratorRunning(r,n){let i=this.sessionManager.getSession(r);if(!i)return;let a=this.getSelectedProvider();if(!i.generatorPromise){this.startGeneratorWithProvider(i,a,n);return}i.currentProvider&&i.currentProvider!==a&&E.info("SESSION","Provider changed, will switch after current generator finishes",{sessionId:r,currentProvider:i.currentProvider,selectedProvider:a,historyLength:i.conversationHistory.length})}startGeneratorWithProvider(r,n,i){if(!r)return;r.abortController.signal.aborted&&(E.debug("SESSION","Resetting aborted AbortController before starting generator",{sessionId:r.sessionDbId}),r.abortController=new AbortController);let a=n==="openrouter"?this.openRouterAgent:n==="gemini"?this.geminiAgent:this.sdkAgent,o=n==="openrouter"?"OpenRouter":n==="gemini"?"Gemini":"Claude SDK";E.info("SESSION",`Generator auto-starting (${i}) using ${o}`,{sessionId:r.sessionDbId,queueDepth:r.pendingMessages.length,historyLength:r.conversationHistory.length}),r.currentProvider=n,r.generatorPromise=a.startSession(r,this.workerService).catch(s=>{if(r.abortController.signal.aborted)return;E.error("SESSION","Generator failed",{sessionId:r.sessionDbId,provider:n,error:s.message},s);let c=this.sessionManager.getPendingMessageStore();try{let u=c.markSessionMessagesFailed(r.sessionDbId);u>0&&E.error("SESSION","Marked messages as failed after generator error",{sessionId:r.sessionDbId,failedCount:u})}catch(u){E.error("SESSION","Failed to mark messages as failed",{sessionId:r.sessionDbId},u)}}).finally(()=>{let s=r.sessionDbId,c=r.abortController.signal.aborted;if(c?E.info("SESSION","Generator aborted",{sessionId:s}):E.error("SESSION","Generator exited unexpectedly",{sessionId:s}),r.generatorPromise=null,r.currentProvider=null,this.workerService.broadcastProcessingStatus(),!c)try{let l=this.sessionManager.getPendingMessageStore().getPendingCount(s),d=3;if(l>0){if(r.consecutiveRestarts=(r.consecutiveRestarts||0)+1,r.consecutiveRestarts>d){E.error("SESSION","CRITICAL: Generator restart limit exceeded - stopping to prevent runaway costs",{sessionId:s,pendingCount:l,consecutiveRestarts:r.consecutiveRestarts,maxRestarts:d,action:"Generator will NOT restart. Check logs for root cause. Messages remain in pending state."}),r.abortController.abort();return}E.info("SESSION","Restarting generator after crash/exit with pending work",{sessionId:s,pendingCount:l,consecutiveRestarts:r.consecutiveRestarts,maxRestarts:d});let p=r.abortController;r.abortController=new AbortController,p.abort();let f=Math.min(1e3*Math.pow(2,r.consecutiveRestarts-1),8e3);setTimeout(()=>{let h=this.sessionManager.getSession(s);h&&!h.generatorPromise&&this.startGeneratorWithProvider(h,this.getSelectedProvider(),"crash-recovery")},f)}else r.abortController.abort(),r.consecutiveRestarts=0,E.debug("SESSION","Aborted controller after natural completion",{sessionId:s})}catch(u){E.debug("SESSION","Error during recovery check, aborting to prevent leaks",{sessionId:s,error:u instanceof Error?u.message:String(u)}),r.abortController.abort()}})}setupRoutes(r){r.post("/sessions/:sessionDbId/init",this.handleSessionInit.bind(this)),r.post("/sessions/:sessionDbId/observations",this.handleObservations.bind(this)),r.post("/sessions/:sessionDbId/summarize",this.handleSummarize.bind(this)),r.get("/sessions/:sessionDbId/status",this.handleSessionStatus.bind(this)),r.delete("/sessions/:sessionDbId",this.handleSessionDelete.bind(this)),r.post("/sessions/:sessionDbId/complete",this.handleSessionComplete.bind(this)),r.post("/api/sessions/init",this.handleSessionInitByClaudeId.bind(this)),r.post("/api/sessions/observations",this.handleObservationsByClaudeId.bind(this)),r.post("/api/sessions/summarize",this.handleSummarizeByClaudeId.bind(this))}handleSessionInit=this.wrapHandler((r,n)=>{let i=this.parseIntParam(r,n,"sessionDbId");if(i===null)return;let{userPrompt:a,promptNumber:o}=r.body;E.info("HTTP","SessionRoutes: handleSessionInit called",{sessionDbId:i,promptNumber:o,has_userPrompt:!!a});let s=this.sessionManager.initializeSession(i,a,o),c=this.dbManager.getSessionStore().getLatestUserPrompt(s.contentSessionId);if(c){this.eventBroadcaster.broadcastNewPrompt({id:c.id,content_session_id:c.content_session_id,project:c.project,prompt_number:c.prompt_number,prompt_text:c.prompt_text,created_at_epoch:c.created_at_epoch});let u=Date.now(),l=c.prompt_text;this.dbManager.getChromaSync().syncUserPrompt(c.id,c.memory_session_id,c.project,l,c.prompt_number,c.created_at_epoch).then(()=>{let d=Date.now()-u,p=l.length>60?l.substring(0,60)+"...":l;E.debug("CHROMA","User prompt synced",{promptId:c.id,duration:`${d}ms`,prompt:p})}).catch(d=>{E.error("CHROMA","User prompt sync failed, continuing without vector search",{promptId:c.id,prompt:l.length>60?l.substring(0,60)+"...":l},d)})}this.ensureGeneratorRunning(i,"init"),this.eventBroadcaster.broadcastSessionStarted(i,s.project),n.json({status:"initialized",sessionDbId:i,port:Rt()})});handleObservations=this.wrapHandler((r,n)=>{let i=this.parseIntParam(r,n,"sessionDbId");if(i===null)return;let{tool_name:a,tool_input:o,tool_response:s,prompt_number:c,cwd:u}=r.body;this.sessionManager.queueObservation(i,{tool_name:a,tool_input:o,tool_response:s,prompt_number:c,cwd:u}),this.ensureGeneratorRunning(i,"observation"),this.eventBroadcaster.broadcastObservationQueued(i),n.json({status:"queued"})});handleSummarize=this.wrapHandler((r,n)=>{let i=this.parseIntParam(r,n,"sessionDbId");if(i===null)return;let{last_assistant_message:a}=r.body;this.sessionManager.queueSummarize(i,a),this.ensureGeneratorRunning(i,"summarize"),this.eventBroadcaster.broadcastSummarizeQueued(),n.json({status:"queued"})});handleSessionStatus=this.wrapHandler((r,n)=>{let i=this.parseIntParam(r,n,"sessionDbId");if(i===null)return;let a=this.sessionManager.getSession(i);if(!a){n.json({status:"not_found"});return}n.json({status:"active",sessionDbId:i,project:a.project,queueLength:a.pendingMessages.length,uptime:Date.now()-a.startTime})});handleSessionDelete=this.wrapHandler(async(r,n)=>{let i=this.parseIntParam(r,n,"sessionDbId");i!==null&&(await this.completionHandler.completeByDbId(i),n.json({status:"deleted"}))});handleSessionComplete=this.wrapHandler(async(r,n)=>{let i=this.parseIntParam(r,n,"sessionDbId");i!==null&&(await this.completionHandler.completeByDbId(i),n.json({success:!0}))});handleObservationsByClaudeId=this.wrapHandler((r,n)=>{let{contentSessionId:i,tool_name:a,tool_input:o,tool_response:s,cwd:c}=r.body;if(!i)return this.badRequest(n,"Missing contentSessionId");let u=Fe.loadFromFile(Yr);if(new Set(u.CLAUDE_MEM_SKIP_TOOLS.split(",").map(y=>y.trim()).filter(Boolean)).has(a)){E.debug("SESSION","Skipping observation for tool",{tool_name:a}),n.json({status:"skipped",reason:"tool_excluded"});return}if(new Set(["Edit","Write","Read","NotebookEdit"]).has(a)&&o){let y=o.file_path||o.notebook_path;if(y&&y.includes("session-memory")){E.debug("SESSION","Skipping meta-observation for session-memory file",{tool_name:a,file_path:y}),n.json({status:"skipped",reason:"session_memory_meta"});return}}let p=this.dbManager.getSessionStore(),f=p.createSDKSession(i,"",""),h=p.getPromptNumberFromUserPrompts(i);if(!Kd.checkUserPromptPrivacy(p,i,h,"observation",f,{tool_name:a})){n.json({status:"skipped",reason:"private"});return}let g=o!==void 0?wE(JSON.stringify(o)):"{}",m=s!==void 0?wE(JSON.stringify(s)):"{}";this.sessionManager.queueObservation(f,{tool_name:a,tool_input:g,tool_response:m,prompt_number:h,cwd:c||(E.error("SESSION","Missing cwd when queueing observation in SessionRoutes",{sessionId:f,tool_name:a}),"")}),this.ensureGeneratorRunning(f,"observation"),this.eventBroadcaster.broadcastObservationQueued(f),n.json({status:"queued"})});handleSummarizeByClaudeId=this.wrapHandler((r,n)=>{let{contentSessionId:i,last_assistant_message:a}=r.body;if(!i)return this.badRequest(n,"Missing contentSessionId");let o=this.dbManager.getSessionStore(),s=o.createSDKSession(i,"",""),c=o.getPromptNumberFromUserPrompts(i);if(!Kd.checkUserPromptPrivacy(o,i,c,"summarize",s)){n.json({status:"skipped",reason:"private"});return}this.sessionManager.queueSummarize(s,a),this.ensureGeneratorRunning(s,"summarize"),this.eventBroadcaster.broadcastSummarizeQueued(),n.json({status:"queued"})});handleSessionInitByClaudeId=this.wrapHandler((r,n)=>{let{contentSessionId:i,project:a,prompt:o}=r.body;if(E.info("HTTP","SessionRoutes: handleSessionInitByClaudeId called",{contentSessionId:i,project:a,prompt_length:o?.length}),!this.validateRequired(r,n,["contentSessionId","project","prompt"]))return;let s=this.dbManager.getSessionStore(),c=s.createSDKSession(i,a,o),u=s.getSessionById(c),l=!u?.memory_session_id;E.info("SESSION",`CREATED | contentSessionId=${i} \u2192 sessionDbId=${c} | isNew=${l} | project=${a}`,{sessionId:c});let p=s.getPromptNumberFromUserPrompts(i)+1,f=u?.memory_session_id||null;p>1?E.debug("HTTP",`[ALIGNMENT] DB Lookup Proof | contentSessionId=${i} \u2192 memorySessionId=${f||"(not yet captured)"} | prompt#=${p}`):E.debug("HTTP",`[ALIGNMENT] New Session | contentSessionId=${i} | prompt#=${p} | memorySessionId will be captured on first SDK response`);let h=rL(o);if(!h||h.trim()===""){E.debug("HOOK","Session init - prompt entirely private",{sessionId:c,promptNumber:p,originalLength:o.length}),n.json({sessionDbId:c,promptNumber:p,skipped:!0,reason:"private"});return}s.saveUserPrompt(i,p,h),E.debug("SESSION","User prompt saved",{sessionId:c,promptNumber:p}),n.json({sessionDbId:c,promptNumber:p,skipped:!1})})};var $E=ut(require("path"),1),Mc=require("fs");Se();var nL=require("os");en();Gr();var qg=class extends an{constructor(r,n,i,a,o,s){super();this.paginationHelper=r;this.dbManager=n;this.sessionManager=i;this.sseBroadcaster=a;this.workerService=o;this.startTime=s}setupRoutes(r){r.get("/api/observations",this.handleGetObservations.bind(this)),r.get("/api/summaries",this.handleGetSummaries.bind(this)),r.get("/api/prompts",this.handleGetPrompts.bind(this)),r.get("/api/observation/:id",this.handleGetObservationById.bind(this)),r.post("/api/observations/batch",this.handleGetObservationsByIds.bind(this)),r.get("/api/session/:id",this.handleGetSessionById.bind(this)),r.post("/api/sdk-sessions/batch",this.handleGetSdkSessionsByIds.bind(this)),r.get("/api/prompt/:id",this.handleGetPromptById.bind(this)),r.get("/api/stats",this.handleGetStats.bind(this)),r.get("/api/projects",this.handleGetProjects.bind(this)),r.get("/api/processing-status",this.handleGetProcessingStatus.bind(this)),r.post("/api/processing",this.handleSetProcessing.bind(this)),r.get("/api/pending-queue",this.handleGetPendingQueue.bind(this)),r.post("/api/pending-queue/process",this.handleProcessPendingQueue.bind(this)),r.delete("/api/pending-queue/failed",this.handleClearFailedQueue.bind(this)),r.delete("/api/pending-queue/all",this.handleClearAllQueue.bind(this)),r.post("/api/import",this.handleImport.bind(this))}handleGetObservations=this.wrapHandler((r,n)=>{let{offset:i,limit:a,project:o}=this.parsePaginationParams(r),s=this.paginationHelper.getObservations(i,a,o);n.json(s)});handleGetSummaries=this.wrapHandler((r,n)=>{let{offset:i,limit:a,project:o}=this.parsePaginationParams(r),s=this.paginationHelper.getSummaries(i,a,o);n.json(s)});handleGetPrompts=this.wrapHandler((r,n)=>{let{offset:i,limit:a,project:o}=this.parsePaginationParams(r),s=this.paginationHelper.getPrompts(i,a,o);n.json(s)});handleGetObservationById=this.wrapHandler((r,n)=>{let i=this.parseIntParam(r,n,"id");if(i===null)return;let o=this.dbManager.getSessionStore().getObservationById(i);if(!o){this.notFound(n,`Observation #${i} not found`);return}n.json(o)});handleGetObservationsByIds=this.wrapHandler((r,n)=>{let{ids:i,orderBy:a,limit:o,project:s}=r.body;if(!i||!Array.isArray(i)){this.badRequest(n,"ids must be an array of numbers");return}if(i.length===0){n.json([]);return}if(!i.every(l=>typeof l=="number"&&Number.isInteger(l))){this.badRequest(n,"All ids must be integers");return}let u=this.dbManager.getSessionStore().getObservationsByIds(i,{orderBy:a,limit:o,project:s});n.json(u)});handleGetSessionById=this.wrapHandler((r,n)=>{let i=this.parseIntParam(r,n,"id");if(i===null)return;let o=this.dbManager.getSessionStore().getSessionSummariesByIds([i]);if(o.length===0){this.notFound(n,`Session #${i} not found`);return}n.json(o[0])});handleGetSdkSessionsByIds=this.wrapHandler((r,n)=>{let{memorySessionIds:i}=r.body;if(!Array.isArray(i)){this.badRequest(n,"memorySessionIds must be an array");return}let o=this.dbManager.getSessionStore().getSdkSessionsBySessionIds(i);n.json(o)});handleGetPromptById=this.wrapHandler((r,n)=>{let i=this.parseIntParam(r,n,"id");if(i===null)return;let o=this.dbManager.getSessionStore().getUserPromptsByIds([i]);if(o.length===0){this.notFound(n,`Prompt #${i} not found`);return}n.json(o[0])});handleGetStats=this.wrapHandler((r,n)=>{let i=this.dbManager.getSessionStore().db,a=Qr(),o=$E.default.join(a,"package.json"),c=JSON.parse((0,Mc.readFileSync)(o,"utf-8")).version,u=i.prepare("SELECT COUNT(*) as count FROM observations").get(),l=i.prepare("SELECT COUNT(*) as count FROM sdk_sessions").get(),d=i.prepare("SELECT COUNT(*) as count FROM session_summaries").get(),p=$E.default.join((0,nL.homedir)(),".claude-mem","claude-mem.db"),f=0;(0,Mc.existsSync)(p)&&(f=(0,Mc.statSync)(p).size);let h=Math.floor((Date.now()-this.startTime)/1e3),_=this.sessionManager.getActiveSessionCount(),g=this.sseBroadcaster.getClientCount();n.json({worker:{version:c,uptime:h,activeSessions:_,sseClients:g,port:Rt()},database:{path:p,size:f,observations:u.count,sessions:l.count,summaries:d.count}})});handleGetProjects=this.wrapHandler((r,n)=>{let o=this.dbManager.getSessionStore().db.prepare(` SELECT DISTINCT project FROM observations WHERE project IS NOT NULL