@xdarkicex/openclaw-memory-libravdb 1.9.10-beta.17 → 1.9.10-beta.19

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.
@@ -20,6 +20,11 @@ type OpenClawCompatibleAssembleResult = {
20
20
  promptAuthority: OpenClawCompatiblePromptAuthority;
21
21
  debug?: AssembleContextInternalResponse["debug"];
22
22
  };
23
+ export interface Speaker {
24
+ name: string;
25
+ displayName: string;
26
+ }
27
+ export declare function extractSpeakers(messages: OpenClawCompatibleMessage[]): Speaker[];
23
28
  type OpenClawCompatibleCompactResult = {
24
29
  ok: boolean;
25
30
  compacted: boolean;
@@ -24,6 +24,29 @@ const SELECTED_CONTEXT_HEADER = "Conversation context (untrusted, chronological,
24
24
  const RETRIEVAL_QUERY_MAX_CHARS = 1000;
25
25
  const SELECTED_CONTEXT_TURN_RE = /^#\d+\s+[A-Za-z]{3}\s+\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}(?::\d{2})?\s+\S+\s+([^:\n]{1,80}):\s*(.*)$/;
26
26
  const ASSISTANT_SPEAKER_RE = /\b(?:assistant|openclaw|z3robot|bot)\b/i;
27
+ // Multi-speaker envelope regex: matches [HH:MM] Speaker: text
28
+ const MULTI_SPEAKER_LINE_RE = /^\[\d{2}:\d{2}\]\s+(\S[^:]{0,80}):\s/;
29
+ export function extractSpeakers(messages) {
30
+ const seen = new Set();
31
+ const speakers = [];
32
+ for (const msg of messages) {
33
+ if (msg.role !== "user")
34
+ continue;
35
+ const text = normalizeKernelContent(msg.content);
36
+ for (const line of text.split("\n")) {
37
+ const match = line.match(MULTI_SPEAKER_LINE_RE);
38
+ if (match) {
39
+ const displayName = match[1].trim();
40
+ const name = displayName.toLowerCase();
41
+ if (name && !ASSISTANT_SPEAKER_RE.test(name) && !seen.has(name)) {
42
+ seen.add(name);
43
+ speakers.push({ name, displayName });
44
+ }
45
+ }
46
+ }
47
+ }
48
+ return speakers;
49
+ }
27
50
  const OPENCLAW_METADATA_HEADERS = [
28
51
  "Conversation info (untrusted metadata):",
29
52
  "Sender (untrusted metadata):",
package/dist/index.js CHANGED
@@ -16789,82 +16789,6 @@ var require_undici = __commonJS({
16789
16789
  }
16790
16790
  });
16791
16791
 
16792
- // src/index.ts
16793
- import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
16794
-
16795
- // src/cli.ts
16796
- import { createInterface } from "node:readline/promises";
16797
- import { stdin, stdout } from "node:process";
16798
-
16799
- // src/cli-descriptors.ts
16800
- var PLUGIN_ID = "libravdb-memory";
16801
- var MEMORY_CLI_DESCRIPTOR = {
16802
- name: "memory",
16803
- description: "Manage LibraVDB memory",
16804
- hasSubcommands: true
16805
- };
16806
- function isMemorySlotSelected(api) {
16807
- const slots = api.config?.plugins?.slots;
16808
- return slots?.memory === PLUGIN_ID;
16809
- }
16810
- function registerMemoryCliMetadata(api) {
16811
- if (!isMemorySlotSelected(api)) {
16812
- return;
16813
- }
16814
- api.registerCli?.(() => {
16815
- }, {
16816
- descriptors: [MEMORY_CLI_DESCRIPTOR]
16817
- });
16818
- }
16819
-
16820
- // src/memory-scopes.ts
16821
- var SESSION_KEY_NAMESPACE_PREFIX = "session-key:";
16822
- var AGENT_ID_NAMESPACE_PREFIX = "agent-id:";
16823
- var USER_COLLECTION_PREFIX = "user:";
16824
- var RESERVED_NAMESPACE_PREFIXES = [SESSION_KEY_NAMESPACE_PREFIX, AGENT_ID_NAMESPACE_PREFIX, USER_COLLECTION_PREFIX];
16825
- var COLLECTION_NAME_RE = /^[a-zA-Z][a-zA-Z0-9_.:@#-]{0,127}$/;
16826
- function validateNamespace(name) {
16827
- if (!COLLECTION_NAME_RE.test(name)) {
16828
- throw new Error(
16829
- `Invalid collection namespace: "${name}". Must match ${COLLECTION_NAME_RE.source}`
16830
- );
16831
- }
16832
- return name;
16833
- }
16834
- function resolveDurableNamespace(params) {
16835
- const explicitUserId = firstNonEmpty(params.userId);
16836
- if (explicitUserId) {
16837
- for (const prefix of RESERVED_NAMESPACE_PREFIXES) {
16838
- if (explicitUserId.startsWith(prefix)) {
16839
- throw new Error(
16840
- `Invalid userId "${explicitUserId}": must not start with reserved prefix "${prefix}"`
16841
- );
16842
- }
16843
- }
16844
- return validateNamespace(explicitUserId);
16845
- }
16846
- const sessionKey = firstNonEmpty(params.sessionKey);
16847
- if (sessionKey) return validateNamespace(`${SESSION_KEY_NAMESPACE_PREFIX}${sessionKey}`);
16848
- const agentId = firstNonEmpty(params.agentId);
16849
- if (agentId) return validateNamespace(`${AGENT_ID_NAMESPACE_PREFIX}${agentId}`);
16850
- const fallback2 = firstNonEmpty(params.fallback);
16851
- if (fallback2) return validateNamespace(fallback2);
16852
- return "default";
16853
- }
16854
- function resolveUserCollection(userId) {
16855
- const namespace = firstNonEmpty(userId);
16856
- if (!namespace) {
16857
- throw new Error("Invalid user collection namespace: userId must be non-empty");
16858
- }
16859
- validateNamespace(namespace);
16860
- return validateNamespace(`${USER_COLLECTION_PREFIX}${namespace}`);
16861
- }
16862
- function firstNonEmpty(value) {
16863
- if (typeof value !== "string") return void 0;
16864
- const trimmed = value.trim();
16865
- return trimmed.length > 0 ? trimmed : void 0;
16866
- }
16867
-
16868
16792
  // src/identity.ts
16869
16793
  import { userInfo, hostname } from "node:os";
16870
16794
  import { createHash } from "node:crypto";
@@ -16987,6 +16911,82 @@ function resolveTenantKey(cfg) {
16987
16911
  }).userId;
16988
16912
  }
