agent-memory-graph 0.3.0 → 0.4.1
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/plugin/entry.js +109 -0
- package/openclaw.plugin.json +6 -1
- package/package.json +1 -1
package/dist/plugin/entry.js
CHANGED
|
@@ -5774,6 +5774,63 @@ var entry_default = definePluginEntry({
|
|
|
5774
5774
|
name: "Memory Graph",
|
|
5775
5775
|
description: "Auto-builds a knowledge graph from conversations. Extracts entities/relationships and exposes graph query tools.",
|
|
5776
5776
|
register(api) {
|
|
5777
|
+
api.on(
|
|
5778
|
+
"before_prompt_build",
|
|
5779
|
+
async (event, ctx) => {
|
|
5780
|
+
const config = ctx?.pluginConfig;
|
|
5781
|
+
if (config?.promptInjection === false) return;
|
|
5782
|
+
try {
|
|
5783
|
+
const graph = await getGraph(config);
|
|
5784
|
+
const stats = graph.stats();
|
|
5785
|
+
if (stats.entities === 0) return;
|
|
5786
|
+
const prompt = event.prompt || "";
|
|
5787
|
+
let contextLines = [];
|
|
5788
|
+
const PRIORITY_TYPES = ["Project", "Person", "Platform", "Organization", "Company", "Event", "System"];
|
|
5789
|
+
const SKIP_TYPES = ["Tool", "Concept", "File", "Award"];
|
|
5790
|
+
if (prompt && prompt.length > 20) {
|
|
5791
|
+
const searchQuery = prompt.slice(0, 100).replace(/[\n\r]+/g, " ").trim();
|
|
5792
|
+
const results = graph.search(searchQuery, 15);
|
|
5793
|
+
if (results.length > 0) {
|
|
5794
|
+
const filtered = results.filter((r) => !SKIP_TYPES.includes(r.entity.type)).sort((a, b) => {
|
|
5795
|
+
const aPriority = PRIORITY_TYPES.indexOf(a.entity.type);
|
|
5796
|
+
const bPriority = PRIORITY_TYPES.indexOf(b.entity.type);
|
|
5797
|
+
const aScore = (aPriority >= 0 ? 100 - aPriority : 50) + (a.relations?.length || 0);
|
|
5798
|
+
const bScore = (bPriority >= 0 ? 100 - bPriority : 50) + (b.relations?.length || 0);
|
|
5799
|
+
return bScore - aScore;
|
|
5800
|
+
}).slice(0, 5);
|
|
5801
|
+
if (filtered.length > 0) {
|
|
5802
|
+
contextLines = filtered.map((r) => {
|
|
5803
|
+
const rels = r.relations?.filter((rel) => !SKIP_TYPES.includes(rel.targetType || "")).slice(0, 3).map((rel) => `${rel.direction === "outgoing" ? "\u2192" : "\u2190"} ${rel.relation} ${rel.target}`).join(", ") || "";
|
|
5804
|
+
return `${r.entity.name} (${r.entity.type})${rels ? ": " + rels : ""}`;
|
|
5805
|
+
});
|
|
5806
|
+
}
|
|
5807
|
+
}
|
|
5808
|
+
}
|
|
5809
|
+
if (contextLines.length === 0) {
|
|
5810
|
+
const allEntities = graph.listEntities({ limit: 30, sortBy: "updated_at" });
|
|
5811
|
+
const prioritized = allEntities.filter((e) => !SKIP_TYPES.includes(e.type)).sort((a, b) => {
|
|
5812
|
+
const aPriority = PRIORITY_TYPES.indexOf(a.type);
|
|
5813
|
+
const bPriority = PRIORITY_TYPES.indexOf(b.type);
|
|
5814
|
+
return (bPriority >= 0 ? bPriority : -1) - (aPriority >= 0 ? aPriority : -1);
|
|
5815
|
+
}).slice(0, 5);
|
|
5816
|
+
if (prioritized.length > 0) {
|
|
5817
|
+
contextLines = prioritized.map((e) => `${e.name} (${e.type})`);
|
|
5818
|
+
}
|
|
5819
|
+
}
|
|
5820
|
+
if (contextLines.length === 0) return;
|
|
5821
|
+
const injection = `## Knowledge Graph Context (auto-injected by memory-graph plugin)
|
|
5822
|
+
Relevant entities from your knowledge graph:
|
|
5823
|
+
${contextLines.join("\n")}
|
|
5824
|
+
|
|
5825
|
+
Use memory_graph_query or memory_graph_search tools for more details when needed.`;
|
|
5826
|
+
return { appendContext: injection };
|
|
5827
|
+
} catch (err) {
|
|
5828
|
+
console.warn("[memory-graph] Prompt injection failed:", err.message);
|
|
5829
|
+
return;
|
|
5830
|
+
}
|
|
5831
|
+
},
|
|
5832
|
+
{ priority: 10 }
|
|
5833
|
+
);
|
|
5777
5834
|
api.on(
|
|
5778
5835
|
"message_received",
|
|
5779
5836
|
async (event) => {
|
|
@@ -5827,6 +5884,58 @@ var entry_default = definePluginEntry({
|
|
|
5827
5884
|
},
|
|
5828
5885
|
{ priority: 10 }
|
|
5829
5886
|
);
|
|
5887
|
+
api.on(
|
|
5888
|
+
"agent_end",
|
|
5889
|
+
async (event, ctx) => {
|
|
5890
|
+
const config = ctx?.pluginConfig;
|
|
5891
|
+
const sessionKey = ctx?.sessionKey || "";
|
|
5892
|
+
const messages = event.messages || [];
|
|
5893
|
+
console.log(`[memory-graph] agent_end fired: sessionKey=${sessionKey}, messages=${messages.length}, success=${event.success}`);
|
|
5894
|
+
if (config?.sessionSummary === false) return;
|
|
5895
|
+
if (!sessionKey) return;
|
|
5896
|
+
const textParts = [];
|
|
5897
|
+
for (const msg of messages) {
|
|
5898
|
+
const role = msg.role || "unknown";
|
|
5899
|
+
if (role !== "user" && role !== "assistant") continue;
|
|
5900
|
+
const text = typeof msg.content === "string" ? msg.content : Array.isArray(msg.content) ? msg.content.filter((p) => p.type === "text").map((p) => p.text).join(" ") : "";
|
|
5901
|
+
if (!text || text.length < MIN_MEANINGFUL_LENGTH) continue;
|
|
5902
|
+
textParts.push(`[${role}]: ${text.slice(0, 500)}`);
|
|
5903
|
+
}
|
|
5904
|
+
if (textParts.length < 2) return;
|
|
5905
|
+
const transcript = textParts.join("\n").slice(0, 3e3);
|
|
5906
|
+
try {
|
|
5907
|
+
const graph = await getGraph(config);
|
|
5908
|
+
const { OpenAI } = await import("openai");
|
|
5909
|
+
const client = new OpenAI({
|
|
5910
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
5911
|
+
baseURL: process.env.OPENAI_BASE_URL
|
|
5912
|
+
});
|
|
5913
|
+
const summaryResponse = await client.chat.completions.create({
|
|
5914
|
+
model: config?.extractionModel || process.env.MEMORY_GRAPH_MODEL || "kr/claude-haiku-4.5",
|
|
5915
|
+
messages: [
|
|
5916
|
+
{
|
|
5917
|
+
role: "system",
|
|
5918
|
+
content: `You are a session summarizer for a knowledge graph. Given a conversation transcript, extract a 2-4 sentence summary of KEY ACTIONS taken (what was built, fixed, published, decided, configured). Focus on concrete outcomes, versions, platforms, and decisions. Skip casual chat and test/debug noise. If nothing meaningful happened, respond with "NO_SUMMARY". Output plain text only.`
|
|
5919
|
+
},
|
|
5920
|
+
{ role: "user", content: transcript }
|
|
5921
|
+
],
|
|
5922
|
+
max_tokens: 300,
|
|
5923
|
+
temperature: 0.3
|
|
5924
|
+
});
|
|
5925
|
+
const summary = summaryResponse.choices?.[0]?.message?.content?.trim();
|
|
5926
|
+
if (summary && summary !== "NO_SUMMARY" && summary.length >= 20) {
|
|
5927
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
5928
|
+
await graph.ingest(summary, { source: `session-summary-${today}`, sessionId: sessionKey });
|
|
5929
|
+
console.log(`[memory-graph] Session summary ingested (${textParts.length} msgs \u2192 ${summary.length} chars)`);
|
|
5930
|
+
} else {
|
|
5931
|
+
console.log(`[memory-graph] No meaningful summary for session (${textParts.length} msgs)`);
|
|
5932
|
+
}
|
|
5933
|
+
} catch (err) {
|
|
5934
|
+
console.warn("[memory-graph] Session summary failed:", err.message);
|
|
5935
|
+
}
|
|
5936
|
+
},
|
|
5937
|
+
{ priority: 10 }
|
|
5938
|
+
);
|
|
5830
5939
|
api.on(
|
|
5831
5940
|
"session_end",
|
|
5832
5941
|
async (event, ctx) => {
|
package/openclaw.plugin.json
CHANGED
|
@@ -71,6 +71,11 @@
|
|
|
71
71
|
}
|
|
72
72
|
},
|
|
73
73
|
"default": []
|
|
74
|
+
},
|
|
75
|
+
"promptInjection": {
|
|
76
|
+
"type": "boolean",
|
|
77
|
+
"description": "Automatically inject relevant graph context into every agent prompt",
|
|
78
|
+
"default": true
|
|
74
79
|
}
|
|
75
80
|
},
|
|
76
81
|
"additionalProperties": false
|
|
@@ -79,7 +84,7 @@
|
|
|
79
84
|
"openclawVersion": ">=2026.3.24",
|
|
80
85
|
"entry": "dist/plugin/entry.js"
|
|
81
86
|
},
|
|
82
|
-
"version": "0.
|
|
87
|
+
"version": "0.4.1",
|
|
83
88
|
"author": "KLSGG",
|
|
84
89
|
"repository": "https://github.com/KLSGG/agent-memory-graph",
|
|
85
90
|
"license": "MIT",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-memory-graph",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Domain-agnostic knowledge graph memory for AI agents. Zero-config, local-first, SQLite-powered. Works as OpenClaw skill (CLI) or plugin (auto-hook).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/src/index.js",
|