claude-mem 12.4.3 → 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.
- package/dist/npx-cli/index.js +189 -181
- package/dist/opencode-plugin/index.js +1 -1
- package/openclaw/dist/index.js +13 -13
- package/openclaw/src/index.test.ts +1 -8
- package/openclaw/src/index.ts +3 -28
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/hooks/hooks.json +0 -12
- package/plugin/package.json +1 -1
- package/plugin/scripts/mcp-server.cjs +37 -37
- package/plugin/scripts/worker-service.cjs +447 -426
- package/plugin/ui/viewer-bundle.js +35 -23
- package/dist/binaries/worker-service-v10.3.1-win-x64.exe +0 -0
- package/dist/index.d.ts +0 -7
- package/dist/index.js +0 -8
- package/dist/sdk/index.d.ts +0 -109
- package/dist/sdk/index.js +0 -183
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var d="http://127.0.0.1:37777";var g={"Content-Type":"application/json"};function l(
|
|
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};
|
package/openclaw/dist/index.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
var
|
|
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
|
|
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
|
-
`);
|
|
6
|
-
`).filter(S=>S.startsWith("data:")).map(S=>S.slice(5).trim());if(
|
|
7
|
-
`);if(
|
|
8
|
-
`));let
|
|
9
|
-
`));break}}await
|
|
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: ${
|
|
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,
|
|
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,
|
|
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(" "),
|
|
14
|
-
`)}}),e.registerCommand({name:"claude_mem_status",description:"Check Claude-Mem worker health and session status",handler:async()=>{let r=await
|
|
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: ${
|
|
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
|
|
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 () => {
|
package/openclaw/src/index.ts
CHANGED
|
@@ -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
|
|
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
|
-
//
|
|
926
|
-
//
|
|
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
package/plugin/hooks/hooks.json
CHANGED
|
@@ -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
|
}
|