16989
16913
 
16914
+ // src/index.ts
16915
+ import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
16916
+
16917
+ // src/cli.ts
16918
+ import { createInterface } from "node:readline/promises";
16919
+ import { stdin, stdout } from "node:process";
16920
+
16921
+ // src/cli-descriptors.ts
16922
+ var PLUGIN_ID = "libravdb-memory";
16923
+ var MEMORY_CLI_DESCRIPTOR = {
16924
+ name: "memory",
16925
+ description: "Manage LibraVDB memory",
16926
+ hasSubcommands: true
16927
+ };
16928
+ function isMemorySlotSelected(api) {
16929
+ const slots = api.config?.plugins?.slots;
16930
+ return slots?.memory === PLUGIN_ID;
16931
+ }
16932
+ function registerMemoryCliMetadata(api) {
16933
+ if (!isMemorySlotSelected(api)) {
16934
+ return;
16935
+ }
16936
+ api.registerCli?.(() => {
16937
+ }, {
16938
+ descriptors: [MEMORY_CLI_DESCRIPTOR]
16939
+ });
16940
+ }
16941
+
16942
+ // src/memory-scopes.ts
16943
+ var SESSION_KEY_NAMESPACE_PREFIX = "session-key:";
16944
+ var AGENT_ID_NAMESPACE_PREFIX = "agent-id:";
16945
+ var USER_COLLECTION_PREFIX = "user:";
16946
+ var RESERVED_NAMESPACE_PREFIXES = [SESSION_KEY_NAMESPACE_PREFIX, AGENT_ID_NAMESPACE_PREFIX, USER_COLLECTION_PREFIX];
16947
+ var COLLECTION_NAME_RE = /^[a-zA-Z][a-zA-Z0-9_.:@#-]{0,127}$/;
16948
+ function validateNamespace(name) {
16949
+ if (!COLLECTION_NAME_RE.test(name)) {
16950
+ throw new Error(
16951
+ `Invalid collection namespace: "${name}". Must match ${COLLECTION_NAME_RE.source}`
16952
+ );
16953
+ }
16954
+ return name;
16955
+ }
16956
+ function resolveDurableNamespace(params) {
16957
+ const explicitUserId = firstNonEmpty(params.userId);
16958
+ if (explicitUserId) {
16959
+ for (const prefix of RESERVED_NAMESPACE_PREFIXES) {
16960
+ if (explicitUserId.startsWith(prefix)) {
16961
+ throw new Error(
16962
+ `Invalid userId "${explicitUserId}": must not start with reserved prefix "${prefix}"`
16963
+ );
16964
+ }
16965
+ }
16966
+ return validateNamespace(explicitUserId);
16967
+ }
16968
+ const sessionKey = firstNonEmpty(params.sessionKey);
16969
+ if (sessionKey) return validateNamespace(`${SESSION_KEY_NAMESPACE_PREFIX}${sessionKey}`);
16970
+ const agentId = firstNonEmpty(params.agentId);
16971
+ if (agentId) return validateNamespace(`${AGENT_ID_NAMESPACE_PREFIX}${agentId}`);
16972
+ const fallback2 = firstNonEmpty(params.fallback);
16973
+ if (fallback2) return validateNamespace(fallback2);
16974
+ return "default";
16975
+ }
16976
+ function resolveUserCollection(userId) {
16977
+ const namespace = firstNonEmpty(userId);
16978
+ if (!namespace) {
16979
+ throw new Error("Invalid user collection namespace: userId must be non-empty");
16980
+ }
16981
+ validateNamespace(namespace);
16982
+ return validateNamespace(`${USER_COLLECTION_PREFIX}${namespace}`);
16983
+ }
16984
+ function firstNonEmpty(value) {
16985
+ if (typeof value !== "string") return void 0;
16986
+ const trimmed = value.trim();
16987
+ return trimmed.length > 0 ? trimmed : void 0;
16988
+ }
16989
+
16990
16990
  // src/format-error.ts
16991
16991
  function formatError(error2) {
16992
16992
  if (error2 instanceof Error && error2.message.trim()) {
@@ -18646,6 +18646,27 @@ var SELECTED_CONTEXT_HEADER = "Conversation context (untrusted, chronological, s
18646
18646
  var RETRIEVAL_QUERY_MAX_CHARS = 1e3;
18647
18647
  var SELECTED_CONTEXT_TURN_RE = /^#\d+\s+[A-Za-z]{3}\s+\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}(?::\d{2})?\s+\S+\s+([^:\n]{1,80}):\s*(.*)$/;
18648
18648
  var ASSISTANT_SPEAKER_RE = /\b(?:assistant|openclaw|z3robot|bot)\b/i;
18649
+ var MULTI_SPEAKER_LINE_RE = /^\[\d{2}:\d{2}\]\s+(\S[^:]{0,80}):\s/;
18650
+ function extractSpeakers(messages) {
18651
+ const seen = /* @__PURE__ */ new Set();
18652
+ const speakers = [];
18653
+ for (const msg of messages) {
18654
+ if (msg.role !== "user") continue;
18655
+ const text = normalizeKernelContent(msg.content);
18656
+ for (const line of text.split("\n")) {
18657
+ const match = line.match(MULTI_SPEAKER_LINE_RE);
18658
+ if (match) {
18659
+ const displayName = match[1].trim();
18660
+ const name = displayName.toLowerCase();
18661
+ if (name && !ASSISTANT_SPEAKER_RE.test(name) && !seen.has(name)) {
18662
+ seen.add(name);
18663
+ speakers.push({ name, displayName });
18664
+ }
18665
+ }
18666
+ }
18667
+ }
18668
+ return speakers;
18669
+ }
18649
18670
  var OPENCLAW_METADATA_HEADERS = [
18650
18671
  "Conversation info (untrusted metadata):",
18651
18672
  "Sender (untrusted metadata):",
@@ -31337,6 +31358,56 @@ function createGetUserCardTool(getClient, logger = console) {
31337
31358
  }
31338
31359
  };
31339
31360
  }
31361
+ function createListUserCardsTool(getClient, logger = console) {
31362
+ return {
31363
+ name: "list_user_cards",
31364
+ label: "List User Cards",
31365
+ description: "List every person you have a user card for. Returns user IDs and card previews. Use this when asked 'who do you know?' or when you need to see the full roster. For detailed information about a specific person, use get_user_card.",
31366
+ parameters: { type: "object", additionalProperties: false, properties: {} },
31367
+ execute: async (_toolCallId, _rawParams) => {
31368
+ try {
31369
+ const client = await getClient();
31370
+ const resp = await client.listByMeta({
31371
+ collection: "",
31372
+ key: "type",
31373
+ value: "user_card"
31374
+ });
31375
+ const users = [];
31376
+ for (const result of resp.results) {
31377
+ let userId = "";
31378
+ let preview = "";
31379
+ let updatedAt;
31380
+ let version;
31381
+ if (result.metadataJson && result.metadataJson.length > 0) {
31382
+ try {
31383
+ const decoder = new TextDecoder();
31384
+ const meta = JSON.parse(decoder.decode(result.metadataJson));
31385
+ userId = typeof meta._user_id === "string" ? meta._user_id : "";
31386
+ const cardJson = typeof meta.card_json === "string" ? meta.card_json : null;
31387
+ if (cardJson) {
31388
+ try {
31389
+ preview = JSON.parse(cardJson).card ?? cardJson;
31390
+ } catch {
31391
+ preview = cardJson;
31392
+ }
31393
+ preview = preview.slice(0, 200);
31394
+ }
31395
+ updatedAt = typeof meta.updated_at === "number" ? meta.updated_at : void 0;
31396
+ version = typeof meta.version === "number" ? meta.version : void 0;
31397
+ } catch {
31398
+ }
31399
+ }
31400
+ if (!userId) continue;
31401
+ users.push({ user_id: userId, preview, updated_at: updatedAt, version });
31402
+ }
31403
+ return jsonResult({ users, total: users.length });
31404
+ } catch (error2) {
31405
+ logger.warn?.(`list_user_cards failed: ${formatError(error2)}`);
31406
+ return jsonResult({ users: [], total: 0, error: formatError(error2) });
31407
+ }
31408
+ }
31409
+ };
31410
+ }
31340
31411
 
31341
31412
  // src/memory-tools.ts
31342
31413
  var MEMORY_SEARCH_SCHEMA = {
@@ -34780,6 +34851,10 @@ var LibravDBClient = class {
34780
34851
  this.guardOpen();
34781
34852
  return this.client.getUserCard(req);
34782
34853
  }
34854
+ async listByMeta(req) {
34855
+ this.guardOpen();
34856
+ return this.client.listByMeta(req);
34857
+ }
34783
34858
  close() {
34784
34859
  this.closed = true;
34785
34860
  }
@@ -35107,6 +35182,10 @@ function register(api) {
35107
35182
  const getClient = runtimeOrNull.getClient;
35108
35183
  return createGetUserCardTool(getClient, logger);
35109
35184
  }, { names: ["get_user_card"] });
35185
+ api.registerTool?.((ctx) => {
35186
+ const getClient = runtimeOrNull.getClient;
35187
+ return createListUserCardsTool(getClient, logger);
35188
+ }, { names: ["list_user_cards"] });
35110
35189
  }
35111
35190
  if (isLightweight || isDiscovery) {
35112
35191
  if (!isLightweight) {
@@ -35213,6 +35292,65 @@ function register(api) {
35213
35292
  const trigger = ctx?.trigger;
35214
35293
  if (sessionId) setSessionTrigger(sessionId, trigger);
35215
35294
  });
35295
+ const MULTI_SPEAKER_PROVIDERS = /* @__PURE__ */ new Set([
35296
+ "discord",
35297
+ "telegram",
35298
+ "imessage",
35299
+ "slack",
35300
+ "whatsapp",
35301
+ "signal",
35302
+ "matrix",
35303
+ "irc"
35304
+ ]);
35305
+ api.on("before_prompt_build", async (event, ctx) => {
35306
+ const c = ctx;
35307
+ const provider = c.messageProvider;
35308
+ if (!provider || !MULTI_SPEAKER_PROVIDERS.has(provider.toLowerCase())) return;
35309
+ const e = event;
35310
+ const messages = e.messages ?? [];
35311
+ const speakers = extractSpeakers(messages);
35312
+ if (speakers.length === 0) return;
35313
+ const mainIdentity = resolveIdentity({
35314
+ configUserId: cfg.userId,
35315
+ identityPath: cfg.identityPath,
35316
+ sessionKey: c.sessionKey,
35317
+ logger,
35318
+ noAutoPersist: true
35319
+ });
35320
+ const mainUserId = mainIdentity.userId.toLowerCase();
35321
+ const otherSpeakers = speakers.filter((s) => s.name !== mainUserId);
35322
+ if (otherSpeakers.length === 0) return;
35323
+ try {
35324
+ const client = await runtime.getClient();
35325
+ const results = await Promise.all(
35326
+ otherSpeakers.map(async (speaker) => {
35327
+ try {
35328
+ const resp = await client.getUserCard({ userId: speaker.name });
35329
+ if (!resp.cardJson) return null;
35330
+ let card;
35331
+ try {
35332
+ card = JSON.parse(resp.cardJson).card ?? resp.cardJson;
35333
+ } catch {
35334
+ card = resp.cardJson;
35335
+ }
35336
+ if (!card || !card.trim()) return null;
35337
+ return `<speaker_context speaker="${speaker.displayName}">
35338
+ ${card.trim()}
35339
+ </speaker_context>`;
35340
+ } catch {
35341
+ return null;
35342
+ }
35343
+ })
35344
+ );
35345
+ const validCards = results.filter((c2) => c2 !== null);
35346
+ if (validCards.length === 0) return;
35347
+ return { appendSystemContext: validCards.join("\n") };
35348
+ } catch (error2) {
35349
+ logger.warn?.(
35350
+ `LibraVDB speaker card injection failed: ${error2 instanceof Error ? error2.message : String(error2)}`
35351
+ );
35352
+ }
35353
+ });
35216
35354
  api.on("session_end", async (_event, ctx) => {
35217
35355
  const sessionId = ctx?.sessionId;
35218
35356
  if (sessionId) clearSessionTrigger(sessionId);
@@ -1,7 +1,7 @@
1
1
  import type { Interceptor } from "@connectrpc/connect";
2
2
  import type { PartialMessage } from "@bufbuild/protobuf";
3
3
  import type { LoggerLike } from "./types.js";
4
- import type { AfterTurnKernelRequest, AfterTurnKernelResponse, BeforeTurnKernelRequest, BeforeTurnKernelResponse, AssembleContextInternalRequest, AssembleContextInternalResponse, BootstrapSessionKernelRequest, BootstrapSessionKernelResponse, CompactSessionRequest, CompactSessionResponse, DeleteAuthoredDocumentRequest, DeleteAuthoredDocumentResponse, DreamPromotionResponse, ExportMemoryRequest, ExportMemoryResponse, FlushNamespaceRequest, FlushNamespaceResponse, FlushRequest, FlushResponse, HealthRequest, HealthResponse, IngestMarkdownDocumentRequest, IngestMarkdownDocumentResponse, IngestMessageKernelRequest, IngestMessageKernelResponse, ListCollectionRequest, ListCollectionResponse, ListLifecycleJournalRequest, ListLifecycleJournalResponse, MarkMemorySupersededRequest, MarkMemorySupersededResponse, MemoryStatusRequest, MemoryStatusResponse, PromoteDreamEntriesRequest, RankCandidatesRequest, RankCandidatesResponse, RebuildIndexRequest, RebuildIndexResponse, ReindexAuthoredDocumentRequest, ReindexAuthoredDocumentResponse, SearchTextCollectionsRequest, SearchTextRequest, SearchTextResponse, SessionLifecycleHintRequest, SessionLifecycleHintResponse, SummarizeMessagesRequest, SummarizeMessagesResponse, ExpandSummaryRequest, ExpandSummaryResponse, UpsertUserCardRequest, UpsertUserCardResponse, GetUserCardRequest, GetUserCardResponse } from "@xdarkicex/libravdb-contracts";
4
+ import type { AfterTurnKernelRequest, AfterTurnKernelResponse, BeforeTurnKernelRequest, BeforeTurnKernelResponse, AssembleContextInternalRequest, AssembleContextInternalResponse, BootstrapSessionKernelRequest, BootstrapSessionKernelResponse, CompactSessionRequest, CompactSessionResponse, DeleteAuthoredDocumentRequest, DeleteAuthoredDocumentResponse, DreamPromotionResponse, ExportMemoryRequest, ExportMemoryResponse, FlushNamespaceRequest, FlushNamespaceResponse, FlushRequest, FlushResponse, HealthRequest, HealthResponse, IngestMarkdownDocumentRequest, IngestMarkdownDocumentResponse, IngestMessageKernelRequest, IngestMessageKernelResponse, ListByMetaRequest, ListByMetaResponse, ListCollectionRequest, ListCollectionResponse, ListLifecycleJournalRequest, ListLifecycleJournalResponse, MarkMemorySupersededRequest, MarkMemorySupersededResponse, MemoryStatusRequest, MemoryStatusResponse, PromoteDreamEntriesRequest, RankCandidatesRequest, RankCandidatesResponse, RebuildIndexRequest, RebuildIndexResponse, ReindexAuthoredDocumentRequest, ReindexAuthoredDocumentResponse, SearchTextCollectionsRequest, SearchTextRequest, SearchTextResponse, SessionLifecycleHintRequest, SessionLifecycleHintResponse, SummarizeMessagesRequest, SummarizeMessagesResponse, ExpandSummaryRequest, ExpandSummaryResponse, UpsertUserCardRequest, UpsertUserCardResponse, GetUserCardRequest, GetUserCardResponse } from "@xdarkicex/libravdb-contracts";
5
5
  export interface LibravDBClientOptions {
6
6
  endpoint?: string;
7
7
  secret?: string;
@@ -67,6 +67,7 @@ export declare class LibravDBClient {
67
67
  rankCandidates(req: PartialMessage<RankCandidatesRequest>): Promise<RankCandidatesResponse>;
68
68
  upsertUserCard(req: PartialMessage<UpsertUserCardRequest>): Promise<UpsertUserCardResponse>;
69
69
  getUserCard(req: PartialMessage<GetUserCardRequest>): Promise<GetUserCardResponse>;
70
+ listByMeta(req: PartialMessage<ListByMetaRequest>): Promise<ListByMetaResponse>;
70
71
  close(): void;
71
72
  }
72
73
  export declare function loadSecretFromEnv(logger?: LoggerLike): string | undefined;
@@ -346,6 +346,10 @@ export class LibravDBClient {
346
346
  this.guardOpen();
347
347
  return this.client.getUserCard(req);
348
348
  }
349
+ async listByMeta(req) {
350
+ this.guardOpen();
351
+ return this.client.listByMeta(req);
352
+ }
349
353
  close() {
350
354
  this.closed = true;
351
355
  }
@@ -189,4 +189,25 @@ export declare function createGetUserCardTool(getClient: ClientGetter, logger?:
189
189
  };
190
190
  execute: (_toolCallId: string, rawParams: unknown) => Promise<ToolResult<GetUserCardDetails>>;
191
191
  };
192
+ type ListUserCardsDetails = {
193
+ users: Array<{
194
+ user_id: string;
195
+ preview: string;
196
+ updated_at?: number;
197
+ version?: number;
198
+ }>;
199
+ total: number;
200
+ error?: string;
201
+ };
202
+ export declare function createListUserCardsTool(getClient: ClientGetter, logger?: LoggerLike): {
203
+ name: string;
204
+ label: string;
205
+ description: string;
206
+ parameters: {
207
+ readonly type: "object";
208
+ readonly additionalProperties: false;
209
+ readonly properties: {};
210
+ };
211
+ execute: (_toolCallId: string, _rawParams: unknown) => Promise<ToolResult<ListUserCardsDetails>>;
212
+ };
192
213
  export {};
@@ -511,3 +511,58 @@ export function createGetUserCardTool(getClient, logger = console) {
511
511
  },
512
512
  };
513
513
  }
514
+ export function createListUserCardsTool(getClient, logger = console) {
515
+ return {
516
+ name: "list_user_cards",
517
+ label: "List User Cards",
518
+ description: "List every person you have a user card for. Returns user IDs and card previews. " +
519
+ "Use this when asked 'who do you know?' or when you need to see the full roster. " +
520
+ "For detailed information about a specific person, use get_user_card.",
521
+ parameters: { type: "object", additionalProperties: false, properties: {} },
522
+ execute: async (_toolCallId, _rawParams) => {
523
+ try {
524
+ const client = await getClient();
525
+ const resp = await client.listByMeta({
526
+ collection: "",
527
+ key: "type",
528
+ value: "user_card",
529
+ });
530
+ const users = [];
531
+ for (const result of resp.results) {
532
+ let userId = "";
533
+ let preview = "";
534
+ let updatedAt;
535
+ let version;
536
+ if (result.metadataJson && result.metadataJson.length > 0) {
537
+ try {
538
+ const decoder = new TextDecoder();
539
+ const meta = JSON.parse(decoder.decode(result.metadataJson));
540
+ userId = typeof meta._user_id === "string" ? meta._user_id : "";
541
+ const cardJson = typeof meta.card_json === "string" ? meta.card_json : null;
542
+ if (cardJson) {
543
+ try {
544
+ preview = JSON.parse(cardJson).card ?? cardJson;
545
+ }
546
+ catch {
547
+ preview = cardJson;
548
+ }
549
+ preview = preview.slice(0, 200);
550
+ }
551
+ updatedAt = typeof meta.updated_at === "number" ? meta.updated_at : undefined;
552
+ version = typeof meta.version === "number" ? meta.version : undefined;
553
+ }
554
+ catch { /* best-effort metadata parse */ }
555
+ }
556
+ if (!userId)
557
+ continue;
558
+ users.push({ user_id: userId, preview, updated_at: updatedAt, version });
559
+ }
560
+ return jsonResult({ users, total: users.length });
561
+ }
562
+ catch (error) {
563
+ logger.warn?.(`list_user_cards failed: ${formatError(error)}`);
564
+ return jsonResult({ users: [], total: 0, error: formatError(error) });
565
+ }
566
+ },
567
+ };
568
+ }
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "id": "libravdb-memory",
3
3
  "name": "LibraVDB Memory",
4
- "description": "Persistent vector memory with three-tier hybrid scoring",
5
- "version": "1.9.10-beta.16",
4
+ "description": "Cognitive memory engine — causal graph reasoning, predictive context, identity tracking, and hybrid vector recall with back-door adjustment scoring",
5
+ "version": "1.9.10-beta.18",
6
6
  "kind": [
7
7
  "memory",
8
8
  "context-engine"
@@ -15,7 +15,9 @@
15
15
  "memory_get",
16
16
  "memory_describe",
17
17
  "memory_expand",
18
- "memory_grep"
18
+ "memory_grep",
19
+ "update_user_card",
20
+ "get_user_card"
19
21
  ]
20
22
  },
21
23
  "activation": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xdarkicex/openclaw-memory-libravdb",
3
- "version": "1.9.10-beta.17",
3
+ "version": "1.9.10-beta.19",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",