claude-mem 12.4.2 → 12.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -196,7 +196,7 @@ Examples:
196
196
  claude-mem gemini-cli uninstall # Remove hooks
197
197
 
198
198
  For more info: https://docs.claude-mem.ai/usage/gemini-provider
199
- `),0}}var Qt,B,ne,dt,Qa,Pe,qr=S(()=>{"use strict";K();ut();Qt=Vr.join(Za(),".gemini"),B=Vr.join(Qt,"settings.json"),ne=Vr.join(Qt,"GEMINI.md"),dt="claude-mem",Qa=1e4,Pe={SessionStart:"context",BeforeAgent:"session-init",AfterAgent:"observation",BeforeTool:"observation",AfterTool:"observation",PreCompress:"summarize",Notification:"observation",SessionEnd:"session-complete"}});import ac from"path";import{existsSync as cc,readFileSync as lc,writeFileSync as zr,mkdirSync as uc}from"fs";function er(e,t,r){let n=ac.dirname(e);uc(n,{recursive:!0});let s=`${pt}
199
+ `),0}}var Qt,B,ne,dt,Qa,Pe,qr=S(()=>{"use strict";K();ut();Qt=Vr.join(Za(),".gemini"),B=Vr.join(Qt,"settings.json"),ne=Vr.join(Qt,"GEMINI.md"),dt="claude-mem",Qa=1e4,Pe={SessionStart:"context",BeforeAgent:"session-init",AfterAgent:"observation",BeforeTool:"observation",AfterTool:"observation",PreCompress:"summarize",Notification:"observation"}});import ac from"path";import{existsSync as cc,readFileSync as lc,writeFileSync as zr,mkdirSync as uc}from"fs";function er(e,t,r){let n=ac.dirname(e);uc(n,{recursive:!0});let s=`${pt}
200
200
  ${t}
201
201
  ${Fe}`;if(cc(e)){let o=lc(e,"utf-8"),i=o.indexOf(pt),a=o.indexOf(Fe);i!==-1&&a!==-1?o=o.slice(0,i)+s+o.slice(a+Fe.length):o=o.trimEnd()+`
202
202
 
