openclaw-memory-alibaba-local 1.0.3 → 1.0.5

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.
Files changed (3) hide show
  1. package/db.ts +1 -1
  2. package/index.ts +32 -47
  3. package/package.json +1 -1
package/db.ts CHANGED
@@ -1400,7 +1400,7 @@ export class MemoryDB {
1400
1400
  try {
1401
1401
  const eid = sqlEscapeLiteral(id);
1402
1402
  await (this.table! as any).update(
1403
- { createdAt: now },
1403
+ { createdAt: String(now) },
1404
1404
  { where: `id = '${eid}' AND agentId = '${a}' AND category = '${wf}'` },
1405
1405
  );
1406
1406
  } catch (err) {
package/index.ts CHANGED
@@ -947,6 +947,11 @@ type DeltaFullContextRow = {
947
947
  /**
948
948
  * agent_end: per-role cursors → delta rows by source → LanceDB for full_context_* (shared batchId, no embed / no dedup);
949
949
  * then Promise.all(user-memory pipeline on user deltas, self-improving on user+assistant deltas).
950
+ *
951
+ * When `cursorOnly` is true the function advances the per-role cursor and persists it
952
+ * but skips all memory extraction / storage. This is used for non-user triggers
953
+ * (heartbeat, cron, memory, …) so the cursor stays in sync with the growing
954
+ * session messages list without accidentally capturing heartbeat content.
950
955
  */
951
956
  async function runAgentEndCapture(
952
957
  cfg: MemoryConfig,
@@ -957,6 +962,7 @@ async function runAgentEndCapture(
957
962
  userId: string | null,
958
963
  messages: unknown[],
959
964
  lancedbDir: string,
965
+ cursorOnly = false,
960
966
  ): Promise<void> {
961
967
  if (messages.length === 0) {
962
968
  return;
@@ -973,6 +979,25 @@ async function runAgentEndCapture(
973
979
  }
974
980
 
975
981
  const running: Record<string, number> = { ...saved };
982
+
983
+ // --- cursor-only fast path: count roles then persist without extraction ---
984
+ if (cursorOnly) {
985
+ for (const msg of messages) {
986
+ if (!msg || typeof msg !== "object") continue;
987
+ const m = msg as Record<string, unknown>;
988
+ const roleRaw = typeof m.role === "string" ? m.role : "unknown";
989
+ const roleKey = normalizeRoleForCursor(roleRaw);
990
+ running[roleKey] = (running[roleKey] ?? 0) + 1;
991
+ }
992
+ map[key] = { version: 2, roleCounts: { ...running }, lastMessagesLength: messages.length };
993
+ saveAgentEndCursorMap(lancedbDir, map);
994
+ console.log(
995
+ `[openclaw-memory-alibaba-local] agent_end cursor-only advance (non-user trigger) messages=${messages.length}`,
996
+ );
997
+ return;
998
+ }
999
+
1000
+ // --- full capture path (trigger === "user") ---
976
1001
  const fullRows: DeltaFullContextRow[] = [];
977
1002
  const userRawTexts: string[] = [];
978
1003
  const uaLines: string[] = [];
@@ -1852,58 +1877,14 @@ const memoryPlugin = {
1852
1877
  );
1853
1878
  }
1854
1879
 
1855
- // --- Hooks: session_start (inject top user memories), before_prompt_build (recall), agent_end (auto-capture) ---
1856
-
1857
- // Session-start memory promise cache: stores the in-flight DB query Promise so
1858
- // before_prompt_build can await it even if session_start hasn't finished yet.
1859
- type SessionStartMemory = { category: MemoryCategory; text: string; importance: number };
1860
- const _sessionStartPromiseCache = new Map<string, Promise<SessionStartMemory[]>>();
1861
-
1862
- api.on("session_start", async (_event, ctx) => {
1863
- if (!db) return;
1864
- const sessionId = ctx.sessionId;
1865
- // Store the Promise immediately — before_prompt_build will await it.
1866
- const promise = (async (): Promise<SessionStartMemory[]> => {
1867
- try {
1868
- const agentId = resolveAgentIdForMemory(ctx);
1869
- const topMemories = await db.listTopByImportance(agentId, [USER_MEMORY_FACT], 5);
1870
- const entries = topMemories.map((m) => ({ category: m.category as MemoryCategory, text: m.text, importance: m.importance }));
1871
- console.log(
1872
- `[openclaw-memory-alibaba-local] session_start loaded ${entries.length} user_memory_fact for session=${sessionId} agent=${agentId}`,
1873
- );
1874
- return entries;
1875
- } catch (err) {
1876
- console.warn(`[openclaw-memory-alibaba-local] session_start memory load failed: ${String(err)}`);
1877
- return [];
1878
- }
1879
- })();
1880
- _sessionStartPromiseCache.set(sessionId, promise);
1881
- });
1880
+ // --- Hooks: before_prompt_build (recall), agent_end (auto-capture) ---
1882
1881
 
1883
1882
  if (cfg.autoRecall) {
1884
1883
  api.on("before_prompt_build", async (event, ctx) => {
1884
+ if ((ctx as { trigger?: string }).trigger !== "user") return;
1885
1885
  if (!db || !backend) return;
1886
1886
  if (!event.prompt || event.prompt.length < 5) return;
1887
1887
 
1888
- // Await session-start promise (inject once on the first prompt of a new session)
1889
- // Timeout after 10s to avoid blocking prompt build if DB is slow.
1890
- const sessionId = (ctx as { sessionId?: string }).sessionId ?? "";
1891
- const sessionStartPromise = sessionId ? _sessionStartPromiseCache.get(sessionId) : undefined;
1892
- if (sessionStartPromise) {
1893
- _sessionStartPromiseCache.delete(sessionId);
1894
- const timeout = new Promise<SessionStartMemory[]>((resolve) => setTimeout(() => resolve([]), 10_000));
1895
- const entries = await Promise.race([sessionStartPromise, timeout]);
1896
- if (entries.length > 0) {
1897
- // Session-start: inject cached memories directly, skip recall
1898
- console.log(
1899
- `[openclaw-memory-alibaba-local] session_start inject ${entries.length} cached memories (importance desc), skip recall`,
1900
- );
1901
- return {
1902
- appendSystemContext: formatRelevantMemoriesContext(entries),
1903
- };
1904
- }
1905
- }
1906
-
1907
1888
  try {
1908
1889
  const extracted = extractUserQueryForRecall(event.prompt);
1909
1890
  if (extracted.query.length < 5) {
@@ -1993,6 +1974,9 @@ const memoryPlugin = {
1993
1974
  return;
1994
1975
  }
1995
1976
 
1977
+ const trigger = (ctx as { trigger?: string }).trigger;
1978
+ const isUserTrigger = trigger === "user";
1979
+
1996
1980
  try {
1997
1981
  const tCap0 = Date.now();
1998
1982
  const storageSessionKey = resolveStorageSessionKey(ctx);
@@ -2014,9 +1998,10 @@ const memoryPlugin = {
2014
1998
  userId,
2015
1999
  event.messages,
2016
2000
  resolvedDbPath,
2001
+ !isUserTrigger,
2017
2002
  );
2018
2003
  console.log(
2019
- `[openclaw-memory-alibaba-local] agent_end capture done totalHookMs=${Date.now() - tCap0} messages=${event.messages.length}`,
2004
+ `[openclaw-memory-alibaba-local] agent_end ${isUserTrigger ? "capture" : "cursor-only"} done totalHookMs=${Date.now() - tCap0} messages=${event.messages.length} trigger=${trigger ?? "unknown"}`,
2020
2005
  );
2021
2006
  } catch (err) {
2022
2007
  console.warn(`[openclaw-memory-alibaba-local] agent_end capture failed: ${String(err)}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-memory-alibaba-local",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "OpenClaw memory plugin: local LanceDB + DashScope-compatible embeddings",
5
5
  "type": "module",
6
6
  "engines": {