@nowledge/openclaw-nowledge-mem 0.8.30 → 0.8.31

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/CHANGELOG.md CHANGED
@@ -4,6 +4,12 @@ All notable changes to the Nowledge Mem OpenClaw plugin will be documented in th
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## [0.8.31] - 2026-07-12
8
+
9
+ ### Improved
10
+
11
+ - New OpenClaw Threads use the first meaningful user message as their title instead of exposing an internal `agent:...` session key. When no user turn is available, the plugin falls back to a readable channel label such as `OpenClaw · WeChat`.
12
+
7
13
  ## [0.8.30] - 2026-07-12
8
14
 
9
15
  ### Fixed
@@ -2,7 +2,7 @@
2
2
  "id": "openclaw-nowledge-mem",
3
3
  "name": "Nowledge Mem",
4
4
  "description": "Cross-tool knowledge graph memory for OpenClaw with startup context, graph search, and session distillation.",
5
- "version": "0.8.30",
5
+ "version": "0.8.31",
6
6
  "kind": ["memory", "context-engine"],
7
7
  "contracts": {
8
8
  "tools": [
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nowledge/openclaw-nowledge-mem",
3
3
  "displayName": "Nowledge Mem for OpenClaw",
4
- "version": "0.8.30",
4
+ "version": "0.8.31",
5
5
  "type": "module",
6
6
  "description": "Nowledge Mem memory plugin for OpenClaw, local-first personal knowledge base",
7
7
  "author": {
@@ -12,6 +12,7 @@ export const DEFAULT_MAX_MESSAGE_CHARS = 800;
12
12
  export const MAX_DISTILL_MESSAGE_CHARS = 2000;
13
13
  export const MAX_CONVERSATION_CHARS = 30_000;
14
14
  export const MIN_MESSAGES_FOR_DISTILL = 4;
15
+ export const MAX_THREAD_TITLE_CHARS = 80;
15
16
  const SESSION_RESET_PROMPT_PREFIX =
16
17
  "A new session was started via /new or /reset.";
17
18
  const OPENCLAW_DIRECTIVE_TAG_RE =
@@ -309,9 +310,47 @@ export function normalizeRoleMessage(
309
310
  };
310
311
  }
311
312
 
312
- export function buildThreadTitle(ctx) {
313
- const session = ctx?.sessionKey || ctx?.sessionId || "session";
314
- return `OpenClaw ${session}`;
313
+ function humanizeChannel(value) {
314
+ const normalized = String(value || "")
315
+ .trim()
316
+ .toLowerCase();
317
+ if (!normalized) return "";
318
+ const known = {
319
+ dashboard: "Dashboard",
320
+ "openclaw-weixin": "WeChat",
321
+ weixin: "WeChat",
322
+ wechat: "WeChat",
323
+ telegram: "Telegram",
324
+ discord: "Discord",
325
+ whatsapp: "WhatsApp",
326
+ };
327
+ if (known[normalized]) return known[normalized];
328
+ return normalized
329
+ .replace(/^openclaw[-_]/, "")
330
+ .split(/[-_]+/)
331
+ .filter(Boolean)
332
+ .map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)
333
+ .join(" ");
334
+ }
335
+
336
+ export function buildThreadTitle(ctx, normalizedMessages = []) {
337
+ const firstUserMessage = normalizedMessages.find(
338
+ (message) => message?.role === "user",
339
+ );
340
+ const userText = String(
341
+ firstUserMessage?.fullContent || firstUserMessage?.content || "",
342
+ )
343
+ .replace(/\s+/gu, " ")
344
+ .trim();
345
+ if (userText) return truncate(userText, MAX_THREAD_TITLE_CHARS);
346
+
347
+ const explicitChannel = ctx?.channel || ctx?.channelId;
348
+ const sessionKey = String(ctx?.sessionKey || "");
349
+ const sessionParts = sessionKey.split(":").filter(Boolean);
350
+ const sessionChannel =
351
+ sessionParts[0]?.toLowerCase() === "agent" ? sessionParts[2] : "";
352
+ const channel = humanizeChannel(explicitChannel || sessionChannel);
353
+ return channel ? `OpenClaw · ${channel}` : "OpenClaw conversation";
315
354
  }
316
355
 
317
356
  function sanitizeIdPart(input, max = 48) {
@@ -423,11 +462,11 @@ export async function appendOrCreateThread({
423
462
  const threadId = buildStableThreadId(event, ctx);
424
463
  const sessionKey = String(ctx?.sessionKey || ctx?.sessionId || "session");
425
464
  const sessionId = String(ctx?.sessionId || "").trim();
426
- const title = buildThreadTitle(ctx);
427
465
  const normalized = rawMessages
428
466
  .map((message) => normalizeRoleMessage(message, maxMessageChars))
429
467
  .filter(Boolean);
430
468
  if (normalized.length === 0) return;
469
+ const title = buildThreadTitle(ctx, normalized);
431
470
 
432
471
  const buildMessages = (resolvedMessageMode) =>
433
472
  normalized.map((message, index) => ({
@@ -669,7 +708,7 @@ export async function triageAndDistill({
669
708
 
670
709
  const distillResult = await client.distillThread({
671
710
  threadId: captureResult.threadId,
672
- title: buildThreadTitle(ctx),
711
+ title: buildThreadTitle(ctx, captureResult.normalized),
673
712
  content: conversationText,
674
713
  });
675
714