@@ -1,2 +1,2 @@
1
- var d="http://127.0.0.1:37777";var g={"Content-Type":"application/json"};function l(t,s){fetch(`${d}${t}`,{method:"POST",headers:g,body:JSON.stringify(s)}).catch(o=>{let r=o instanceof Error?o.message:String(o);r.includes("ECONNREFUSED")||console.warn(`[claude-mem] Worker POST ${t} failed: ${r}`)})}async function E(t){try{let s=await fetch(`${d}${t}`,{headers:g});return s.ok?await s.text():(console.warn(`[claude-mem] Worker GET ${t} returned ${s.status}`),null)}catch(s){let o=s instanceof Error?s.message:String(s);return o.includes("ECONNREFUSED")||console.warn(`[claude-mem] Worker GET ${t} failed: ${o}`),null}}var a=new Map,S=1e3;function u(t){if(!a.has(t)){for(;a.size>=S;){let s=a.keys().next().value;if(s!==void 0)a.delete(s);else break}a.set(t,`opencode-${t}-${Date.now()}`)}return a.get(t)}var w=async t=>{let s=t.project?.name||"opencode";return console.log(`[claude-mem] OpenCode plugin loading (project: ${s})`),{hooks:{tool:{execute:{after:(o,r)=>{let e=u(o.sessionID),n=r.output||"";n.length>1e3&&(n=n.slice(0,1e3)),l("/api/sessions/observations",{contentSessionId:e,tool_name:o.tool,tool_input:o.args||{},tool_response:n,cwd:t.directory})}}}},event:(o,r)=>{switch(o){case"session.created":{let{event:e}=r,n=u(e.sessionID);l("/api/sessions/init",{contentSessionId:n,project:s,prompt:""});break}case"message.updated":{let{event:e}=r;if(e.role!=="assistant")break;let n=u(e.sessionID),c=e.content||"";c.length>1e3&&(c=c.slice(0,1e3)),l("/api/sessions/observations",{contentSessionId:n,tool_name:"assistant_message",tool_input:{},tool_response:c,cwd:t.directory});break}case"session.compacted":{let{event:e}=r,n=u(e.sessionID);l("/api/sessions/summarize",{contentSessionId:n,last_assistant_message:e.summary||""});break}case"file.edited":{let{event:e}=r,n=u(e.sessionID);l("/api/sessions/observations",{contentSessionId:n,tool_name:"file_edit",tool_input:{path:e.path},tool_response:e.diff?e.diff.slice(0,1e3):`File edited: ${e.path}`,cwd:t.directory});break}case"session.deleted":{let{event:e}=r,n=a.get(e.sessionID);n&&(l("/api/sessions/complete",{contentSessionId:n}),a.delete(e.sessionID));break}}},tool:{claude_mem_search:{description:"Search claude-mem memory database for past observations, sessions, and context",args:{query:{type:"string",description:"Search query for memory observations"}},async execute(o){let r=String(o.query||"");if(!r)return"Please provide a search query.";let e=await E(`/api/search/observations?query=${encodeURIComponent(r)}&limit=10`);if(!e)return"claude-mem worker is not running. Start it with: npx claude-mem start";let n;try{n=JSON.parse(e)}catch(i){return console.warn("[claude-mem] Failed to parse search results:",i instanceof Error?i.message:String(i)),"Failed to parse search results."}let c=Array.isArray(n.items)?n.items:[];return c.length===0?`No results found for "${r}".`:c.slice(0,10).map((i,m)=>{let f=String(i.title||i.subtitle||"Untitled"),p=i.project?` [${String(i.project)}]`:"";return`${m+1}. ${f}${p}`}).join(`
1
+ var d="http://127.0.0.1:37777";var g={"Content-Type":"application/json"};function l(n,s){fetch(`${d}${n}`,{method:"POST",headers:g,body:JSON.stringify(s)}).catch(o=>{let r=o instanceof Error?o.message:String(o);r.includes("ECONNREFUSED")||console.warn(`[claude-mem] Worker POST ${n} failed: ${r}`)})}async function E(n){try{let s=await fetch(`${d}${n}`,{headers:g});return s.ok?await s.text():(console.warn(`[claude-mem] Worker GET ${n} returned ${s.status}`),null)}catch(s){let o=s instanceof Error?s.message:String(s);return o.includes("ECONNREFUSED")||console.warn(`[claude-mem] Worker GET ${n} failed: ${o}`),null}}var c=new Map,S=1e3;function u(n){if(!c.has(n)){for(;c.size>=S;){let s=c.keys().next().value;if(s!==void 0)c.delete(s);else break}c.set(n,`opencode-${n}-${Date.now()}`)}return c.get(n)}var w=async n=>{let s=n.project?.name||"opencode";return console.log(`[claude-mem] OpenCode plugin loading (project: ${s})`),{hooks:{tool:{execute:{after:(o,r)=>{let e=u(o.sessionID),t=r.output||"";t.length>1e3&&(t=t.slice(0,1e3)),l("/api/sessions/observations",{contentSessionId:e,tool_name:o.tool,tool_input:o.args||{},tool_response:t,cwd:n.directory})}}}},event:(o,r)=>{switch(o){case"session.created":{let{event:e}=r,t=u(e.sessionID);l("/api/sessions/init",{contentSessionId:t,project:s,prompt:""});break}case"message.updated":{let{event:e}=r;if(e.role!=="assistant")break;let t=u(e.sessionID),a=e.content||"";a.length>1e3&&(a=a.slice(0,1e3)),l("/api/sessions/observations",{contentSessionId:t,tool_name:"assistant_message",tool_input:{},tool_response:a,cwd:n.directory});break}case"session.compacted":{let{event:e}=r,t=u(e.sessionID);l("/api/sessions/summarize",{contentSessionId:t,last_assistant_message:e.summary||""});break}case"file.edited":{let{event:e}=r,t=u(e.sessionID);l("/api/sessions/observations",{contentSessionId:t,tool_name:"file_edit",tool_input:{path:e.path},tool_response:e.diff?e.diff.slice(0,1e3):`File edited: ${e.path}`,cwd:n.directory});break}case"session.deleted":{let{event:e}=r;c.delete(e.sessionID);break}}},tool:{claude_mem_search:{description:"Search claude-mem memory database for past observations, sessions, and context",args:{query:{type:"string",description:"Search query for memory observations"}},async execute(o){let r=String(o.query||"");if(!r)return"Please provide a search query.";let e=await E(`/api/search/observations?query=${encodeURIComponent(r)}&limit=10`);if(!e)return"claude-mem worker is not running. Start it with: npx claude-mem start";let t;try{t=JSON.parse(e)}catch(i){return console.warn("[claude-mem] Failed to parse search results:",i instanceof Error?i.message:String(i)),"Failed to parse search results."}let a=Array.isArray(t.items)?t.items:[];return a.length===0?`No results found for "${r}".`:a.slice(0,10).map((i,m)=>{let f=String(i.title||i.subtitle||"Untitled"),p=i.project?` [${String(i.project)}]`:"";return`${m+1}. ${f}${p}`}).join(`
2
2
  `)}}}}},O=w;export{w as ClaudeMemPlugin,O as default};
@@ -1,15 +1,15 @@
1
- var V="127.0.0.1",Z=["\u{1F527}","\u{1F4D0}","\u{1F50D}","\u{1F4BB}","\u{1F9EA}","\u{1F41B}","\u{1F6E1}\uFE0F","\u2601\uFE0F","\u{1F4E6}","\u{1F3AF}","\u{1F52E}","\u26A1","\u{1F30A}","\u{1F3A8}","\u{1F4CA}","\u{1F680}","\u{1F52C}","\u{1F3D7}\uFE0F","\u{1F4DD}","\u{1F3AD}"];function te(e){let s=0;for(let o=0;o<e.length;o++)s=(s<<5)-s+e.charCodeAt(o)|0;return Z[Math.abs(s)%Z.length]}var re="\u{1F99E}",se="\u2328\uFE0F",oe="Claude Code Session",ie="\u{1F980}";function ae(e){let s=e?.primary??re,o=e?.claudeCode??se,a=e?.claudeCodeLabel??oe,c=e?.default??ie,g=e?.agents??{};return function(f){if(!f)return c;if(f.startsWith("openclaw-")){let b=f.slice(9);return b?`${g[b]||te(b)} ${b}`:`${s} openclaw`}if(f==="openclaw")return`${s} openclaw`;let v=a.trim();return v?`${o} ${v} (${f})`:`${o} ${f}`}}var H=V;function T(e){return`http://${H}:${e}`}var ce=3,ee=3e4,h="CLOSED",j=0,G=0,P=!1;function z(e){return h==="CLOSED"?!0:h==="OPEN"?Date.now()-G>=ee?(h="HALF_OPEN",e.info("[claude-mem] Circuit breaker: probing worker connection"),P?!1:(P=!0,!0)):!1:P?!1:(P=!0,!0)}function X(e){h!=="CLOSED"&&e.info("[claude-mem] Worker connection restored \u2014 circuit closed"),h="CLOSED",j=0,P=!1}function O(e){P=!1,j++,(h==="HALF_OPEN"||h==="CLOSED"&&j>=ce)&&(h="OPEN",G=Date.now(),e.warn(`[claude-mem] Worker unreachable \u2014 disabling requests for ${ee/1e3}s`))}function le(){h="CLOSED",j=0,G=0,P=!1}async function Y(e,s,o,a){if(!z(a))return null;try{let c=await fetch(`${T(e)}${s}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(o)});return c.ok?(X(a),await c.json()):(O(a),a.warn(`[claude-mem] Worker POST ${s} returned ${c.status}`),null)}catch(c){let g=c instanceof Error?c.message:String(c);return O(a),h!=="OPEN"&&a.warn(`[claude-mem] Worker POST ${s} failed: ${g}`),null}}function Q(e,s,o,a){z(a)&&fetch(`${T(e)}${s}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(o)}).then(c=>{if(!c.ok){O(a),a.warn(`[claude-mem] Worker POST ${s} returned ${c.status}`);return}X(a)}).catch(c=>{let g=c instanceof Error?c.message:String(c);O(a),h!=="OPEN"&&a.warn(`[claude-mem] Worker POST ${s} failed: ${g}`)})}async function J(e,s,o){if(!z(o))return null;try{let a=await fetch(`${T(e)}${s}`);return a.ok?(X(o),await a.text()):(O(o),o.warn(`[claude-mem] Worker GET ${s} returned ${a.status}`),null)}catch(a){let c=a instanceof Error?a.message:String(a);return O(o),h!=="OPEN"&&o.warn(`[claude-mem] Worker GET ${s} failed: ${c}`),null}}async function q(e,s,o){let a=await J(e,s,o);if(!a)return null;try{return JSON.parse(a)}catch{return o.warn(`[claude-mem] Worker GET ${s} returned non-JSON response`),null}}function ue(e,s){let o=e.title||"Untitled",c=`${s(e.project)}
1
+ var Z="127.0.0.1",z=["\u{1F527}","\u{1F4D0}","\u{1F50D}","\u{1F4BB}","\u{1F9EA}","\u{1F41B}","\u{1F6E1}\uFE0F","\u2601\uFE0F","\u{1F4E6}","\u{1F3AF}","\u{1F52E}","\u26A1","\u{1F30A}","\u{1F3A8}","\u{1F4CA}","\u{1F680}","\u{1F52C}","\u{1F3D7}\uFE0F","\u{1F4DD}","\u{1F3AD}"];function Q(e){let s=0;for(let o=0;o<e.length;o++)s=(s<<5)-s+e.charCodeAt(o)|0;return z[Math.abs(s)%z.length]}var V="\u{1F99E}",ee="\u2328\uFE0F",ne="Claude Code Session",te="\u{1F980}";function re(e){let s=e?.primary??V,o=e?.claudeCode??ee,a=e?.claudeCodeLabel??ne,c=e?.default??te,d=e?.agents??{};return function(m){if(!m)return c;if(m.startsWith("openclaw-")){let p=m.slice(9);return p?`${d[p]||Q(p)} ${p}`:`${s} openclaw`}if(m==="openclaw")return`${s} openclaw`;let b=a.trim();return b?`${o} ${b} (${m})`:`${o} ${m}`}}var W=Z;function T(e){return`http://${W}:${e}`}var se=3,Y=3e4,h="CLOSED",N=0,H=0,P=!1;function J(e){return h==="CLOSED"?!0:h==="OPEN"?Date.now()-H>=Y?(h="HALF_OPEN",e.info("[claude-mem] Circuit breaker: probing worker connection"),P?!1:(P=!0,!0)):!1:P?!1:(P=!0,!0)}function G(e){h!=="CLOSED"&&e.info("[claude-mem] Worker connection restored \u2014 circuit closed"),h="CLOSED",N=0,P=!1}function x(e){P=!1,N++,(h==="HALF_OPEN"||h==="CLOSED"&&N>=se)&&(h="OPEN",H=Date.now(),e.warn(`[claude-mem] Worker unreachable \u2014 disabling requests for ${Y/1e3}s`))}function oe(){h="CLOSED",N=0,H=0,P=!1}async function X(e,s,o,a){if(!J(a))return null;try{let c=await fetch(`${T(e)}${s}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(o)});return c.ok?(G(a),await c.json()):(x(a),a.warn(`[claude-mem] Worker POST ${s} returned ${c.status}`),null)}catch(c){let d=c instanceof Error?c.message:String(c);return x(a),h!=="OPEN"&&a.warn(`[claude-mem] Worker POST ${s} failed: ${d}`),null}}function ie(e,s,o,a){J(a)&&fetch(`${T(e)}${s}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(o)}).then(c=>{if(!c.ok){x(a),a.warn(`[claude-mem] Worker POST ${s} returned ${c.status}`);return}G(a)}).catch(c=>{let d=c instanceof Error?c.message:String(c);x(a),h!=="OPEN"&&a.warn(`[claude-mem] Worker POST ${s} failed: ${d}`)})}async function q(e,s,o){if(!J(o))return null;try{let a=await fetch(`${T(e)}${s}`);return a.ok?(G(o),await a.text()):(x(o),o.warn(`[claude-mem] Worker GET ${s} returned ${a.status}`),null)}catch(a){let c=a instanceof Error?a.message:String(a);return x(o),h!=="OPEN"&&o.warn(`[claude-mem] Worker GET ${s} failed: ${c}`),null}}async function K(e,s,o){let a=await q(e,s,o);if(!a)return null;try{return JSON.parse(a)}catch{return o.warn(`[claude-mem] Worker GET ${s} returned non-JSON response`),null}}function ae(e,s){let o=e.title||"Untitled",c=`${s(e.project)}
2
2
  **${o}**`;return e.subtitle&&(c+=`
3
- ${e.subtitle}`),c}var ge={telegram:{namespace:"telegram",functionName:"sendMessageTelegram"},whatsapp:{namespace:"whatsapp",functionName:"sendMessageWhatsApp"},discord:{namespace:"discord",functionName:"sendMessageDiscord"},slack:{namespace:"slack",functionName:"sendMessageSlack"},signal:{namespace:"signal",functionName:"sendMessageSignal"},imessage:{namespace:"imessage",functionName:"sendMessageIMessage"},line:{namespace:"line",functionName:"sendMessageLine"}};async function de(e,s,o,a){try{let c=await fetch(`https://api.telegram.org/bot${e}/sendMessage`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({chat_id:s,text:o,parse_mode:"Markdown"})});if(!c.ok){let g=await c.text();a.warn(`[claude-mem] Direct Telegram send failed (${c.status}): ${g}`)}}catch(c){let g=c instanceof Error?c.message:String(c);a.warn(`[claude-mem] Direct Telegram send error: ${g}`)}}function me(e,s,o,a,c){if(c&&s==="telegram")return de(c,o,a,e.logger);let g=ge[s];if(!g)return e.logger.warn(`[claude-mem] Unsupported channel type: ${s}`),Promise.resolve();let w=e.runtime.channel[g.namespace];if(!w)return e.logger.warn(`[claude-mem] Channel "${s}" not available in runtime`),Promise.resolve();let f=w[g.functionName];return f?f(...s==="whatsapp"?[o,a,{verbose:!1}]:[o,a]).catch(b=>{let m=b instanceof Error?b.message:String(b);e.logger.error(`[claude-mem] Failed to send to ${s}: ${m}`)}):(e.logger.warn(`[claude-mem] Channel "${s}" has no ${g.functionName} function`),Promise.resolve())}async function fe(e,s,o,a,c,g,w,f){let v=1e3,b=3e4;for(;!c.signal.aborted;){try{g("reconnecting"),e.logger.info(`[claude-mem] Connecting to SSE stream at ${T(s)}/stream`);let m=await fetch(`${T(s)}/stream`,{signal:c.signal,headers:{Accept:"text/event-stream"}});if(!m.ok)throw new Error(`SSE stream returned HTTP ${m.status}`);if(!m.body)throw new Error("SSE stream response has no body");g("connected"),v=1e3,e.logger.info("[claude-mem] Connected to SSE stream");let A=m.body.getReader(),D=new TextDecoder,k="";for(;;){let{done:B,value:U}=await A.read();if(B)break;k+=D.decode(U,{stream:!0}),k.length>1048576&&(e.logger.warn("[claude-mem] SSE buffer overflow, clearing buffer"),k="");let M=k.split(`
3
+ ${e.subtitle}`),c}var ce={telegram:{namespace:"telegram",functionName:"sendMessageTelegram"},whatsapp:{namespace:"whatsapp",functionName:"sendMessageWhatsApp"},discord:{namespace:"discord",functionName:"sendMessageDiscord"},slack:{namespace:"slack",functionName:"sendMessageSlack"},signal:{namespace:"signal",functionName:"sendMessageSignal"},imessage:{namespace:"imessage",functionName:"sendMessageIMessage"},line:{namespace:"line",functionName:"sendMessageLine"}};async function le(e,s,o,a){try{let c=await fetch(`https://api.telegram.org/bot${e}/sendMessage`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({chat_id:s,text:o,parse_mode:"Markdown"})});if(!c.ok){let d=await c.text();a.warn(`[claude-mem] Direct Telegram send failed (${c.status}): ${d}`)}}catch(c){let d=c instanceof Error?c.message:String(c);a.warn(`[claude-mem] Direct Telegram send error: ${d}`)}}function ue(e,s,o,a,c){if(c&&s==="telegram")return le(c,o,a,e.logger);let d=ce[s];if(!d)return e.logger.warn(`[claude-mem] Unsupported channel type: ${s}`),Promise.resolve();let w=e.runtime.channel[d.namespace];if(!w)return e.logger.warn(`[claude-mem] Channel "${s}" not available in runtime`),Promise.resolve();let m=w[d.functionName];return m?m(...s==="whatsapp"?[o,a,{verbose:!1}]:[o,a]).catch(p=>{let v=p instanceof Error?p.message:String(p);e.logger.error(`[claude-mem] Failed to send to ${s}: ${v}`)}):(e.logger.warn(`[claude-mem] Channel "${s}" has no ${d.functionName} function`),Promise.resolve())}async function de(e,s,o,a,c,d,w,m){let b=1e3,p=3e4;for(;!c.signal.aborted;){try{d("reconnecting"),e.logger.info(`[claude-mem] Connecting to SSE stream at ${T(s)}/stream`);let v=await fetch(`${T(s)}/stream`,{signal:c.signal,headers:{Accept:"text/event-stream"}});if(!v.ok)throw new Error(`SSE stream returned HTTP ${v.status}`);if(!v.body)throw new Error("SSE stream response has no body");d("connected"),b=1e3,e.logger.info("[claude-mem] Connected to SSE stream");let M=v.body.getReader(),j=new TextDecoder,_="";for(;;){let{done:R,value:k}=await M.read();if(R)break;_+=j.decode(k,{stream:!0}),_.length>1048576&&(e.logger.warn("[claude-mem] SSE buffer overflow, clearing buffer"),_="");let L=_.split(`
4
4
 
5
- `);k=M.pop()||"";for(let E of M){let R=E.split(`
6
- `).filter(S=>S.startsWith("data:")).map(S=>S.slice(5).trim());if(R.length===0)continue;let L=R.join(`
7
- `);if(L)try{let S=JSON.parse(L);if(S.type==="new_observation"&&S.observation){let x=ue(S.observation,w);await me(e,o,a,x,f)}}catch(S){let F=S instanceof Error?S.message:String(S);e.logger.warn(`[claude-mem] Failed to parse SSE frame: ${F}`)}}}}catch(m){if(c.signal.aborted)break;g("reconnecting");let A=m instanceof Error?m.message:String(m);e.logger.warn(`[claude-mem] SSE stream error: ${A}. Reconnecting in ${v/1e3}s`)}if(c.signal.aborted)break;await new Promise(m=>setTimeout(m,v)),v=Math.min(v*2,b)}g("disconnected")}function pe(e){let s=e.pluginConfig||{},o=s.workerPort||37777;H=s.workerHost||V;let a=s.project||"openclaw",c=ae(s.observationFeed?.emojis);function g(r){return r.agentId?`openclaw-${r.agentId}`:a}let w=new Map,f=new Map,v=new Map,b=new Map,m=new Map,A=(()=>{let r=Number(s.completionDelayMs);return Number.isFinite(r)?Math.max(0,r):5e3})(),D=s.syncMemoryFile!==!1,k=new Set(s.syncMemoryFileExclude||[]);function B(r){let n=r||"default";return w.has(n)||w.set(n,`openclaw-${n}-${Date.now()}`),w.get(n)}function U(r){if(!D)return!1;let n=r?.agentId;return!(n&&k.has(n))}function M(r){let n=new Set;for(let t of[r.sessionKey,r.conversationId,r.channelId]){let i=typeof t=="string"?t.trim():"";i&&n.add(i)}return n.size===0&&n.add("default"),Array.from(n)}function E(r){let n=M(r),t=n.find(u=>f.has(u));t=t?f.get(t):n[0];let i=v.get(t);i||(i=new Set([t]),v.set(t,i));for(let u of n)i.add(u),f.set(u,t);let l=B(t);for(let u of i)w.set(u,l);return{canonicalKey:t,contentSessionId:l}}function R(r,n,t){let i=Date.now();for(let[d,p]of m)i-p>2e3&&m.delete(d);let l=`${r}::${n}::${t}`,u=m.get(l);return m.set(l,i),typeof u=="number"&&i-u<=2e3}function L(r){let n=M(r),t=n.map(l=>f.get(l)).find(Boolean)||n[0],i=v.get(t)||new Set([t,...n]);for(let l of i)f.delete(l),w.delete(l);v.delete(t),w.delete(t)}function S(r){let n=b.get(r);n&&clearTimeout(n);let t=setTimeout(()=>{b.delete(r),Q(o,"/api/sessions/complete",{contentSessionId:r},e.logger)},A);b.set(r,t)}let F=6e4,x=new Map;async function ne(r){let n=[a],t=r?g(r):null;t&&t!==a&&n.push(t);let i=n.join(","),l=x.get(i);if(l&&Date.now()-l.fetchedAt<F)return l.text;let u=await J(o,`/api/context/inject?projects=${encodeURIComponent(i)}`,e.logger);if(u&&u.trim().length>0){let d=u.trim();return x.set(i,{text:d,fetchedAt:Date.now()}),d}return null}e.on("session_start",async(r,n)=>{let{contentSessionId:t}=E(n);e.logger.info(`[claude-mem] Session tracking initialized: ${t}`)}),e.on("message_received",async(r,n)=>{let{canonicalKey:t,contentSessionId:i}=E(n);e.logger.info(`[claude-mem] Message received \u2014 prompt capture deferred to before_agent_start: session=${t} contentSessionId=${i} hasContent=${!!r.content}`)}),e.on("after_compaction",async(r,n)=>{let{contentSessionId:t}=E(n);e.logger.info(`[claude-mem] Session preserved after compaction: ${t}`)}),e.on("before_agent_start",async(r,n)=>{let{contentSessionId:t}=E(n),i=g(n),l=r.prompt||"agent run";if(R(t,i,l)){e.logger.info(`[claude-mem] Skipping duplicate prompt init: contentSessionId=${t} project=${i}`);return}await Y(o,"/api/sessions/init",{contentSessionId:t,project:i,prompt:l},e.logger),e.logger.info(`[claude-mem] Session initialized via before_agent_start: contentSessionId=${t} project=${i}`)}),e.on("before_prompt_build",async(r,n)=>{if(!U(n))return;let t=await ne(n);if(t)return e.logger.info(`[claude-mem] Context injected via system prompt for agent=${n.agentId??"unknown"}`),{appendSystemContext:t}}),e.on("tool_result_persist",(r,n)=>{e.logger.info(`[claude-mem] tool_result_persist fired: tool=${r.toolName??"unknown"} agent=${n.agentId??"none"} session=${n.sessionKey??"none"}`);let t=r.toolName;if(!t||t.startsWith("memory_"))return;let{canonicalKey:i,contentSessionId:l}=E(n),u="",d=r.message?.content;Array.isArray(d)&&(u=d.filter(C=>(C.type==="tool_result"||C.type==="text")&&"text"in C).map(C=>String(C.text)).join(`
8
- `));let p=1e3;u.length>p&&(u=u.slice(0,p));let y=n.workspaceDir;if(!y){e.logger.warn(`[claude-mem] Skipping observation persist because workspaceDir is unavailable: session=${i} tool=${t}`);return}Q(o,"/api/sessions/observations",{contentSessionId:l,tool_name:t,tool_input:r.params||{},tool_response:u,cwd:y},e.logger)}),e.on("agent_end",async(r,n)=>{let{contentSessionId:t}=E(n),i="";if(Array.isArray(r.messages))for(let l=r.messages.length-1;l>=0;l--){let u=r.messages[l];if(u?.role==="assistant"){typeof u.content=="string"?i=u.content:Array.isArray(u.content)&&(i=u.content.filter(d=>d.type==="text").map(d=>d.text||"").join(`
9
- `));break}}await Y(o,"/api/sessions/summarize",{contentSessionId:t,last_assistant_message:i},e.logger),e.logger.info(`[claude-mem] Scheduling session complete in ${A}ms: ${t}`),S(t)}),e.on("session_end",async(r,n)=>{L(n),e.logger.info("[claude-mem] Session tracking cleaned up")}),e.on("gateway_start",async()=>{le(),w.clear(),x.clear(),m.clear(),f.clear(),v.clear();for(let r of b.values())clearTimeout(r);b.clear(),e.logger.info("[claude-mem] Gateway started \u2014 session tracking reset")});let _=null,N="disconnected",$=null;e.registerService({id:"claude-mem-observation-feed",start:async r=>{_&&(_.abort(),$&&(await $,$=null));let n=s.observationFeed;if(!n?.enabled){e.logger.info("[claude-mem] Observation feed disabled");return}if(!n.channel||!n.to){e.logger.warn("[claude-mem] Observation feed misconfigured \u2014 channel or target missing");return}e.logger.info(`[claude-mem] Observation feed starting \u2014 channel: ${n.channel}, target: ${n.to}`),_=new AbortController,$=fe(e,o,n.channel,n.to,_,t=>{N=t},c,n.botToken)},stop:async r=>{_&&(_.abort(),_=null),$&&(await $,$=null),N="disconnected",e.logger.info("[claude-mem] Observation feed stopped \u2014 SSE connection closed")}});function K(r,n=5){return!Array.isArray(r)||r.length===0?"No results found.":r.slice(0,n).map((t,i)=>{let l=t,u=String(l.title||l.subtitle||l.text||"Untitled"),d=l.project?` [${String(l.project)}]`:"";return`${i+1}. ${u}${d}`}).join(`
10
- `)}function I(r,n=10){let t=Number(r);return Number.isFinite(t)?Math.max(1,Math.min(50,Math.trunc(t))):n}e.registerCommand({name:"claude_mem_feed",description:"Show or toggle Claude-Mem observation feed status",acceptsArgs:!0,handler:async r=>{let n=s.observationFeed;if(!n)return{text:"Observation feed not configured. Add observationFeed to your plugin config."};let t=r.args?.trim();return t==="on"?(e.logger.info("[claude-mem] Feed enable requested via command"),{text:"Feed enable requested. Update observationFeed.enabled in your plugin config to persist."}):t==="off"?(e.logger.info("[claude-mem] Feed disable requested via command"),{text:"Feed disable requested. Update observationFeed.enabled in your plugin config to persist."}):{text:["Claude-Mem Observation Feed",`Enabled: ${n.enabled?"yes":"no"}`,`Channel: ${n.channel||"not set"}`,`Target: ${n.to||"not set"}`,`Connection: ${N}`].join(`
11
- `)}}}),e.registerCommand({name:"claude-mem-search",description:"Search Claude-Mem observations by query",acceptsArgs:!0,handler:async r=>{let n=r.args?.trim()||"";if(!n)return"Usage: /claude-mem-search <query> [limit]";let t=n.split(/\s+/),i=t[t.length-1],l=/^\d+$/.test(i),u=l?I(i,10):10,d=l?t.slice(0,-1).join(" "):n,p=await q(o,`/api/search/observations?query=${encodeURIComponent(d)}&limit=${u}`,e.logger);if(!p)return"Claude-Mem search failed (worker unavailable or invalid response).";let y=Array.isArray(p.items)?p.items:[];return[`Claude-Mem Search: "${d}"`,K(y,u)].join(`
12
- `)}}),e.registerCommand({name:"claude-mem-recent",description:"Show recent Claude-Mem context for a project",acceptsArgs:!0,handler:async r=>{let n=r.args?.trim()||"",t=n?n.split(/\s+/):[],i=t.length>0?t[t.length-1]:"",l=/^\d+$/.test(i),u=l?I(i,3):3,d=l?t.slice(0,-1).join(" "):n,p=new URLSearchParams;p.set("limit",String(u)),d&&p.set("project",d);let y=await q(o,`/api/context/recent?${p.toString()}`,e.logger);if(!y)return"Claude-Mem recent context failed (worker unavailable or invalid response).";let C=Array.isArray(y.session_summaries)?y.session_summaries:[],W=Array.isArray(y.recent_observations)?y.recent_observations:[];return["Claude-Mem Recent Context",`Project: ${d||"(auto)"}`,`Session summaries: ${C.length}`,`Recent observations: ${W.length}`,K(W,Math.min(5,W.length||5))].join(`
13
- `)}}),e.registerCommand({name:"claude-mem-timeline",description:"Find best memory match and show nearby timeline events",acceptsArgs:!0,handler:async r=>{let n=r.args?.trim()||"";if(!n)return"Usage: /claude-mem-timeline <query> [depthBefore] [depthAfter]";let t=n.split(/\s+/),i=5,l=5;t.length>=2&&/^\d+$/.test(t[t.length-1])&&(i=I(t.pop(),5)),t.length>=2&&/^\d+$/.test(t[t.length-1])&&(l=I(t.pop(),5));let u=t.join(" "),d=new URLSearchParams({query:u,mode:"auto",depth_before:String(l),depth_after:String(i)}),p=await q(o,`/api/timeline/by-query?${d.toString()}`,e.logger);if(!p)return"Claude-Mem timeline lookup failed (worker unavailable or invalid response).";let y=Array.isArray(p.timeline)?p.timeline:[],C=p.anchor?String(p.anchor):"(none)";return[`Claude-Mem Timeline: "${u}"`,`Anchor: ${C}`,K(y,8)].join(`
14
- `)}}),e.registerCommand({name:"claude_mem_status",description:"Check Claude-Mem worker health and session status",handler:async()=>{let r=await J(o,"/api/health",e.logger);if(!r)return{text:`Claude-Mem worker unreachable at port ${o}`};try{return{text:["Claude-Mem Worker Status",`Status: ${JSON.parse(r).status||"unknown"}`,`Port: ${o}`,`Active sessions: ${w.size}`,`Observation feed: ${N}`].join(`
15
- `)}}catch{return{text:"Claude-Mem worker responded but returned unexpected data"}}}}),e.logger.info(`[claude-mem] OpenClaw plugin loaded \u2014 v1.0.0 (worker: ${H}:${o})`)}export{pe as default};
5
+ `);_=L.pop()||"";for(let D of L){let F=D.split(`
6
+ `).filter(S=>S.startsWith("data:")).map(S=>S.slice(5).trim());if(F.length===0)continue;let A=F.join(`
7
+ `);if(A)try{let S=JSON.parse(A);if(S.type==="new_observation"&&S.observation){let O=ae(S.observation,w);await ue(e,o,a,O,m)}}catch(S){let C=S instanceof Error?S.message:String(S);e.logger.warn(`[claude-mem] Failed to parse SSE frame: ${C}`)}}}}catch(v){if(c.signal.aborted)break;d("reconnecting");let M=v instanceof Error?v.message:String(v);e.logger.warn(`[claude-mem] SSE stream error: ${M}. Reconnecting in ${b/1e3}s`)}if(c.signal.aborted)break;await new Promise(v=>setTimeout(v,b)),b=Math.min(b*2,p)}d("disconnected")}function ge(e){let s=e.pluginConfig||{},o=s.workerPort||37777;W=s.workerHost||Z;let a=s.project||"openclaw",c=re(s.observationFeed?.emojis);function d(r){return r.agentId?`openclaw-${r.agentId}`:a}let w=new Map,m=new Map,b=new Map,p=new Map,v=s.syncMemoryFile!==!1,M=new Set(s.syncMemoryFileExclude||[]);function j(r){let n=r||"default";return w.has(n)||w.set(n,`openclaw-${n}-${Date.now()}`),w.get(n)}function _(r){if(!v)return!1;let n=r?.agentId;return!(n&&M.has(n))}function R(r){let n=new Set;for(let t of[r.sessionKey,r.conversationId,r.channelId]){let i=typeof t=="string"?t.trim():"";i&&n.add(i)}return n.size===0&&n.add("default"),Array.from(n)}function k(r){let n=R(r),t=n.find(u=>m.has(u));t=t?m.get(t):n[0];let i=b.get(t);i||(i=new Set([t]),b.set(t,i));for(let u of n)i.add(u),m.set(u,t);let l=j(t);for(let u of i)w.set(u,l);return{canonicalKey:t,contentSessionId:l}}function L(r,n,t){let i=Date.now();for(let[g,f]of p)i-f>2e3&&p.delete(g);let l=`${r}::${n}::${t}`,u=p.get(l);return p.set(l,i),typeof u=="number"&&i-u<=2e3}function D(r){let n=R(r),t=n.map(l=>m.get(l)).find(Boolean)||n[0],i=b.get(t)||new Set([t,...n]);for(let l of i)m.delete(l),w.delete(l);b.delete(t),w.delete(t)}let F=6e4,A=new Map;async function S(r){let n=[a],t=r?d(r):null;t&&t!==a&&n.push(t);let i=n.join(","),l=A.get(i);if(l&&Date.now()-l.fetchedAt<F)return l.text;let u=await q(o,`/api/context/inject?projects=${encodeURIComponent(i)}`,e.logger);if(u&&u.trim().length>0){let g=u.trim();return A.set(i,{text:g,fetchedAt:Date.now()}),g}return null}e.on("session_start",async(r,n)=>{let{contentSessionId:t}=k(n);e.logger.info(`[claude-mem] Session tracking initialized: ${t}`)}),e.on("message_received",async(r,n)=>{let{canonicalKey:t,contentSessionId:i}=k(n);e.logger.info(`[claude-mem] Message received \u2014 prompt capture deferred to before_agent_start: session=${t} contentSessionId=${i} hasContent=${!!r.content}`)}),e.on("after_compaction",async(r,n)=>{let{contentSessionId:t}=k(n);e.logger.info(`[claude-mem] Session preserved after compaction: ${t}`)}),e.on("before_agent_start",async(r,n)=>{let{contentSessionId:t}=k(n),i=d(n),l=r.prompt||"agent run";if(L(t,i,l)){e.logger.info(`[claude-mem] Skipping duplicate prompt init: contentSessionId=${t} project=${i}`);return}await X(o,"/api/sessions/init",{contentSessionId:t,project:i,prompt:l},e.logger),e.logger.info(`[claude-mem] Session initialized via before_agent_start: contentSessionId=${t} project=${i}`)}),e.on("before_prompt_build",async(r,n)=>{if(!_(n))return;let t=await S(n);if(t)return e.logger.info(`[claude-mem] Context injected via system prompt for agent=${n.agentId??"unknown"}`),{appendSystemContext:t}}),e.on("tool_result_persist",(r,n)=>{e.logger.info(`[claude-mem] tool_result_persist fired: tool=${r.toolName??"unknown"} agent=${n.agentId??"none"} session=${n.sessionKey??"none"}`);let t=r.toolName;if(!t||t.startsWith("memory_"))return;let{canonicalKey:i,contentSessionId:l}=k(n),u="",g=r.message?.content;Array.isArray(g)&&(u=g.filter(E=>(E.type==="tool_result"||E.type==="text")&&"text"in E).map(E=>String(E.text)).join(`
8
+ `));let f=1e3;u.length>f&&(u=u.slice(0,f));let y=n.workspaceDir;if(!y){e.logger.warn(`[claude-mem] Skipping observation persist because workspaceDir is unavailable: session=${i} tool=${t}`);return}ie(o,"/api/sessions/observations",{contentSessionId:l,tool_name:t,tool_input:r.params||{},tool_response:u,cwd:y},e.logger)}),e.on("agent_end",async(r,n)=>{let{contentSessionId:t}=k(n),i="";if(Array.isArray(r.messages))for(let l=r.messages.length-1;l>=0;l--){let u=r.messages[l];if(u?.role==="assistant"){typeof u.content=="string"?i=u.content:Array.isArray(u.content)&&(i=u.content.filter(g=>g.type==="text").map(g=>g.text||"").join(`
9
+ `));break}}await X(o,"/api/sessions/summarize",{contentSessionId:t,last_assistant_message:i},e.logger)}),e.on("session_end",async(r,n)=>{D(n),e.logger.info("[claude-mem] Session tracking cleaned up")}),e.on("gateway_start",async()=>{oe(),w.clear(),A.clear(),p.clear(),m.clear(),b.clear(),e.logger.info("[claude-mem] Gateway started \u2014 session tracking reset")});let C=null,O="disconnected",$=null;e.registerService({id:"claude-mem-observation-feed",start:async r=>{C&&(C.abort(),$&&(await $,$=null));let n=s.observationFeed;if(!n?.enabled){e.logger.info("[claude-mem] Observation feed disabled");return}if(!n.channel||!n.to){e.logger.warn("[claude-mem] Observation feed misconfigured \u2014 channel or target missing");return}e.logger.info(`[claude-mem] Observation feed starting \u2014 channel: ${n.channel}, target: ${n.to}`),C=new AbortController,$=de(e,o,n.channel,n.to,C,t=>{O=t},c,n.botToken)},stop:async r=>{C&&(C.abort(),C=null),$&&(await $,$=null),O="disconnected",e.logger.info("[claude-mem] Observation feed stopped \u2014 SSE connection closed")}});function B(r,n=5){return!Array.isArray(r)||r.length===0?"No results found.":r.slice(0,n).map((t,i)=>{let l=t,u=String(l.title||l.subtitle||l.text||"Untitled"),g=l.project?` [${String(l.project)}]`:"";return`${i+1}. ${u}${g}`}).join(`
10
+ `)}function I(r,n=10){let t=Number(r);return Number.isFinite(t)?Math.max(1,Math.min(50,Math.trunc(t))):n}e.registerCommand({name:"claude_mem_feed",description:"Show or toggle Claude-Mem observation feed status",acceptsArgs:!0,handler:async r=>{let n=s.observationFeed;if(!n)return{text:"Observation feed not configured. Add observationFeed to your plugin config."};let t=r.args?.trim();return t==="on"?(e.logger.info("[claude-mem] Feed enable requested via command"),{text:"Feed enable requested. Update observationFeed.enabled in your plugin config to persist."}):t==="off"?(e.logger.info("[claude-mem] Feed disable requested via command"),{text:"Feed disable requested. Update observationFeed.enabled in your plugin config to persist."}):{text:["Claude-Mem Observation Feed",`Enabled: ${n.enabled?"yes":"no"}`,`Channel: ${n.channel||"not set"}`,`Target: ${n.to||"not set"}`,`Connection: ${O}`].join(`
11
+ `)}}}),e.registerCommand({name:"claude-mem-search",description:"Search Claude-Mem observations by query",acceptsArgs:!0,handler:async r=>{let n=r.args?.trim()||"";if(!n)return"Usage: /claude-mem-search <query> [limit]";let t=n.split(/\s+/),i=t[t.length-1],l=/^\d+$/.test(i),u=l?I(i,10):10,g=l?t.slice(0,-1).join(" "):n,f=await K(o,`/api/search/observations?query=${encodeURIComponent(g)}&limit=${u}`,e.logger);if(!f)return"Claude-Mem search failed (worker unavailable or invalid response).";let y=Array.isArray(f.items)?f.items:[];return[`Claude-Mem Search: "${g}"`,B(y,u)].join(`
12
+ `)}}),e.registerCommand({name:"claude-mem-recent",description:"Show recent Claude-Mem context for a project",acceptsArgs:!0,handler:async r=>{let n=r.args?.trim()||"",t=n?n.split(/\s+/):[],i=t.length>0?t[t.length-1]:"",l=/^\d+$/.test(i),u=l?I(i,3):3,g=l?t.slice(0,-1).join(" "):n,f=new URLSearchParams;f.set("limit",String(u)),g&&f.set("project",g);let y=await K(o,`/api/context/recent?${f.toString()}`,e.logger);if(!y)return"Claude-Mem recent context failed (worker unavailable or invalid response).";let E=Array.isArray(y.session_summaries)?y.session_summaries:[],U=Array.isArray(y.recent_observations)?y.recent_observations:[];return["Claude-Mem Recent Context",`Project: ${g||"(auto)"}`,`Session summaries: ${E.length}`,`Recent observations: ${U.length}`,B(U,Math.min(5,U.length||5))].join(`
13
+ `)}}),e.registerCommand({name:"claude-mem-timeline",description:"Find best memory match and show nearby timeline events",acceptsArgs:!0,handler:async r=>{let n=r.args?.trim()||"";if(!n)return"Usage: /claude-mem-timeline <query> [depthBefore] [depthAfter]";let t=n.split(/\s+/),i=5,l=5;t.length>=2&&/^\d+$/.test(t[t.length-1])&&(i=I(t.pop(),5)),t.length>=2&&/^\d+$/.test(t[t.length-1])&&(l=I(t.pop(),5));let u=t.join(" "),g=new URLSearchParams({query:u,mode:"auto",depth_before:String(l),depth_after:String(i)}),f=await K(o,`/api/timeline/by-query?${g.toString()}`,e.logger);if(!f)return"Claude-Mem timeline lookup failed (worker unavailable or invalid response).";let y=Array.isArray(f.timeline)?f.timeline:[],E=f.anchor?String(f.anchor):"(none)";return[`Claude-Mem Timeline: "${u}"`,`Anchor: ${E}`,B(y,8)].join(`
14
+ `)}}),e.registerCommand({name:"claude_mem_status",description:"Check Claude-Mem worker health and session status",handler:async()=>{let r=await q(o,"/api/health",e.logger);if(!r)return{text:`Claude-Mem worker unreachable at port ${o}`};try{return{text:["Claude-Mem Worker Status",`Status: ${JSON.parse(r).status||"unknown"}`,`Port: ${o}`,`Active sessions: ${w.size}`,`Observation feed: ${O}`].join(`
15
+ `)}}catch{return{text:"Claude-Mem worker responded but returned unexpected data"}}}}),e.logger.info(`[claude-mem] OpenClaw plugin loaded \u2014 v1.0.0 (worker: ${W}:${o})`)}export{ge as default};
@@ -266,12 +266,6 @@ describe("Observation I/O event handlers", () => {
266
266
  return;
267
267
  }
268
268
 
269
- if (req.url === "/api/sessions/complete") {
270
- res.writeHead(200, { "Content-Type": "application/json" });
271
- res.end(JSON.stringify({ status: "completed" }));
272
- return;
273
- }
274
-
275
269
  if (req.url?.startsWith("/api/context/inject")) {
276
270
  res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
277
271
  res.end("# Claude-Mem Context\n\n## Timeline\n- Session 1: Did some work");
@@ -446,8 +440,7 @@ describe("Observation I/O event handlers", () => {
446
440
  assert.ok(summarizeRequest!.body.contentSessionId.startsWith("openclaw-summarize-test-"));
447
441
 
448
442
  const completeRequest = receivedRequests.find((r) => r.url === "/api/sessions/complete");
449
- assert.ok(completeRequest, "should send complete to worker");
450
- assert.ok(completeRequest!.body.contentSessionId.startsWith("openclaw-summarize-test-"));
443
+ assert.ok(!completeRequest, "should not send complete (worker self-completes)");
451
444
  });
452
445
 
453
446
  it("agent_end extracts text from array content", async () => {
@@ -644,12 +644,7 @@ export default function claudeMemPlugin(api: OpenClawPluginApi): void {
644
644
  const sessionIds = new Map<string, string>();
645
645
  const canonicalSessionKeys = new Map<string, string>();
646
646
  const sessionAliasesByCanonicalKey = new Map<string, Set<string>>();
647
- const pendingCompletionTimers = new Map<string, ReturnType<typeof setTimeout>>();
648
647
  const recentPromptInits = new Map<string, number>();
649
- const completionDelayMs = (() => {
650
- const val = Number((userConfig as Record<string, unknown>).completionDelayMs);
651
- return Number.isFinite(val) ? Math.max(0, val) : 5000;
652
- })();
653
648
  const syncMemoryFile = userConfig.syncMemoryFile !== false; // default true
654
649
  const syncMemoryFileExclude = new Set(userConfig.syncMemoryFileExclude || []);
655
650
 
@@ -733,18 +728,6 @@ export default function claudeMemPlugin(api: OpenClawPluginApi): void {
733
728
  sessionIds.delete(canonicalKey);
734
729
  }
735
730
 
736
- function scheduleSessionComplete(contentSessionId: string): void {
737
- const existingTimer = pendingCompletionTimers.get(contentSessionId);
738
- if (existingTimer) clearTimeout(existingTimer);
739
- const timer = setTimeout(() => {
740
- pendingCompletionTimers.delete(contentSessionId);
741
- workerPostFireAndForget(workerPort, "/api/sessions/complete", {
742
- contentSessionId,
743
- }, api.logger);
744
- }, completionDelayMs);
745
- pendingCompletionTimers.set(contentSessionId, timer);
746
- }
747
-
748
731
  // TTL cache for context injection to avoid re-fetching on every LLM turn.
749
732
  // before_prompt_build fires on every turn; caching for 60s keeps the worker
750
733
  // load manageable while still picking up new observations reasonably quickly.
@@ -898,7 +881,7 @@ export default function claudeMemPlugin(api: OpenClawPluginApi): void {
898
881
  });
899
882
 
900
883
  // ------------------------------------------------------------------
901
- // Event: agent_end — summarize and complete session
884
+ // Event: agent_end — summarize session (worker self-completes)
902
885
  // ------------------------------------------------------------------
903
886
  api.on("agent_end", async (event, ctx) => {
904
887
  const { contentSessionId } = rememberSessionContext(ctx);
@@ -922,16 +905,12 @@ export default function claudeMemPlugin(api: OpenClawPluginApi): void {
922
905
  }
923
906
  }
924
907
 
925
- // Await summarize so the worker receives it before complete.
926
- // This also gives in-flight tool_result_persist observations time to arrive
927
- // (they use fire-and-forget and may still be in transit).
908
+ // Send summarize. The worker self-completes the session when its SDK-agent
909
+ // generator drains; no explicit complete call needed.
928
910
  await workerPost(workerPort, "/api/sessions/summarize", {
929
911
  contentSessionId,
930
912
  last_assistant_message: lastAssistantMessage,
931
913
  }, api.logger);
932
-
933
- api.logger.info(`[claude-mem] Scheduling session complete in ${completionDelayMs}ms: ${contentSessionId}`);
934
- scheduleSessionComplete(contentSessionId);
935
914
  });
936
915
 
937
916
  // ------------------------------------------------------------------
@@ -952,10 +931,6 @@ export default function claudeMemPlugin(api: OpenClawPluginApi): void {
952
931
  recentPromptInits.clear();
953
932
  canonicalSessionKeys.clear();
954
933
  sessionAliasesByCanonicalKey.clear();
955
- for (const timer of pendingCompletionTimers.values()) {
956
- clearTimeout(timer);
957
- }
958
- pendingCompletionTimers.clear();
959
934
  api.logger.info("[claude-mem] Gateway started — session tracking reset");
960
935
  });
961
936
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-mem",
3
- "version": "12.4.2",
3
+ "version": "12.4.4",
4
4
  "description": "Memory compression system for Claude Code - persist context across sessions",
5
5
  "keywords": [
6
6
  "claude",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-mem",
3
- "version": "12.4.2",
3
+ "version": "12.4.4",
4
4
  "description": "Persistent memory system for Claude Code - seamlessly preserve context across sessions",
5
5
  "author": {
6
6
  "name": "Alex Newman"
@@ -88,18 +88,6 @@
88
88
  }
89
89
  ]
90
90
  }
91
- ],
92
- "SessionEnd": [
93
- {
94
- "hooks": [
95
- {
96
- "type": "command",
97
- "shell": "bash",
98
- "command": "export PATH=\"$($SHELL -lc 'echo $PATH' 2>/dev/null):$PATH\"; _R=\"${CLAUDE_PLUGIN_ROOT}\"; [ -z \"$_R\" ] && _R=$(ls -dt $HOME/.claude/plugins/cache/thedotmack/claude-mem/[0-9]*/ 2>/dev/null | head -1); _R=\"${_R%/}\"; [ -z \"$_R\" ] && _R=\"$HOME/.claude/plugins/marketplaces/thedotmack/plugin\"; node \"$_R/scripts/bun-runner.js\" \"$_R/scripts/worker-service.cjs\" hook claude-code session-complete",
99
- "timeout": 30
100
- }
101
- ]
102
- }
103
91
  ]
104
92
  }
105
93
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-mem-plugin",
3
- "version": "12.4.2",
3
+ "version": "12.4.4",
4
4
  "private": true,
5
5
  "description": "Runtime dependencies for claude-mem bundled hooks",
6
6
  "type": "module",
@@ -671,7 +671,7 @@ ${o.stack}`:` ${o.message}`:this.getLevel()===0&&typeof o=="object"?u=`
671
671
  content_session_id, prompt_number, prompt_text,
672
672
  created_at, created_at_epoch
673
673
  ) VALUES (?, ?, ?, ?, ?)
674
- `).run(e.content_session_id,e.prompt_number,e.prompt_text,e.created_at,e.created_at_epoch).lastInsertRowid}}};var Ue=L(require("path"),1),xe=require("os");var C=require("fs"),x=require("path"),oe=require("os"),W=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-sonnet-4-6",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:String(37700+(process.getuid?.()??77)%100),CLAUDE_MEM_WORKER_HOST:"127.0.0.1",CLAUDE_MEM_SKIP_TOOLS:"ListMcpResourcesTool,SlashCommand,Skill,TodoWrite,AskUserQuestion",CLAUDE_MEM_PROVIDER:"claude",CLAUDE_MEM_CLAUDE_AUTH_METHOD:"cli",CLAUDE_MEM_GEMINI_API_KEY:"",CLAUDE_MEM_GEMINI_MODEL:"gemini-2.5-flash-lite",CLAUDE_MEM_GEMINI_RATE_LIMITING_ENABLED:"true",CLAUDE_MEM_GEMINI_MAX_CONTEXT_MESSAGES:"20",CLAUDE_MEM_GEMINI_MAX_TOKENS:"100000",CLAUDE_MEM_OPENROUTER_API_KEY:"",CLAUDE_MEM_OPENROUTER_MODEL:"xiaomi/mimo-v2-flash:free",CLAUDE_MEM_OPENROUTER_SITE_URL:"",CLAUDE_MEM_OPENROUTER_APP_NAME:"claude-mem",CLAUDE_MEM_OPENROUTER_MAX_CONTEXT_MESSAGES:"20",CLAUDE_MEM_OPENROUTER_MAX_TOKENS:"100000",CLAUDE_MEM_DATA_DIR:(0,x.join)((0,oe.homedir)(),".claude-mem"),CLAUDE_MEM_LOG_LEVEL:"INFO",CLAUDE_MEM_PYTHON_VERSION:"3.13",CLAUDE_CODE_PATH:"",CLAUDE_MEM_MODE:"code",CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS:"false",CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS:"false",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT:"false",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT:"true",CLAUDE_MEM_CONTEXT_FULL_COUNT:"0",CLAUDE_MEM_CONTEXT_FULL_FIELD:"narrative",CLAUDE_MEM_CONTEXT_SESSION_COUNT:"10",CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY:"true",CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE:"false",CLAUDE_MEM_CONTEXT_SHOW_TERMINAL_OUTPUT:"true",CLAUDE_MEM_FOLDER_CLAUDEMD_ENABLED:"false",CLAUDE_MEM_FOLDER_USE_LOCAL_MD:"false",CLAUDE_MEM_TRANSCRIPTS_ENABLED:"true",CLAUDE_MEM_TRANSCRIPTS_CONFIG_PATH:(0,x.join)((0,oe.homedir)(),".claude-mem","transcript-watch.json"),CLAUDE_MEM_MAX_CONCURRENT_AGENTS:"2",CLAUDE_MEM_HOOK_FAIL_LOUD_THRESHOLD:"3",CLAUDE_MEM_EXCLUDED_PROJECTS:"",CLAUDE_MEM_FOLDER_MD_EXCLUDE:"[]",CLAUDE_MEM_SEMANTIC_INJECT:"false",CLAUDE_MEM_SEMANTIC_INJECT_LIMIT:"5",CLAUDE_MEM_TIER_ROUTING_ENABLED:"true",CLAUDE_MEM_TIER_SIMPLE_MODEL:"haiku",CLAUDE_MEM_TIER_SUMMARY_MODEL:"",CLAUDE_MEM_CHROMA_ENABLED:"true",CLAUDE_MEM_CHROMA_MODE:"local",CLAUDE_MEM_CHROMA_HOST:"127.0.0.1",CLAUDE_MEM_CHROMA_PORT:"8000",CLAUDE_MEM_CHROMA_SSL:"false",CLAUDE_MEM_CHROMA_API_KEY:"",CLAUDE_MEM_CHROMA_TENANT:"default_tenant",CLAUDE_MEM_CHROMA_DATABASE:"default_database",CLAUDE_MEM_TELEGRAM_ENABLED:"true",CLAUDE_MEM_TELEGRAM_BOT_TOKEN:"",CLAUDE_MEM_TELEGRAM_CHAT_ID:"",CLAUDE_MEM_TELEGRAM_TRIGGER_TYPES:"security_alert",CLAUDE_MEM_TELEGRAM_TRIGGER_CONCEPTS:""};static getAllDefaults(){return{...this.DEFAULTS}}static get(e){return process.env[e]??this.DEFAULTS[e]}static getInt(e){let t=this.get(e);return parseInt(t,10)}static getBool(e){let t=this.get(e);return t==="true"||t===!0}static applyEnvOverrides(e){let t={...e};for(let s of Object.keys(this.DEFAULTS))process.env[s]!==void 0&&(t[s]=process.env[s]);return t}static loadFromFile(e){try{if(!(0,C.existsSync)(e)){let i=this.getAllDefaults();try{let a=(0,x.dirname)(e);(0,C.existsSync)(a)||(0,C.mkdirSync)(a,{recursive:!0}),(0,C.writeFileSync)(e,JSON.stringify(i,null,2),"utf-8"),console.log("[SETTINGS] Created settings file with defaults:",e)}catch(a){console.warn("[SETTINGS] Failed to create settings file, using in-memory defaults:",e,a instanceof Error?a.message:String(a))}return this.applyEnvOverrides(i)}let t=(0,C.readFileSync)(e,"utf-8"),s=JSON.parse(t),n=s;if(s.env&&typeof s.env=="object"){n=s.env;try{(0,C.writeFileSync)(e,JSON.stringify(n,null,2),"utf-8"),console.log("[SETTINGS] Migrated settings file from nested to flat schema:",e)}catch(i){console.warn("[SETTINGS] Failed to auto-migrate settings file:",e,i instanceof Error?i.message:String(i))}}let o={...this.DEFAULTS};for(let i of Object.keys(this.DEFAULTS))n[i]!==void 0&&(o[i]=n[i]);return this.applyEnvOverrides(o)}catch(t){return console.warn("[SETTINGS] Failed to load settings, using defaults:",e,t instanceof Error?t.message:String(t)),this.applyEnvOverrides(this.getAllDefaults())}}};var k=require("fs"),q=require("path");var A=class r{static instance=null;activeMode=null;modesDir;constructor(){let e=Ce(),t=[(0,q.join)(e,"modes"),(0,q.join)(e,"..","plugin","modes")],s=t.find(n=>(0,k.existsSync)(n));this.modesDir=s||t[0]}static getInstance(){return r.instance||(r.instance=new r),r.instance}parseInheritance(e){let t=e.split("--");if(t.length===1)return{hasParent:!1,parentId:"",overrideId:""};if(t.length>2)throw new Error(`Invalid mode inheritance: ${e}. Only one level of inheritance supported (parent--override)`);return{hasParent:!0,parentId:t[0],overrideId:e}}isPlainObject(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}deepMerge(e,t){let s={...e};for(let n in t){let o=t[n],i=e[n];this.isPlainObject(o)&&this.isPlainObject(i)?s[n]=this.deepMerge(i,o):s[n]=o}return s}loadModeFile(e){let t=(0,q.join)(this.modesDir,`${e}.json`);if(!(0,k.existsSync)(t))throw new Error(`Mode file not found: ${t}`);let s=(0,k.readFileSync)(t,"utf-8");return JSON.parse(s)}loadMode(e){let t=this.parseInheritance(e);if(!t.hasParent)try{let d=this.loadModeFile(e);return this.activeMode=d,c.debug("SYSTEM",`Loaded mode: ${d.name} (${e})`,void 0,{types:d.observation_types.map(m=>m.id),concepts:d.observation_concepts.map(m=>m.id)}),d}catch(d){if(d instanceof Error?c.warn("WORKER",`Mode file not found: ${e}, falling back to 'code'`,{message:d.message}):c.warn("WORKER",`Mode file not found: ${e}, falling back to 'code'`,{error:String(d)}),e==="code")throw new Error("Critical: code.json mode file missing");return this.loadMode("code")}let{parentId:s,overrideId:n}=t,o;try{o=this.loadMode(s)}catch(d){d instanceof Error?c.warn("WORKER",`Parent mode '${s}' not found for ${e}, falling back to 'code'`,{message:d.message}):c.warn("WORKER",`Parent mode '${s}' not found for ${e}, falling back to 'code'`,{error:String(d)}),o=this.loadMode("code")}let i;try{i=this.loadModeFile(n),c.debug("SYSTEM",`Loaded override file: ${n} for parent ${s}`)}catch(d){return d instanceof Error?c.warn("WORKER",`Override file '${n}' not found, using parent mode '${s}' only`,{message:d.message}):c.warn("WORKER",`Override file '${n}' not found, using parent mode '${s}' only`,{error:String(d)}),this.activeMode=o,o}if(!i)return c.warn("SYSTEM",`Invalid override file: ${n}, using parent mode '${s}' only`),this.activeMode=o,o;let a=this.deepMerge(o,i);return this.activeMode=a,c.debug("SYSTEM",`Loaded mode with inheritance: ${a.name} (${e} = ${s} + ${n})`,void 0,{parent:s,override:n,types:a.observation_types.map(d=>d.id),concepts:a.observation_concepts.map(d=>d.id)}),a}getActiveMode(){if(!this.activeMode)throw new Error("No mode loaded. Call loadMode() first.");return this.activeMode}getObservationTypes(){return this.getActiveMode().observation_types}getObservationConcepts(){return this.getActiveMode().observation_concepts}getTypeIcon(e){return this.getObservationTypes().find(s=>s.id===e)?.emoji||"\u{1F4DD}"}getWorkEmoji(e){return this.getObservationTypes().find(s=>s.id===e)?.work_emoji||"\u{1F4DD}"}validateType(e){return this.getObservationTypes().some(t=>t.id===e)}getTypeLabel(e){return this.getObservationTypes().find(s=>s.id===e)?.label||e}};function ie(){let r=Ue.default.join((0,xe.homedir)(),".claude-mem","settings.json"),e=W.loadFromFile(r),t=A.getInstance().getActiveMode(),s=new Set(t.observation_types.map(o=>o.id)),n=new Set(t.observation_concepts.map(o=>o.id));return{totalObservationCount:parseInt(e.CLAUDE_MEM_CONTEXT_OBSERVATIONS,10),fullObservationCount:parseInt(e.CLAUDE_MEM_CONTEXT_FULL_COUNT,10),sessionCount:parseInt(e.CLAUDE_MEM_CONTEXT_SESSION_COUNT,10),showReadTokens:e.CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS==="true",showWorkTokens:e.CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS==="true",showSavingsAmount:e.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT==="true",showSavingsPercent:e.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT==="true",observationTypes:s,observationConcepts:n,fullObservationField:e.CLAUDE_MEM_CONTEXT_FULL_FIELD,showLastSummary:e.CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY==="true",showLastMessage:e.CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE==="true"}}var _={reset:"\x1B[0m",bright:"\x1B[1m",dim:"\x1B[2m",cyan:"\x1B[36m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",magenta:"\x1B[35m",gray:"\x1B[90m",red:"\x1B[31m"},ke=4,ae=1;function de(r){let e=(r.title?.length||0)+(r.subtitle?.length||0)+(r.narrative?.length||0)+JSON.stringify(r.facts||[]).length;return Math.ceil(e/ke)}function _e(r){let e=r.length,t=r.reduce((i,a)=>i+de(a),0),s=r.reduce((i,a)=>i+(a.discovery_tokens||0),0),n=s-t,o=s>0?Math.round(n/s*100):0;return{totalObservations:e,totalReadTokens:t,totalDiscoveryTokens:s,savings:n,savingsPercent:o}}function Bt(r){return A.getInstance().getWorkEmoji(r)}function w(r,e){let t=de(r),s=r.discovery_tokens||0,n=Bt(r.type),o=s>0?`${n} ${s.toLocaleString()}`:"-";return{readTokens:t,discoveryTokens:s,discoveryDisplay:o,workEmoji:n}}function Y(r){return r.showReadTokens||r.showWorkTokens||r.showSavingsAmount||r.showSavingsPercent}var Fe=L(require("path"),1),V=require("fs");var Wt=["private","claude-mem-context","system_instruction","system-instruction","persisted-output","system-reminder"],Ks=new RegExp(`<(${Wt.join("|")})\\b[^>]*>[\\s\\S]*?</\\1>`,"g"),we=/<system-reminder>[\s\S]*?<\/system-reminder>/g;var qt=["task-notification"],Js=new RegExp(`^\\s*<(${qt.join("|")})\\b[^>]*>[\\s\\S]*</\\1>\\s*$`);function ue(r,e,t){let s=Array.from(t.observationTypes),n=s.map(()=>"?").join(","),o=Array.from(t.observationConcepts),i=o.map(()=>"?").join(",");return r.db.prepare(`
674
+ `).run(e.content_session_id,e.prompt_number,e.prompt_text,e.created_at,e.created_at_epoch).lastInsertRowid}}};var Ue=L(require("path"),1),xe=require("os");var C=require("fs"),x=require("path"),oe=require("os"),W=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-sonnet-4-6",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:String(37700+(process.getuid?.()??77)%100),CLAUDE_MEM_WORKER_HOST:"127.0.0.1",CLAUDE_MEM_SKIP_TOOLS:"ListMcpResourcesTool,SlashCommand,Skill,TodoWrite,AskUserQuestion",CLAUDE_MEM_PROVIDER:"claude",CLAUDE_MEM_CLAUDE_AUTH_METHOD:"cli",CLAUDE_MEM_GEMINI_API_KEY:"",CLAUDE_MEM_GEMINI_MODEL:"gemini-2.5-flash-lite",CLAUDE_MEM_GEMINI_RATE_LIMITING_ENABLED:"true",CLAUDE_MEM_GEMINI_MAX_CONTEXT_MESSAGES:"20",CLAUDE_MEM_GEMINI_MAX_TOKENS:"100000",CLAUDE_MEM_OPENROUTER_API_KEY:"",CLAUDE_MEM_OPENROUTER_MODEL:"xiaomi/mimo-v2-flash:free",CLAUDE_MEM_OPENROUTER_SITE_URL:"",CLAUDE_MEM_OPENROUTER_APP_NAME:"claude-mem",CLAUDE_MEM_OPENROUTER_MAX_CONTEXT_MESSAGES:"20",CLAUDE_MEM_OPENROUTER_MAX_TOKENS:"100000",CLAUDE_MEM_DATA_DIR:(0,x.join)((0,oe.homedir)(),".claude-mem"),CLAUDE_MEM_LOG_LEVEL:"INFO",CLAUDE_MEM_PYTHON_VERSION:"3.13",CLAUDE_CODE_PATH:"",CLAUDE_MEM_MODE:"code",CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS:"false",CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS:"false",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT:"false",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT:"true",CLAUDE_MEM_CONTEXT_FULL_COUNT:"0",CLAUDE_MEM_CONTEXT_FULL_FIELD:"narrative",CLAUDE_MEM_CONTEXT_SESSION_COUNT:"10",CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY:"true",CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE:"false",CLAUDE_MEM_CONTEXT_SHOW_TERMINAL_OUTPUT:"true",CLAUDE_MEM_FOLDER_CLAUDEMD_ENABLED:"false",CLAUDE_MEM_FOLDER_USE_LOCAL_MD:"false",CLAUDE_MEM_TRANSCRIPTS_ENABLED:"true",CLAUDE_MEM_TRANSCRIPTS_CONFIG_PATH:(0,x.join)((0,oe.homedir)(),".claude-mem","transcript-watch.json"),CLAUDE_MEM_MAX_CONCURRENT_AGENTS:"2",CLAUDE_MEM_HOOK_FAIL_LOUD_THRESHOLD:"3",CLAUDE_MEM_EXCLUDED_PROJECTS:"",CLAUDE_MEM_FOLDER_MD_EXCLUDE:"[]",CLAUDE_MEM_SEMANTIC_INJECT:"false",CLAUDE_MEM_SEMANTIC_INJECT_LIMIT:"5",CLAUDE_MEM_TIER_ROUTING_ENABLED:"true",CLAUDE_MEM_TIER_SIMPLE_MODEL:"haiku",CLAUDE_MEM_TIER_SUMMARY_MODEL:"",CLAUDE_MEM_CHROMA_ENABLED:"true",CLAUDE_MEM_CHROMA_MODE:"local",CLAUDE_MEM_CHROMA_HOST:"127.0.0.1",CLAUDE_MEM_CHROMA_PORT:"8000",CLAUDE_MEM_CHROMA_SSL:"false",CLAUDE_MEM_CHROMA_API_KEY:"",CLAUDE_MEM_CHROMA_TENANT:"default_tenant",CLAUDE_MEM_CHROMA_DATABASE:"default_database",CLAUDE_MEM_TELEGRAM_ENABLED:"true",CLAUDE_MEM_TELEGRAM_BOT_TOKEN:"",CLAUDE_MEM_TELEGRAM_CHAT_ID:"",CLAUDE_MEM_TELEGRAM_TRIGGER_TYPES:"security_alert",CLAUDE_MEM_TELEGRAM_TRIGGER_CONCEPTS:""};static getAllDefaults(){return{...this.DEFAULTS}}static get(e){return process.env[e]??this.DEFAULTS[e]}static getInt(e){let t=this.get(e);return parseInt(t,10)}static getBool(e){let t=this.get(e);return t==="true"||t===!0}static applyEnvOverrides(e){let t={...e};for(let s of Object.keys(this.DEFAULTS))process.env[s]!==void 0&&(t[s]=process.env[s]);return t}static loadFromFile(e){try{if(!(0,C.existsSync)(e)){let i=this.getAllDefaults();try{let a=(0,x.dirname)(e);(0,C.existsSync)(a)||(0,C.mkdirSync)(a,{recursive:!0}),(0,C.writeFileSync)(e,JSON.stringify(i,null,2),"utf-8"),console.log("[SETTINGS] Created settings file with defaults:",e)}catch(a){console.warn("[SETTINGS] Failed to create settings file, using in-memory defaults:",e,a instanceof Error?a.message:String(a))}return this.applyEnvOverrides(i)}let t=(0,C.readFileSync)(e,"utf-8"),s=JSON.parse(t),n=s;if(s.env&&typeof s.env=="object"){n=s.env;try{(0,C.writeFileSync)(e,JSON.stringify(n,null,2),"utf-8"),console.log("[SETTINGS] Migrated settings file from nested to flat schema:",e)}catch(i){console.warn("[SETTINGS] Failed to auto-migrate settings file:",e,i instanceof Error?i.message:String(i))}}let o={...this.DEFAULTS};for(let i of Object.keys(this.DEFAULTS))n[i]!==void 0&&(o[i]=n[i]);return this.applyEnvOverrides(o)}catch(t){return console.warn("[SETTINGS] Failed to load settings, using defaults:",e,t instanceof Error?t.message:String(t)),this.applyEnvOverrides(this.getAllDefaults())}}};var k=require("fs"),q=require("path");var A=class r{static instance=null;activeMode=null;modesDir;constructor(){let e=Ce(),t=[(0,q.join)(e,"modes"),(0,q.join)(e,"..","plugin","modes")],s=t.find(n=>(0,k.existsSync)(n));this.modesDir=s||t[0]}static getInstance(){return r.instance||(r.instance=new r),r.instance}parseInheritance(e){let t=e.split("--");if(t.length===1)return{hasParent:!1,parentId:"",overrideId:""};if(t.length>2)throw new Error(`Invalid mode inheritance: ${e}. Only one level of inheritance supported (parent--override)`);return{hasParent:!0,parentId:t[0],overrideId:e}}isPlainObject(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}deepMerge(e,t){let s={...e};for(let n in t){let o=t[n],i=e[n];this.isPlainObject(o)&&this.isPlainObject(i)?s[n]=this.deepMerge(i,o):s[n]=o}return s}loadModeFile(e){let t=(0,q.join)(this.modesDir,`${e}.json`);if(!(0,k.existsSync)(t))throw new Error(`Mode file not found: ${t}`);let s=(0,k.readFileSync)(t,"utf-8");return JSON.parse(s)}loadMode(e){let t=this.parseInheritance(e);if(!t.hasParent)try{let d=this.loadModeFile(e);return this.activeMode=d,c.debug("SYSTEM",`Loaded mode: ${d.name} (${e})`,void 0,{types:d.observation_types.map(m=>m.id),concepts:d.observation_concepts.map(m=>m.id)}),d}catch(d){if(d instanceof Error?c.warn("WORKER",`Mode file not found: ${e}, falling back to 'code'`,{message:d.message}):c.warn("WORKER",`Mode file not found: ${e}, falling back to 'code'`,{error:String(d)}),e==="code")throw new Error("Critical: code.json mode file missing");return this.loadMode("code")}let{parentId:s,overrideId:n}=t,o;try{o=this.loadMode(s)}catch(d){d instanceof Error?c.warn("WORKER",`Parent mode '${s}' not found for ${e}, falling back to 'code'`,{message:d.message}):c.warn("WORKER",`Parent mode '${s}' not found for ${e}, falling back to 'code'`,{error:String(d)}),o=this.loadMode("code")}let i;try{i=this.loadModeFile(n),c.debug("SYSTEM",`Loaded override file: ${n} for parent ${s}`)}catch(d){return d instanceof Error?c.warn("WORKER",`Override file '${n}' not found, using parent mode '${s}' only`,{message:d.message}):c.warn("WORKER",`Override file '${n}' not found, using parent mode '${s}' only`,{error:String(d)}),this.activeMode=o,o}if(!i)return c.warn("SYSTEM",`Invalid override file: ${n}, using parent mode '${s}' only`),this.activeMode=o,o;let a=this.deepMerge(o,i);return this.activeMode=a,c.debug("SYSTEM",`Loaded mode with inheritance: ${a.name} (${e} = ${s} + ${n})`,void 0,{parent:s,override:n,types:a.observation_types.map(d=>d.id),concepts:a.observation_concepts.map(d=>d.id)}),a}getActiveMode(){if(!this.activeMode)throw new Error("No mode loaded. Call loadMode() first.");return this.activeMode}getObservationTypes(){return this.getActiveMode().observation_types}getObservationConcepts(){return this.getActiveMode().observation_concepts}getTypeIcon(e){return this.getObservationTypes().find(s=>s.id===e)?.emoji||"\u{1F4DD}"}getWorkEmoji(e){return this.getObservationTypes().find(s=>s.id===e)?.work_emoji||"\u{1F4DD}"}validateType(e){return this.getObservationTypes().some(t=>t.id===e)}getTypeLabel(e){return this.getObservationTypes().find(s=>s.id===e)?.label||e}};function ie(){let r=Ue.default.join((0,xe.homedir)(),".claude-mem","settings.json"),e=W.loadFromFile(r),t=A.getInstance().getActiveMode(),s=new Set(t.observation_types.map(o=>o.id)),n=new Set(t.observation_concepts.map(o=>o.id));return{totalObservationCount:parseInt(e.CLAUDE_MEM_CONTEXT_OBSERVATIONS,10),fullObservationCount:parseInt(e.CLAUDE_MEM_CONTEXT_FULL_COUNT,10),sessionCount:parseInt(e.CLAUDE_MEM_CONTEXT_SESSION_COUNT,10),showReadTokens:e.CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS==="true",showWorkTokens:e.CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS==="true",showSavingsAmount:e.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT==="true",showSavingsPercent:e.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT==="true",observationTypes:s,observationConcepts:n,fullObservationField:e.CLAUDE_MEM_CONTEXT_FULL_FIELD,showLastSummary:e.CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY==="true",showLastMessage:e.CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE==="true"}}var _={reset:"\x1B[0m",bright:"\x1B[1m",dim:"\x1B[2m",cyan:"\x1B[36m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",magenta:"\x1B[35m",gray:"\x1B[90m",red:"\x1B[31m"},ke=4,ae=1;function de(r){let e=(r.title?.length||0)+(r.subtitle?.length||0)+(r.narrative?.length||0)+JSON.stringify(r.facts||[]).length;return Math.ceil(e/ke)}function _e(r){let e=r.length,t=r.reduce((i,a)=>i+de(a),0),s=r.reduce((i,a)=>i+(a.discovery_tokens||0),0),n=s-t,o=s>0?Math.round(n/s*100):0;return{totalObservations:e,totalReadTokens:t,totalDiscoveryTokens:s,savings:n,savingsPercent:o}}function Bt(r){return A.getInstance().getWorkEmoji(r)}function w(r,e){let t=de(r),s=r.discovery_tokens||0,n=Bt(r.type),o=s>0?`${n} ${s.toLocaleString()}`:"-";return{readTokens:t,discoveryTokens:s,discoveryDisplay:o,workEmoji:n}}function Y(r){return r.showReadTokens||r.showWorkTokens||r.showSavingsAmount||r.showSavingsPercent}var Fe=L(require("path"),1),V=require("fs");var Wt=["private","claude-mem-context","system_instruction","system-instruction","persisted-output","system-reminder"],Ks=new RegExp(`<(${Wt.join("|")})\\b[^>]*>[\\s\\S]*?</\\1>`,"g"),we=/<system-reminder>[\s\S]*?<\/system-reminder>/g;var qt=["task-notification"],Js=new RegExp(`^\\s*<(${qt.join("|")})\\b[^>]*>(?:(?!<\\1\\b|</\\1\\b)[\\s\\S])*</\\1>\\s*$`),Qs=256*1024;function ue(r,e,t){let s=Array.from(t.observationTypes),n=s.map(()=>"?").join(","),o=Array.from(t.observationConcepts),i=o.map(()=>"?").join(",");return r.db.prepare(`
675
675
  SELECT
676
676
  o.id,
677
677
  o.memory_session_id,
@@ -187,7 +187,7 @@ ${f}`}let s=i.lineStart;for(let u=i.lineStart-1;u>=0;u--){let l=a[u].trim();if(l
187
187
  ${c}`}var $b=new Set([".js",".jsx",".ts",".tsx",".mjs",".cjs",".py",".pyw",".go",".rs",".rb",".java",".cs",".cpp",".cc",".cxx",".c",".h",".hpp",".hh",".swift",".kt",".kts",".php",".vue",".svelte",".ex",".exs",".lua",".scala",".sc",".sh",".bash",".zsh",".hs",".zig",".css",".scss",".toml",".yml",".yaml",".sql",".md",".mdx"]),uD=new Set(["node_modules",".git","dist","build",".next","__pycache__",".venv","venv","env",".env","target","vendor",".cache",".turbo","coverage",".nyc_output",".claude",".smart-file-read"]),lD=512*1024;async function*bb(t,e,r=20,n){if(r<=0)return;let o;try{o=await(0,Zn.readdir)(t,{withFileTypes:!0})}catch(i){_.debug("WORKER",`walkDir: failed to read directory ${t}`,void 0,i instanceof Error?i:void 0);return}for(let i of o){if(i.name.startsWith(".")&&i.name!=="."||uD.has(i.name))continue;let a=(0,Ai.join)(t,i.name);if(i.isDirectory())yield*bb(a,e,r-1,n);else if(i.isFile()){let s=i.name.slice(i.name.lastIndexOf("."));($b.has(s)||n&&n.has(s))&&(yield a)}}}async function dD(t){try{let e=await(0,Zn.stat)(t);if(e.size>lD||e.size===0)return null;let r=await(0,Zn.readFile)(t,"utf-8");return r.slice(0,1e3).includes("\0")?null:r}catch(e){return _.debug("WORKER",`safeReadFile: failed to read ${t}`,void 0,e instanceof Error?e:void 0),null}}async function Sb(t,e,r={}){let n=r.maxResults||20,o=e.toLowerCase(),i=o.split(/[\s_\-./]+/).filter(x=>x.length>0),a=r.projectRoot||t,s=Ni(a),c=new Set;for(let x of Object.values(s.grammars))for(let S of x.extensions)$b.has(S)||c.add(S);let u=[];for await(let x of bb(t,t,20,c.size>0?c:void 0)){if(r.filePattern&&!(0,Ai.relative)(t,x).toLowerCase().includes(r.filePattern.toLowerCase()))continue;let S=await dD(x);S&&u.push({absolutePath:x,relativePath:(0,Ai.relative)(t,x),content:S})}let l=vb(u,a),d=[],p=[],f=0;for(let[x,S]of l){f+=pD(S);let z=nc(x.toLowerCase(),i)>0,Ve=[],We=(qn,qr)=>{for(let ye of qn){let rr=0,mt="",Vn=nc(ye.name.toLowerCase(),i);Vn>0&&(rr+=Vn*3,mt="name match"),ye.signature.toLowerCase().includes(o)&&(rr+=2,mt=mt?`${mt} + signature`:"signature match"),ye.jsdoc&&ye.jsdoc.toLowerCase().includes(o)&&(rr+=1,mt=mt?`${mt} + jsdoc`:"jsdoc match"),rr>0&&(z=!0,Ve.push({filePath:x,symbolName:qr?`${qr}.${ye.name}`:ye.name,kind:ye.kind,signature:ye.signature,jsdoc:ye.jsdoc,lineStart:ye.lineStart,lineEnd:ye.lineEnd,matchReason:mt})),ye.children&&We(ye.children,ye.name)}};We(S.symbols),z&&(d.push(S),p.push(...Ve))}p.sort((x,S)=>{let j=nc(x.symbolName.toLowerCase(),i);return nc(S.symbolName.toLowerCase(),i)-j});let h=p.slice(0,n),g=new Set(h.map(x=>x.filePath)),$=d.filter(x=>g.has(x.filePath)).slice(0,n),k=$.reduce((x,S)=>x+S.foldedTokenEstimate,0);return{foldedFiles:$,matchingSymbols:h,totalFilesScanned:u.length,totalSymbolsFound:f,tokenEstimate:k}}function nc(t,e){let r=0;for(let n of e)if(t===n)r+=10;else if(t.includes(n))r+=5;else{let o=0,i=0;for(let a of n){let s=t.indexOf(a,o);s!==-1&&(i++,o=s+1)}i===n.length&&(r+=1)}return r}function pD(t){let e=t.symbols.length;for(let r of t.symbols)r.children&&(e+=r.children.length);return e}function xb(t,e){let r=[];if(r.push(`\u{1F50D} Smart Search: "${e}"`),r.push(` Scanned ${t.totalFilesScanned} files, found ${t.totalSymbolsFound} symbols`),r.push(` ${t.matchingSymbols.length} matches across ${t.foldedFiles.length} files (~${t.tokenEstimate} tokens for folded view)`),r.push(""),t.matchingSymbols.length===0)return r.push(" No matching symbols found."),r.join(`
188
188
  `);r.push("\u2500\u2500 Matching Symbols \u2500\u2500"),r.push("");for(let n of t.matchingSymbols){if(r.push(` ${n.kind} ${n.symbolName} (${n.filePath}:${n.lineStart+1})`),r.push(` ${n.signature}`),n.jsdoc){let o=n.jsdoc.split(`
189
189
  `).find(i=>i.replace(/^[\s*/]+/,"").trim().length>0);o&&r.push(` \u{1F4AC} ${o.replace(/^[\s*/]+/,"").trim()}`)}r.push("")}r.push("\u2500\u2500 Folded File Views \u2500\u2500"),r.push("");for(let n of t.foldedFiles)r.push(Ln(n)),r.push("");return r.push("\u2500\u2500 Actions \u2500\u2500"),r.push(" To see full implementation: use smart_unfold with file path and symbol name"),r.join(`
190
- `)}var ym=require("node:fs/promises"),Eb=require("node:fs"),Fr=require("node:path"),Ib=require("node:url"),xD={},fD="12.4.2";console.log=(...t)=>{_.error("CONSOLE","Intercepted console output (MCP protocol protection)",void 0,{args:t})};var Pb=!1,Tb=(()=>{if(typeof __dirname<"u")return __dirname;try{return(0,Fr.dirname)((0,Ib.fileURLToPath)(xD.url))}catch{return Pb=!0,process.cwd()}})(),$m=(0,Fr.resolve)(Tb,"worker-service.cjs");function mD(){Pb&&((0,Eb.existsSync)($m)||_.error("SYSTEM","mcp-server: dirname resolution failed (both __dirname and import.meta.url are unavailable). Fell back to process.cwd() and the resolved WORKER_SCRIPT_PATH does not exist. This is the actual problem \u2014 the worker bundle is fine, but mcp-server cannot locate it. Worker auto-start will fail until the dirname-resolution path is fixed.",{workerScriptPath:$m,mcpServerDir:Tb}))}var kb={search:"/api/search",timeline:"/api/timeline"};async function _m(t,e){_.debug("SYSTEM","\u2192 Worker API",void 0,{endpoint:t,params:e});let r=new URLSearchParams;for(let[o,i]of Object.entries(e))i!=null&&r.append(o,String(i));let n=`${t}?${r}`;try{let o=await ec(n);if(!o.ok){let a=await o.text();throw new Error(`Worker API error (${o.status}): ${a}`)}let i=await o.json();return _.debug("SYSTEM","\u2190 Worker API success",void 0,{endpoint:t}),i}catch(o){return _.error("SYSTEM","\u2190 Worker API error",{endpoint:t},o instanceof Error?o:new Error(String(o))),{content:[{type:"text",text:`Error calling Worker API: ${o instanceof Error?o.message:String(o)}`}],isError:!0}}}async function hD(t,e){let r=await ec(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!r.ok){let o=await r.text();throw new Error(`Worker API error (${r.status}): ${o}`)}let n=await r.json();return _.debug("HTTP","Worker API success (POST)",void 0,{endpoint:t}),{content:[{type:"text",text:JSON.stringify(n,null,2)}]}}async function Fn(t,e){_.debug("HTTP","Worker API request (POST)",void 0,{endpoint:t});try{return await hD(t,e)}catch(r){return _.error("HTTP","Worker API error (POST)",{endpoint:t},r instanceof Error?r:new Error(String(r))),{content:[{type:"text",text:`Error calling Worker API: ${r instanceof Error?r.message:String(r)}`}],isError:!0}}}async function gD(){try{return(await ec("/api/health")).ok}catch(t){return _.debug("SYSTEM","Worker health check failed",{},t instanceof Error?t:new Error(String(t))),!1}}async function vD(){if(await gD())return!0;_.warn("SYSTEM","Worker not available, attempting auto-start for MCP client"),mD();try{let t=am(),e=await ib(t,$m);return e||_.error("SYSTEM","Worker auto-start returned false \u2014 MCP tools that require the worker (search, timeline, get_observations) will fail until the worker is running. Check earlier log lines for the specific failure reason (Bun not found, missing worker bundle, port conflict, etc.)."),e}catch(t){return _.error("SYSTEM","Worker auto-start threw \u2014 MCP tools that require the worker (search, timeline, get_observations) will fail until the worker is running.",void 0,t instanceof Error?t:new Error(String(t))),!1}}var zb=[{name:"__IMPORTANT",description:`3-LAYER WORKFLOW (ALWAYS FOLLOW):
190
+ `)}var ym=require("node:fs/promises"),Eb=require("node:fs"),Fr=require("node:path"),Ib=require("node:url"),xD={},fD="12.4.4";console.log=(...t)=>{_.error("CONSOLE","Intercepted console output (MCP protocol protection)",void 0,{args:t})};var Pb=!1,Tb=(()=>{if(typeof __dirname<"u")return __dirname;try{return(0,Fr.dirname)((0,Ib.fileURLToPath)(xD.url))}catch{return Pb=!0,process.cwd()}})(),$m=(0,Fr.resolve)(Tb,"worker-service.cjs");function mD(){Pb&&((0,Eb.existsSync)($m)||_.error("SYSTEM","mcp-server: dirname resolution failed (both __dirname and import.meta.url are unavailable). Fell back to process.cwd() and the resolved WORKER_SCRIPT_PATH does not exist. This is the actual problem \u2014 the worker bundle is fine, but mcp-server cannot locate it. Worker auto-start will fail until the dirname-resolution path is fixed.",{workerScriptPath:$m,mcpServerDir:Tb}))}var kb={search:"/api/search",timeline:"/api/timeline"};async function _m(t,e){_.debug("SYSTEM","\u2192 Worker API",void 0,{endpoint:t,params:e});let r=new URLSearchParams;for(let[o,i]of Object.entries(e))i!=null&&r.append(o,String(i));let n=`${t}?${r}`;try{let o=await ec(n);if(!o.ok){let a=await o.text();throw new Error(`Worker API error (${o.status}): ${a}`)}let i=await o.json();return _.debug("SYSTEM","\u2190 Worker API success",void 0,{endpoint:t}),i}catch(o){return _.error("SYSTEM","\u2190 Worker API error",{endpoint:t},o instanceof Error?o:new Error(String(o))),{content:[{type:"text",text:`Error calling Worker API: ${o instanceof Error?o.message:String(o)}`}],isError:!0}}}async function hD(t,e){let r=await ec(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!r.ok){let o=await r.text();throw new Error(`Worker API error (${r.status}): ${o}`)}let n=await r.json();return _.debug("HTTP","Worker API success (POST)",void 0,{endpoint:t}),{content:[{type:"text",text:JSON.stringify(n,null,2)}]}}async function Fn(t,e){_.debug("HTTP","Worker API request (POST)",void 0,{endpoint:t});try{return await hD(t,e)}catch(r){return _.error("HTTP","Worker API error (POST)",{endpoint:t},r instanceof Error?r:new Error(String(r))),{content:[{type:"text",text:`Error calling Worker API: ${r instanceof Error?r.message:String(r)}`}],isError:!0}}}async function gD(){try{return(await ec("/api/health")).ok}catch(t){return _.debug("SYSTEM","Worker health check failed",{},t instanceof Error?t:new Error(String(t))),!1}}async function vD(){if(await gD())return!0;_.warn("SYSTEM","Worker not available, attempting auto-start for MCP client"),mD();try{let t=am(),e=await ib(t,$m);return e||_.error("SYSTEM","Worker auto-start returned false \u2014 MCP tools that require the worker (search, timeline, get_observations) will fail until the worker is running. Check earlier log lines for the specific failure reason (Bun not found, missing worker bundle, port conflict, etc.)."),e}catch(t){return _.error("SYSTEM","Worker auto-start threw \u2014 MCP tools that require the worker (search, timeline, get_observations) will fail until the worker is running.",void 0,t instanceof Error?t:new Error(String(t))),!1}}var zb=[{name:"__IMPORTANT",description:`3-LAYER WORKFLOW (ALWAYS FOLLOW):
191
191
  1. search(query) \u2192 Get index with IDs (~50-100 tokens/result)
192
192
  2. timeline(anchor=ID) \u2192 Get context around interesting results
193
193
  3. get_observations([IDs]) \u2192 Fetch full details ONLY for filtered IDs