diff --git a/plugin/scripts/worker-service.cjs b/plugin/scripts/worker-service.cjs index a8bf3740..686f8fcb 100755 --- a/plugin/scripts/worker-service.cjs +++ b/plugin/scripts/worker-service.cjs @@ -1061,7 +1061,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/nV)}formatObservationIndex(e,r){let n=`#${e.id}`,a=this.formatTime(e.created_at_epoch),s=ut.getInstance().getTypeIcon(e.type),i=e.title||"Untitled",o=this.estimateReadTokens(e),c=ut.getInstance().getWorkEmoji(e.type),u=e.discovery_tokens||0,l=u>0?`${c} ${u}`:"-";return`| ${n} | ${a} | ${s} | ${i} | ~${o} | ${l} |`}formatSessionIndex(e,r){let n=`#S${e.id}`,a=this.formatTime(e.created_at_epoch),s="\u{1F3AF}",i=e.request||`Session ${e.sdk_session_id?.substring(0,8)||"unknown"}`;return`| ${n} | ${a} | ${s} | ${i} | - | - |`}formatUserPromptIndex(e,r){let n=`#P${e.id}`,a=this.formatTime(e.created_at_epoch),s="\u{1F4AC}",i=e.prompt_text.length>60?e.prompt_text.substring(0,57)+"...":e.prompt_text;return`| ${n} | ${a} | ${s} | ${i} | - | - |`}formatTableHeader(){return`| ID | Time | T | Title | Read | Work | |-----|------|---|-------|------|------|`}formatSearchTableHeader(){return`| ID | Time | T | Title | Read | |----|------|---|-------|------|`}formatObservationSearchRow(e,r){let n=`#${e.id}`,a=this.formatTime(e.created_at_epoch),s=ut.getInstance().getTypeIcon(e.type),i=e.title||"Untitled",o=this.estimateReadTokens(e);return{row:`| ${n} | ${a===r?"\u2033":a} | ${s} | ${i} | ~${o} |`,time:a}}formatSessionSearchRow(e,r){let n=`#S${e.id}`,a=this.formatTime(e.created_at_epoch),s="\u{1F3AF}",i=e.request||`Session ${e.sdk_session_id?.substring(0,8)||"unknown"}`;return{row:`| ${n} | ${a===r?"\u2033":a} | ${s} | ${i} | - |`,time:a}}formatUserPromptSearchRow(e,r){let n=`#P${e.id}`,a=this.formatTime(e.created_at_epoch),s="\u{1F4AC}",i=e.prompt_text.length>60?e.prompt_text.substring(0,57)+"...":e.prompt_text;return{row:`| ${n} | ${a===r?"\u2033":a} | ${s} | ${i} | - |`,time:a}}};ia();var Zl=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,a)=>n.epoch-a.epoch),r}filterByDepth(e,r,n,a,s){if(e.length===0)return e;let i=-1;if(typeof r=="number")i=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);i=e.findIndex(l=>l.type==="session"&&l.data.id===u)}else i=e.findIndex(u=>u.epoch>=n),i===-1&&(i=e.length-1);if(i===-1)return e;let o=Math.max(0,i-a),c=Math.min(e.length,i+s+1);return e.slice(o,c)}formatTimeline(e,r,n,a,s){if(e.length===0)return n?`Found observation matching "${n}", but no timeline context available.`:"No timeline items found";let i=[];if(n&&r){let u=e.find(d=>d.type==="observation"&&d.data.id===r),l=u?u.data.title||"Untitled":"Unknown";i.push(`# Timeline for query: "${n}"`),i.push(`**Anchor:** Observation #${r} - ${l}`)}else r?i.push(`# Timeline around anchor: ${r}`):i.push("# Timeline");a!==void 0&&s!==void 0?i.push(`**Window:** ${a} records before \u2192 ${s} records after | **Items:** ${e.length}`):i.push(`**Items:** ${e.length}`),i.push(""),i.push("**Legend:** \u{1F3AF} session-request | \u{1F534} bugfix | \u{1F7E3} feature | \u{1F504} refactor | \u2705 change | \u{1F535} discovery | \u{1F9E0} decision"),i.push("");let o=new Map;for(let u of e){let l=this.formatDate(u.epoch);o.has(l)||o.set(l,[]),o.get(l).push(u)}let c=Array.from(o.entries()).sort((u,l)=>{let d=new Date(u[0]).getTime(),m=new Date(l[0]).getTime();return d-m});for(let[u,l]of c){i.push(`### ${u}`),i.push("");let d=null,m="",p=!1;for(let g of l){let _=typeof r=="number"&&g.type==="observation"&&g.data.id===r||typeof r=="string"&&r.startsWith("S")&&g.type==="session"&&`S${g.data.id}`===r;if(g.type==="session"){p&&(i.push(""),p=!1,d=null,m="");let f=g.data,h=f.request||"Session summary",y=_?" \u2190 **ANCHOR**":"";i.push(`**\u{1F3AF} #S${f.id}** ${h} (${this.formatDateTime(g.epoch)})${y}`),i.push("")}else if(g.type==="prompt"){p&&(i.push(""),p=!1,d=null,m="");let f=g.data,h=f.prompt_text.length>100?f.prompt_text.substring(0,100)+"...":f.prompt_text;i.push(`**\u{1F4AC} User Prompt #${f.prompt_number}** (${this.formatDateTime(g.epoch)})`),i.push(`> ${h}`),i.push("")}else if(g.type==="observation"){let f=g.data,h="General";h!==d&&(p&&i.push(""),i.push(`**${h}**`),i.push("| ID | Time | T | Title | Tokens |"),i.push("|----|------|---|-------|--------|"),d=h,p=!0,m="");let y=this.getTypeIcon(f.type),v=this.formatTime(g.epoch),b=f.title||"Untitled",w=this.estimateTokens(f.narrative),E=v!==m?v:"\u2033";m=v;let T=_?" \u2190 **ANCHOR**":"";i.push(`| #${f.id} | ${E} | ${y} | ${b}${T} | ~${w} |`)}}p&&i.push("")}return i.join(` -`)}getTypeIcon(e){return ut.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 Wl=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 yh=wt(du(),1),yR=wt(gR(),1),_R=wt(require("path"),1);pr();ot();function bR(t){let e=[];e.push(yh.default.json({limit:"50mb"})),e.push((0,yR.default)()),e.push((a,s,i)=>{let c=[".html",".js",".css",".svg",".png",".jpg",".jpeg",".webp",".woff",".woff2",".ttf",".eot"].some(p=>a.path.endsWith(p));if(a.path.startsWith("/health")||a.path==="/"||c)return i();let u=Date.now(),l=`${a.method}-${Date.now()}`,d=t(a.method,a.path,a.body);O.info("HTTP",`\u2192 ${a.method} ${a.path}`,{requestId:l},d);let m=s.send.bind(s);s.send=function(p){let g=Date.now()-u;return O.info("HTTP",`\u2190 ${s.statusCode} ${a.path}`,{requestId:l,duration:`${g}ms`}),m(p)},i()});let r=sr(),n=_R.default.join(r,"plugin","ui");return e.push(yh.default.static(n)),e}function _h(t,e,r){let n=t.ip||t.connection.remoteAddress||"";if(!(n==="127.0.0.1"||n==="::1"||n==="::ffff:127.0.0.1"||n==="localhost")){O.warn("SECURITY","Admin endpoint access denied - not localhost",{endpoint:t.path,clientIp:n,method:t.method}),e.status(403).json({error:"Forbidden",message:"Admin endpoints are only accessible from localhost"});return}r()}function xR(t,e,r){if(!r||Object.keys(r).length===0||e.includes("/init"))return"";if(e.includes("/observations")){let n=r.tool_name||"?",a=r.tool_input;return`tool=${O.formatTool(n,a)}`}return e.includes("/summarize")?"requesting summary":""}var SR=wt(du(),1),Kl=wt(require("path"),1),Jl=require("fs");pr();ot();var Nr=class{wrapHandler(e){return(r,n)=>{try{let a=e(r,n);a instanceof Promise&&a.catch(s=>this.handleError(n,s))}catch(a){this.handleError(n,a)}}}parseIntParam(e,r,n){let a=parseInt(e.params[n],10);return isNaN(a)?(this.badRequest(r,`Invalid ${n}`),null):a}validateRequired(e,r,n){for(let a of n)if(e.body[a]===void 0||e.body[a]===null)return this.badRequest(r,`Missing ${a}`),!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){O.failure("WORKER",n||"Request failed",{},r),e.status(500).json({error:r.message})}};var Xl=class extends Nr{constructor(r,n,a){super();this.sseBroadcaster=r;this.dbManager=n;this.sessionManager=a}setupRoutes(r){let n=sr();r.use(SR.default.static(Kl.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 a=sr(),i=[Kl.default.join(a,"ui","viewer.html"),Kl.default.join(a,"plugin","ui","viewer.html")].find(c=>(0,Jl.existsSync)(c));if(!i)throw new Error("Viewer UI not found at any expected location");let o=(0,Jl.readFileSync)(i,"utf-8");n.setHeader("Content-Type","text/html"),n.send(o)});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 a=this.dbManager.getSessionStore().getAllProjects();this.sseBroadcaster.broadcast({type:"initial_load",projects:a,timestamp:Date.now()});let s=this.sessionManager.isAnySessionProcessing(),i=this.sessionManager.getTotalActiveWork();this.sseBroadcaster.broadcast({type:"processing_status",isProcessing:s,queueDepth:i})})};ot();ot();var wR=100;function uV(t){let e=(t.match(//g)||[]).length,r=(t.match(//g)||[]).length;return e+r}function ER(t){let e=uV(t);return e>wR&&O.warn("SYSTEM","tag count exceeds limit",void 0,{tagCount:e,maxAllowed:wR,contentLength:t.length}),t.replace(/[\s\S]*?<\/claude-mem-context>/g,"").replace(/[\s\S]*?<\/private>/g,"").trim()}function bh(t){return ER(t)}function TR(t){return ER(t)}var Yl=class{constructor(e,r){this.sessionManager=e;this.eventBroadcaster=r}async completeByDbId(e){await this.sessionManager.deleteSession(e),this.eventBroadcaster.broadcastSessionCompleted(e)}};ot();var Qo=class{static checkUserPromptPrivacy(e,r,n,a,s,i){let o=e.getUserPrompt(r,n);return!o||o.trim()===""?(O.debug("HOOK",`Skipping ${a} - user prompt was entirely private`,{sessionId:s,promptNumber:n,...i}),null):o}};$r();pr();var Ql=class extends Nr{constructor(r,n,a,s,i,o,c){super();this.sessionManager=r;this.dbManager=n;this.sdkAgent=a;this.geminiAgent=s;this.openRouterAgent=i;this.eventBroadcaster=o;this.workerService=c;this.completionHandler=new Yl(r,o)}completionHandler;getActiveAgent(){if(mh()){if(fh())return O.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(ph()){if(dh())return O.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 mh()&&fh()?"openrouter":ph()&&dh()?"gemini":"claude"}ensureGeneratorRunning(r,n){let a=this.sessionManager.getSession(r);if(!a)return;let s=this.getSelectedProvider();if(!a.generatorPromise){this.startGeneratorWithProvider(a,s,n);return}a.currentProvider&&a.currentProvider!==s&&O.info("SESSION","Provider changed, will switch after current generator finishes",{sessionId:r,currentProvider:a.currentProvider,selectedProvider:s,historyLength:a.conversationHistory.length})}startGeneratorWithProvider(r,n,a){if(!r)return;let s=n==="openrouter"?this.openRouterAgent:n==="gemini"?this.geminiAgent:this.sdkAgent,i=n==="openrouter"?"OpenRouter":n==="gemini"?"Gemini":"Claude SDK";O.info("SESSION",`Generator auto-starting (${a}) using ${i}`,{sessionId:r.sessionDbId,queueDepth:r.pendingMessages.length,historyLength:r.conversationHistory.length}),r.currentProvider=n,r.generatorPromise=s.startSession(r,this.workerService).catch(o=>{O.error("SESSION","Generator failed",{sessionId:r.sessionDbId,provider:n,error:o.message},o)}).finally(()=>{O.info("SESSION","Generator finished",{sessionId:r.sessionDbId}),r.generatorPromise=null,r.currentProvider=null,this.workerService.broadcastProcessingStatus()})}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 a=this.parseIntParam(r,n,"sessionDbId");if(a===null)return;let{userPrompt:s,promptNumber:i}=r.body;O.info("HTTP","SessionRoutes: handleSessionInit called",{sessionDbId:a,promptNumber:i,has_userPrompt:!!s});let o=this.sessionManager.initializeSession(a,s,i),c=this.dbManager.getSessionStore().getLatestUserPrompt(o.claudeSessionId);if(c){this.eventBroadcaster.broadcastNewPrompt({id:c.id,claude_session_id:c.claude_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.sdk_session_id,c.project,l,c.prompt_number,c.created_at_epoch).then(()=>{let d=Date.now()-u,m=l.length>60?l.substring(0,60)+"...":l;O.debug("CHROMA","User prompt synced",{promptId:c.id,duration:`${d}ms`,prompt:m})}).catch(d=>{O.warn("CHROMA","User prompt sync failed, continuing without vector search",{promptId:c.id,prompt:l.length>60?l.substring(0,60)+"...":l},d)})}this.startGeneratorWithProvider(o,this.getSelectedProvider(),"init"),this.eventBroadcaster.broadcastSessionStarted(a,o.project),n.json({status:"initialized",sessionDbId:a,port:Ln()})});handleObservations=this.wrapHandler((r,n)=>{let a=this.parseIntParam(r,n,"sessionDbId");if(a===null)return;let{tool_name:s,tool_input:i,tool_response:o,prompt_number:c,cwd:u}=r.body;this.sessionManager.queueObservation(a,{tool_name:s,tool_input:i,tool_response:o,prompt_number:c,cwd:u}),this.ensureGeneratorRunning(a,"observation"),this.eventBroadcaster.broadcastObservationQueued(a),n.json({status:"queued"})});handleSummarize=this.wrapHandler((r,n)=>{let a=this.parseIntParam(r,n,"sessionDbId");if(a===null)return;let{last_user_message:s,last_assistant_message:i}=r.body;this.sessionManager.queueSummarize(a,s,i),this.ensureGeneratorRunning(a,"summarize"),this.eventBroadcaster.broadcastSummarizeQueued(),n.json({status:"queued"})});handleSessionStatus=this.wrapHandler((r,n)=>{let a=this.parseIntParam(r,n,"sessionDbId");if(a===null)return;let s=this.sessionManager.getSession(a);if(!s){n.json({status:"not_found"});return}n.json({status:"active",sessionDbId:a,project:s.project,queueLength:s.pendingMessages.length,uptime:Date.now()-s.startTime})});handleSessionDelete=this.wrapHandler(async(r,n)=>{let a=this.parseIntParam(r,n,"sessionDbId");a!==null&&(await this.completionHandler.completeByDbId(a),n.json({status:"deleted"}))});handleSessionComplete=this.wrapHandler(async(r,n)=>{let a=this.parseIntParam(r,n,"sessionDbId");a!==null&&(await this.completionHandler.completeByDbId(a),n.json({success:!0}))});handleObservationsByClaudeId=this.wrapHandler((r,n)=>{let{claudeSessionId:a,tool_name:s,tool_input:i,tool_response:o,cwd:c}=r.body;if(!a)return this.badRequest(n,"Missing claudeSessionId");let u=Ve.loadFromFile(Or);if(new Set(u.CLAUDE_MEM_SKIP_TOOLS.split(",").map(y=>y.trim()).filter(Boolean)).has(s)){O.debug("SESSION","Skipping observation for tool",{tool_name:s}),n.json({status:"skipped",reason:"tool_excluded"});return}if(new Set(["Edit","Write","Read","NotebookEdit"]).has(s)&&i){let y=i.file_path||i.notebook_path;if(y&&y.includes("session-memory")){O.debug("SESSION","Skipping meta-observation for session-memory file",{tool_name:s,file_path:y}),n.json({status:"skipped",reason:"session_memory_meta"});return}}let m=this.dbManager.getSessionStore(),p=m.createSDKSession(a,"",""),g=m.getPromptNumberFromUserPrompts(a);if(!Qo.checkUserPromptPrivacy(m,a,g,"observation",p,{tool_name:s})){n.json({status:"skipped",reason:"private"});return}let f=i!==void 0?bh(JSON.stringify(i)):"{}",h=o!==void 0?bh(JSON.stringify(o)):"{}";this.sessionManager.queueObservation(p,{tool_name:s,tool_input:f,tool_response:h,prompt_number:g,cwd:c||O.happyPathError("SESSION","Missing cwd when queueing observation in SessionRoutes",{sessionId:p},{tool_name:s},"")}),this.ensureGeneratorRunning(p,"observation"),this.eventBroadcaster.broadcastObservationQueued(p),n.json({status:"queued"})});handleSummarizeByClaudeId=this.wrapHandler((r,n)=>{let{claudeSessionId:a,last_user_message:s,last_assistant_message:i}=r.body;if(!a)return this.badRequest(n,"Missing claudeSessionId");let o=this.dbManager.getSessionStore(),c=o.createSDKSession(a,"",""),u=o.getPromptNumberFromUserPrompts(a);if(!Qo.checkUserPromptPrivacy(o,a,u,"summarize",c)){n.json({status:"skipped",reason:"private"});return}this.sessionManager.queueSummarize(c,s||O.happyPathError("SESSION","Missing last_user_message when queueing summary in SessionRoutes",{sessionId:c},void 0,""),i),this.ensureGeneratorRunning(c,"summarize"),this.eventBroadcaster.broadcastSummarizeQueued(),n.json({status:"queued"})});handleSessionInitByClaudeId=this.wrapHandler((r,n)=>{let{claudeSessionId:a,project:s,prompt:i}=r.body;if(O.info("HTTP","SessionRoutes: handleSessionInitByClaudeId called",{claudeSessionId:a,project:s,prompt_length:i?.length}),!this.validateRequired(r,n,["claudeSessionId","project","prompt"]))return;let o=this.dbManager.getSessionStore(),c=o.createSDKSession(a,s,i);O.info("HTTP","SessionRoutes: createSDKSession returned",{sessionDbId:c,claudeSessionId:a});let u=o.getPromptNumberFromUserPrompts(a),l=u+1;O.info("HTTP","SessionRoutes: Calculated promptNumber",{sessionDbId:c,promptNumber:l,currentCount:u});let d=TR(i);if(!d||d.trim()===""){O.debug("HOOK","Session init - prompt entirely private",{sessionId:c,promptNumber:l,originalLength:i.length}),n.json({sessionDbId:c,promptNumber:l,skipped:!0,reason:"private"});return}o.saveUserPrompt(a,l,d),O.info("SESSION","Session initialized via HTTP",{sessionId:c,promptNumber:l,project:s}),n.json({sessionDbId:c,promptNumber:l,skipped:!1})})};var xh=wt(require("path"),1),Ti=require("fs"),kR=require("os");pr();var ed=class extends Nr{constructor(r,n,a,s,i,o){super();this.paginationHelper=r;this.dbManager=n;this.sessionManager=a;this.sseBroadcaster=s;this.workerService=i;this.startTime=o}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.post("/api/import",this.handleImport.bind(this))}handleGetObservations=this.wrapHandler((r,n)=>{let{offset:a,limit:s,project:i}=this.parsePaginationParams(r),o=this.paginationHelper.getObservations(a,s,i);n.json(o)});handleGetSummaries=this.wrapHandler((r,n)=>{let{offset:a,limit:s,project:i}=this.parsePaginationParams(r),o=this.paginationHelper.getSummaries(a,s,i);n.json(o)});handleGetPrompts=this.wrapHandler((r,n)=>{let{offset:a,limit:s,project:i}=this.parsePaginationParams(r),o=this.paginationHelper.getPrompts(a,s,i);n.json(o)});handleGetObservationById=this.wrapHandler((r,n)=>{let a=this.parseIntParam(r,n,"id");if(a===null)return;let i=this.dbManager.getSessionStore().getObservationById(a);if(!i){this.notFound(n,`Observation #${a} not found`);return}n.json(i)});handleGetObservationsByIds=this.wrapHandler((r,n)=>{let{ids:a,orderBy:s,limit:i,project:o}=r.body;if(!a||!Array.isArray(a)){this.badRequest(n,"ids must be an array of numbers");return}if(a.length===0){n.json([]);return}if(!a.every(l=>typeof l=="number"&&Number.isInteger(l))){this.badRequest(n,"All ids must be integers");return}let u=this.dbManager.getSessionStore().getObservationsByIds(a,{orderBy:s,limit:i,project:o});n.json(u)});handleGetSessionById=this.wrapHandler((r,n)=>{let a=this.parseIntParam(r,n,"id");if(a===null)return;let i=this.dbManager.getSessionStore().getSessionSummariesByIds([a]);if(i.length===0){this.notFound(n,`Session #${a} not found`);return}n.json(i[0])});handleGetSdkSessionsByIds=this.wrapHandler((r,n)=>{let{sdkSessionIds:a}=r.body;if(!Array.isArray(a)){this.badRequest(n,"sdkSessionIds must be an array");return}let i=this.dbManager.getSessionStore().getSdkSessionsBySessionIds(a);n.json(i)});handleGetPromptById=this.wrapHandler((r,n)=>{let a=this.parseIntParam(r,n,"id");if(a===null)return;let i=this.dbManager.getSessionStore().getUserPromptsByIds([a]);if(i.length===0){this.notFound(n,`Prompt #${a} not found`);return}n.json(i[0])});handleGetStats=this.wrapHandler((r,n)=>{let a=this.dbManager.getSessionStore().db,s=sr(),i=xh.default.join(s,"package.json"),c=JSON.parse((0,Ti.readFileSync)(i,"utf-8")).version,u=a.prepare("SELECT COUNT(*) as count FROM observations").get(),l=a.prepare("SELECT COUNT(*) as count FROM sdk_sessions").get(),d=a.prepare("SELECT COUNT(*) as count FROM session_summaries").get(),m=xh.default.join((0,kR.homedir)(),".claude-mem","claude-mem.db"),p=0;(0,Ti.existsSync)(m)&&(p=(0,Ti.statSync)(m).size);let g=Math.floor((Date.now()-this.startTime)/1e3),_=this.sessionManager.getActiveSessionCount(),f=this.sseBroadcaster.getClientCount();n.json({worker:{version:c,uptime:g,activeSessions:_,sseClients:f,port:Ln()},database:{path:m,size:p,observations:u.count,sessions:l.count,summaries:d.count}})});handleGetProjects=this.wrapHandler((r,n)=>{let i=this.dbManager.getSessionStore().db.prepare(` +`)}getTypeIcon(e){return ut.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 Wl=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 yh=wt(du(),1),yR=wt(gR(),1),_R=wt(require("path"),1);pr();ot();function bR(t){let e=[];e.push(yh.default.json({limit:"50mb"})),e.push((0,yR.default)()),e.push((a,s,i)=>{let c=[".html",".js",".css",".svg",".png",".jpg",".jpeg",".webp",".woff",".woff2",".ttf",".eot"].some(p=>a.path.endsWith(p));if(a.path.startsWith("/health")||a.path==="/"||c)return i();let u=Date.now(),l=`${a.method}-${Date.now()}`,d=t(a.method,a.path,a.body);O.info("HTTP",`\u2192 ${a.method} ${a.path}`,{requestId:l},d);let m=s.send.bind(s);s.send=function(p){let g=Date.now()-u;return O.info("HTTP",`\u2190 ${s.statusCode} ${a.path}`,{requestId:l,duration:`${g}ms`}),m(p)},i()});let r=sr(),n=_R.default.join(r,"plugin","ui");return e.push(yh.default.static(n)),e}function _h(t,e,r){let n=t.ip||t.connection.remoteAddress||"";if(!(n==="127.0.0.1"||n==="::1"||n==="::ffff:127.0.0.1"||n==="localhost")){O.warn("SECURITY","Admin endpoint access denied - not localhost",{endpoint:t.path,clientIp:n,method:t.method}),e.status(403).json({error:"Forbidden",message:"Admin endpoints are only accessible from localhost"});return}r()}function xR(t,e,r){if(!r||Object.keys(r).length===0||e.includes("/init"))return"";if(e.includes("/observations")){let n=r.tool_name||"?",a=r.tool_input;return`tool=${O.formatTool(n,a)}`}return e.includes("/summarize")?"requesting summary":""}var SR=wt(du(),1),Kl=wt(require("path"),1),Jl=require("fs");pr();ot();var Nr=class{wrapHandler(e){return(r,n)=>{try{let a=e(r,n);a instanceof Promise&&a.catch(s=>this.handleError(n,s))}catch(a){this.handleError(n,a)}}}parseIntParam(e,r,n){let a=parseInt(e.params[n],10);return isNaN(a)?(this.badRequest(r,`Invalid ${n}`),null):a}validateRequired(e,r,n){for(let a of n)if(e.body[a]===void 0||e.body[a]===null)return this.badRequest(r,`Missing ${a}`),!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){O.failure("WORKER",n||"Request failed",{},r),e.status(500).json({error:r.message})}};var Xl=class extends Nr{constructor(r,n,a){super();this.sseBroadcaster=r;this.dbManager=n;this.sessionManager=a}setupRoutes(r){let n=sr();r.use(SR.default.static(Kl.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 a=sr(),i=[Kl.default.join(a,"ui","viewer.html"),Kl.default.join(a,"plugin","ui","viewer.html")].find(c=>(0,Jl.existsSync)(c));if(!i)throw new Error("Viewer UI not found at any expected location");let o=(0,Jl.readFileSync)(i,"utf-8");n.setHeader("Content-Type","text/html"),n.send(o)});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 a=this.dbManager.getSessionStore().getAllProjects();this.sseBroadcaster.broadcast({type:"initial_load",projects:a,timestamp:Date.now()});let s=this.sessionManager.isAnySessionProcessing(),i=this.sessionManager.getTotalActiveWork();this.sseBroadcaster.broadcast({type:"processing_status",isProcessing:s,queueDepth:i})})};ot();ot();var wR=100;function uV(t){let e=(t.match(//g)||[]).length,r=(t.match(//g)||[]).length;return e+r}function ER(t){let e=uV(t);return e>wR&&O.warn("SYSTEM","tag count exceeds limit",void 0,{tagCount:e,maxAllowed:wR,contentLength:t.length}),t.replace(/[\s\S]*?<\/claude-mem-context>/g,"").replace(/[\s\S]*?<\/private>/g,"").trim()}function bh(t){return ER(t)}function TR(t){return ER(t)}var Yl=class{constructor(e,r){this.sessionManager=e;this.eventBroadcaster=r}async completeByDbId(e){await this.sessionManager.deleteSession(e),this.eventBroadcaster.broadcastSessionCompleted(e)}};ot();var Qo=class{static checkUserPromptPrivacy(e,r,n,a,s,i){let o=e.getUserPrompt(r,n);return!o||o.trim()===""?(O.debug("HOOK",`Skipping ${a} - user prompt was entirely private`,{sessionId:s,promptNumber:n,...i}),null):o}};$r();pr();var Ql=class extends Nr{constructor(r,n,a,s,i,o,c){super();this.sessionManager=r;this.dbManager=n;this.sdkAgent=a;this.geminiAgent=s;this.openRouterAgent=i;this.eventBroadcaster=o;this.workerService=c;this.completionHandler=new Yl(r,o)}completionHandler;getActiveAgent(){if(mh()){if(fh())return O.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(ph()){if(dh())return O.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 mh()&&fh()?"openrouter":ph()&&dh()?"gemini":"claude"}ensureGeneratorRunning(r,n){let a=this.sessionManager.getSession(r);if(!a)return;let s=this.getSelectedProvider();if(!a.generatorPromise){this.startGeneratorWithProvider(a,s,n);return}a.currentProvider&&a.currentProvider!==s&&O.info("SESSION","Provider changed, will switch after current generator finishes",{sessionId:r,currentProvider:a.currentProvider,selectedProvider:s,historyLength:a.conversationHistory.length})}startGeneratorWithProvider(r,n,a){if(!r)return;let s=n==="openrouter"?this.openRouterAgent:n==="gemini"?this.geminiAgent:this.sdkAgent,i=n==="openrouter"?"OpenRouter":n==="gemini"?"Gemini":"Claude SDK";O.info("SESSION",`Generator auto-starting (${a}) using ${i}`,{sessionId:r.sessionDbId,queueDepth:r.pendingMessages.length,historyLength:r.conversationHistory.length}),r.currentProvider=n,r.generatorPromise=s.startSession(r,this.workerService).catch(o=>{O.error("SESSION","Generator failed",{sessionId:r.sessionDbId,provider:n,error:o.message},o)}).finally(()=>{O.info("SESSION","Generator finished",{sessionId:r.sessionDbId}),r.generatorPromise=null,r.currentProvider=null,this.workerService.broadcastProcessingStatus()})}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 a=this.parseIntParam(r,n,"sessionDbId");if(a===null)return;let{userPrompt:s,promptNumber:i}=r.body;O.info("HTTP","SessionRoutes: handleSessionInit called",{sessionDbId:a,promptNumber:i,has_userPrompt:!!s});let o=this.sessionManager.initializeSession(a,s,i),c=this.dbManager.getSessionStore().getLatestUserPrompt(o.claudeSessionId);if(c){this.eventBroadcaster.broadcastNewPrompt({id:c.id,claude_session_id:c.claude_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.sdk_session_id,c.project,l,c.prompt_number,c.created_at_epoch).then(()=>{let d=Date.now()-u,m=l.length>60?l.substring(0,60)+"...":l;O.debug("CHROMA","User prompt synced",{promptId:c.id,duration:`${d}ms`,prompt:m})}).catch(d=>{O.warn("CHROMA","User prompt sync failed, continuing without vector search",{promptId:c.id,prompt:l.length>60?l.substring(0,60)+"...":l},d)})}this.startGeneratorWithProvider(o,this.getSelectedProvider(),"init"),this.eventBroadcaster.broadcastSessionStarted(a,o.project),n.json({status:"initialized",sessionDbId:a,port:Ln()})});handleObservations=this.wrapHandler((r,n)=>{let a=this.parseIntParam(r,n,"sessionDbId");if(a===null)return;let{tool_name:s,tool_input:i,tool_response:o,prompt_number:c,cwd:u}=r.body;this.sessionManager.queueObservation(a,{tool_name:s,tool_input:i,tool_response:o,prompt_number:c,cwd:u}),this.ensureGeneratorRunning(a,"observation"),this.eventBroadcaster.broadcastObservationQueued(a),n.json({status:"queued"})});handleSummarize=this.wrapHandler((r,n)=>{let a=this.parseIntParam(r,n,"sessionDbId");if(a===null)return;let{last_user_message:s,last_assistant_message:i}=r.body;this.sessionManager.queueSummarize(a,s,i),this.ensureGeneratorRunning(a,"summarize"),this.eventBroadcaster.broadcastSummarizeQueued(),n.json({status:"queued"})});handleSessionStatus=this.wrapHandler((r,n)=>{let a=this.parseIntParam(r,n,"sessionDbId");if(a===null)return;let s=this.sessionManager.getSession(a);if(!s){n.json({status:"not_found"});return}n.json({status:"active",sessionDbId:a,project:s.project,queueLength:s.pendingMessages.length,uptime:Date.now()-s.startTime})});handleSessionDelete=this.wrapHandler(async(r,n)=>{let a=this.parseIntParam(r,n,"sessionDbId");a!==null&&(await this.completionHandler.completeByDbId(a),n.json({status:"deleted"}))});handleSessionComplete=this.wrapHandler(async(r,n)=>{let a=this.parseIntParam(r,n,"sessionDbId");a!==null&&(await this.completionHandler.completeByDbId(a),n.json({success:!0}))});handleObservationsByClaudeId=this.wrapHandler((r,n)=>{let{claudeSessionId:a,tool_name:s,tool_input:i,tool_response:o,cwd:c}=r.body;if(!a)return this.badRequest(n,"Missing claudeSessionId");let u=Ve.loadFromFile(Or);if(new Set(u.CLAUDE_MEM_SKIP_TOOLS.split(",").map(y=>y.trim()).filter(Boolean)).has(s)){O.debug("SESSION","Skipping observation for tool",{tool_name:s}),n.json({status:"skipped",reason:"tool_excluded"});return}if(new Set(["Edit","Write","Read","NotebookEdit"]).has(s)&&i){let y=i.file_path||i.notebook_path;if(y&&y.includes("session-memory")){O.debug("SESSION","Skipping meta-observation for session-memory file",{tool_name:s,file_path:y}),n.json({status:"skipped",reason:"session_memory_meta"});return}}let m=this.dbManager.getSessionStore(),p=m.createSDKSession(a,"",""),g=m.getPromptNumberFromUserPrompts(a);if(!Qo.checkUserPromptPrivacy(m,a,g,"observation",p,{tool_name:s})){n.json({status:"skipped",reason:"private"});return}let f=i!==void 0?bh(JSON.stringify(i)):"{}",h=o!==void 0?bh(JSON.stringify(o)):"{}";this.sessionManager.queueObservation(p,{tool_name:s,tool_input:f,tool_response:h,prompt_number:g,cwd:c||O.happyPathError("SESSION","Missing cwd when queueing observation in SessionRoutes",{sessionId:p},{tool_name:s},"")}),this.ensureGeneratorRunning(p,"observation"),this.eventBroadcaster.broadcastObservationQueued(p),n.json({status:"queued"})});handleSummarizeByClaudeId=this.wrapHandler((r,n)=>{let{claudeSessionId:a,last_user_message:s,last_assistant_message:i}=r.body;if(!a)return this.badRequest(n,"Missing claudeSessionId");let o=this.dbManager.getSessionStore(),c=o.createSDKSession(a,"",""),u=o.getPromptNumberFromUserPrompts(a);if(!Qo.checkUserPromptPrivacy(o,a,u,"summarize",c)){n.json({status:"skipped",reason:"private"});return}this.sessionManager.queueSummarize(c,s||O.happyPathError("SESSION","Missing last_user_message when queueing summary in SessionRoutes",{sessionId:c},void 0,""),i),this.ensureGeneratorRunning(c,"summarize"),this.eventBroadcaster.broadcastSummarizeQueued(),n.json({status:"queued"})});handleSessionInitByClaudeId=this.wrapHandler((r,n)=>{let{claudeSessionId:a,project:s,prompt:i}=r.body;if(O.info("HTTP","SessionRoutes: handleSessionInitByClaudeId called",{claudeSessionId:a,project:s,prompt_length:i?.length}),!this.validateRequired(r,n,["claudeSessionId","project","prompt"]))return;let o=this.dbManager.getSessionStore(),c=TR(i),u=o.createSDKSession(a,s,c),l=o.getPromptNumberFromUserPrompts(a),d=l+1;if(O.info("HTTP","SessionRoutes: Session initialization",{sessionDbId:u,claudeSessionId:a,promptNumber:d,currentCount:l}),!c||c.trim()===""){O.debug("HOOK","Session init - prompt entirely private",{sessionId:u,promptNumber:d,originalLength:i.length}),n.json({sessionDbId:u,promptNumber:d,skipped:!0,reason:"private"});return}o.saveUserPrompt(a,d,c),O.info("SESSION","Session initialized via HTTP",{sessionId:u,promptNumber:d,project:s}),n.json({sessionDbId:u,promptNumber:d,skipped:!1})})};var xh=wt(require("path"),1),Ti=require("fs"),kR=require("os");pr();var ed=class extends Nr{constructor(r,n,a,s,i,o){super();this.paginationHelper=r;this.dbManager=n;this.sessionManager=a;this.sseBroadcaster=s;this.workerService=i;this.startTime=o}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.post("/api/import",this.handleImport.bind(this))}handleGetObservations=this.wrapHandler((r,n)=>{let{offset:a,limit:s,project:i}=this.parsePaginationParams(r),o=this.paginationHelper.getObservations(a,s,i);n.json(o)});handleGetSummaries=this.wrapHandler((r,n)=>{let{offset:a,limit:s,project:i}=this.parsePaginationParams(r),o=this.paginationHelper.getSummaries(a,s,i);n.json(o)});handleGetPrompts=this.wrapHandler((r,n)=>{let{offset:a,limit:s,project:i}=this.parsePaginationParams(r),o=this.paginationHelper.getPrompts(a,s,i);n.json(o)});handleGetObservationById=this.wrapHandler((r,n)=>{let a=this.parseIntParam(r,n,"id");if(a===null)return;let i=this.dbManager.getSessionStore().getObservationById(a);if(!i){this.notFound(n,`Observation #${a} not found`);return}n.json(i)});handleGetObservationsByIds=this.wrapHandler((r,n)=>{let{ids:a,orderBy:s,limit:i,project:o}=r.body;if(!a||!Array.isArray(a)){this.badRequest(n,"ids must be an array of numbers");return}if(a.length===0){n.json([]);return}if(!a.every(l=>typeof l=="number"&&Number.isInteger(l))){this.badRequest(n,"All ids must be integers");return}let u=this.dbManager.getSessionStore().getObservationsByIds(a,{orderBy:s,limit:i,project:o});n.json(u)});handleGetSessionById=this.wrapHandler((r,n)=>{let a=this.parseIntParam(r,n,"id");if(a===null)return;let i=this.dbManager.getSessionStore().getSessionSummariesByIds([a]);if(i.length===0){this.notFound(n,`Session #${a} not found`);return}n.json(i[0])});handleGetSdkSessionsByIds=this.wrapHandler((r,n)=>{let{sdkSessionIds:a}=r.body;if(!Array.isArray(a)){this.badRequest(n,"sdkSessionIds must be an array");return}let i=this.dbManager.getSessionStore().getSdkSessionsBySessionIds(a);n.json(i)});handleGetPromptById=this.wrapHandler((r,n)=>{let a=this.parseIntParam(r,n,"id");if(a===null)return;let i=this.dbManager.getSessionStore().getUserPromptsByIds([a]);if(i.length===0){this.notFound(n,`Prompt #${a} not found`);return}n.json(i[0])});handleGetStats=this.wrapHandler((r,n)=>{let a=this.dbManager.getSessionStore().db,s=sr(),i=xh.default.join(s,"package.json"),c=JSON.parse((0,Ti.readFileSync)(i,"utf-8")).version,u=a.prepare("SELECT COUNT(*) as count FROM observations").get(),l=a.prepare("SELECT COUNT(*) as count FROM sdk_sessions").get(),d=a.prepare("SELECT COUNT(*) as count FROM session_summaries").get(),m=xh.default.join((0,kR.homedir)(),".claude-mem","claude-mem.db"),p=0;(0,Ti.existsSync)(m)&&(p=(0,Ti.statSync)(m).size);let g=Math.floor((Date.now()-this.startTime)/1e3),_=this.sessionManager.getActiveSessionCount(),f=this.sseBroadcaster.getClientCount();n.json({worker:{version:c,uptime:g,activeSessions:_,sseClients:f,port:Ln()},database:{path:m,size:p,observations:u.count,sessions:l.count,summaries:d.count}})});handleGetProjects=this.wrapHandler((r,n)=>{let i=this.dbManager.getSessionStore().db.prepare(` SELECT DISTINCT project FROM observations WHERE project IS NOT NULL diff --git a/src/services/worker/http/routes/SessionRoutes.ts b/src/services/worker/http/routes/SessionRoutes.ts index 64573d69..1556962a 100644 --- a/src/services/worker/http/routes/SessionRoutes.ts +++ b/src/services/worker/http/routes/SessionRoutes.ts @@ -501,27 +501,24 @@ export class SessionRoutes extends BaseRouteHandler { const store = this.dbManager.getSessionStore(); - // Step 1: Create/get SDK session (idempotent INSERT OR IGNORE) - const sessionDbId = store.createSDKSession(claudeSessionId, project, prompt); + // Step 1: Strip privacy tags from prompt BEFORE storing + // This prevents content from being persisted to sdk_sessions.user_prompt + const cleanedPrompt = stripMemoryTagsFromPrompt(prompt); - logger.info('HTTP', 'SessionRoutes: createSDKSession returned', { - sessionDbId, - claudeSessionId - }); + // Step 2: Create/get SDK session with CLEANED prompt (idempotent INSERT OR IGNORE) + const sessionDbId = store.createSDKSession(claudeSessionId, project, cleanedPrompt); - // Step 2: Get next prompt number from user_prompts count + // Step 3: Get next prompt number from user_prompts count const currentCount = store.getPromptNumberFromUserPrompts(claudeSessionId); const promptNumber = currentCount + 1; - logger.info('HTTP', 'SessionRoutes: Calculated promptNumber', { + logger.info('HTTP', 'SessionRoutes: Session initialization', { sessionDbId, + claudeSessionId, promptNumber, currentCount }); - // Step 3: Strip privacy tags from prompt - const cleanedPrompt = stripMemoryTagsFromPrompt(prompt); - // Step 4: Check if prompt is entirely private if (!cleanedPrompt || cleanedPrompt.trim() === '') { logger.debug('HOOK', 'Session init - prompt entirely private', {