memorylake-openclaw 1.1.5 → 1.1.7

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/index.js CHANGED
@@ -238,6 +238,98 @@ function registerMemoryPromptSection(pctx, cfg) {
238
238
  // lib/tools/memory-tools.ts
239
239
  import { Type } from "@sinclair/typebox";
240
240
 
241
+ // lib/telemetry.ts
242
+ import { createHash } from "crypto";
243
+ var PLUGIN_VERSION = true ? "1.1.7" : "dev";
244
+ var DEFAULT_POSTHOG_HOST = "https://us.i.posthog.com/i/v0/e/";
245
+ var POSTHOG_API_KEY = process.env.MEMORYLAKE_POSTHOG_API_KEY?.trim() ?? "";
246
+ var POSTHOG_HOST = normalizePosthogHost(
247
+ process.env.MEMORYLAKE_POSTHOG_HOST?.trim() || DEFAULT_POSTHOG_HOST
248
+ );
249
+ var FLUSH_INTERVAL_MS = 5e3;
250
+ var FLUSH_THRESHOLD = 10;
251
+ var eventQueue = [];
252
+ var flushTimer;
253
+ var telemetryEnabled;
254
+ var exitHandlerInstalled = false;
255
+ function normalizePosthogHost(value) {
256
+ const trimmed = value.replace(/\/+$/, "");
257
+ return trimmed.endsWith("/i/v0/e") ? `${trimmed}/` : `${trimmed}/i/v0/e/`;
258
+ }
259
+ function isTelemetryEnabled() {
260
+ if (telemetryEnabled !== void 0) return telemetryEnabled;
261
+ telemetryEnabled = !!POSTHOG_API_KEY;
262
+ return telemetryEnabled;
263
+ }
264
+ function getDistinctId(apiKey = "") {
265
+ return createHash("sha256").update(apiKey).digest("hex");
266
+ }
267
+ function ensureFlushTimer() {
268
+ if (flushTimer) return;
269
+ flushTimer = setInterval(flushEvents, FLUSH_INTERVAL_MS);
270
+ if (typeof flushTimer === "object" && "unref" in flushTimer) {
271
+ flushTimer.unref();
272
+ }
273
+ }
274
+ function ensureExitHandler() {
275
+ if (exitHandlerInstalled) return;
276
+ exitHandlerInstalled = true;
277
+ process.on("beforeExit", async () => {
278
+ if (eventQueue.length === 0) return;
279
+ const batch = eventQueue;
280
+ eventQueue = [];
281
+ const body = JSON.stringify({ api_key: POSTHOG_API_KEY, batch });
282
+ try {
283
+ await fetch(POSTHOG_HOST, {
284
+ method: "POST",
285
+ headers: { "Content-Type": "application/json" },
286
+ body,
287
+ signal: AbortSignal.timeout(3e3)
288
+ });
289
+ } catch {
290
+ }
291
+ });
292
+ }
293
+ function flushEvents() {
294
+ if (eventQueue.length === 0) return;
295
+ const batch = eventQueue;
296
+ eventQueue = [];
297
+ const body = JSON.stringify({ api_key: POSTHOG_API_KEY, batch });
298
+ fetch(POSTHOG_HOST, {
299
+ method: "POST",
300
+ headers: { "Content-Type": "application/json" },
301
+ body,
302
+ signal: AbortSignal.timeout(3e3)
303
+ }).catch(() => {
304
+ });
305
+ }
306
+ function captureEvent(eventName, properties = {}, ctx) {
307
+ if (!isTelemetryEnabled()) return;
308
+ try {
309
+ const distinctId = getDistinctId(ctx.apiKey ?? "");
310
+ eventQueue.push({
311
+ event: eventName,
312
+ distinct_id: distinctId,
313
+ properties: {
314
+ source: "OPENCLAW",
315
+ language: "node",
316
+ plugin_version: PLUGIN_VERSION,
317
+ node_version: process.version,
318
+ os: process.platform,
319
+ $process_person_profile: false,
320
+ $lib: "posthog-node",
321
+ ...properties
322
+ }
323
+ });
324
+ ensureFlushTimer();
325
+ ensureExitHandler();
326
+ if (eventQueue.length >= FLUSH_THRESHOLD) {
327
+ flushEvents();
328
+ }
329
+ } catch {
330
+ }
331
+ }
332
+
241
333
  // lib/provider.ts
242
334
  import got from "got";
243
335
 
@@ -473,7 +565,7 @@ function getProvider(effectiveCfg) {
473
565
  }
474
566
 
475
567
  // lib/utils/builders.ts
476
- var PLUGIN_VERSION = true ? "1.1.5" : "dev";
568
+ var PLUGIN_VERSION2 = true ? "1.1.7" : "dev";
477
569
  function buildDocumentContext(results, maxChunkLength = 1e4) {
478
570
  const parts = [];
479
571
  for (const result of results) {
@@ -552,7 +644,7 @@ function buildAddOptions(effectiveCfg, userIdOverride, sessionId) {
552
644
  const opts = {
553
645
  user_id: userIdOverride || effectiveCfg.userId,
554
646
  infer: true,
555
- metadata: { source: "OPENCLAW", plugin_version: PLUGIN_VERSION }
647
+ metadata: { source: "OPENCLAW", plugin_version: PLUGIN_VERSION2 }
556
648
  };
557
649
  if (sessionId) opts.chat_session_id = sessionId;
558
650
  return opts;
@@ -588,6 +680,7 @@ function registerMemoryTools(pctx, cfg) {
588
680
  )
589
681
  }),
590
682
  async execute(_toolCallId, params) {
683
+ const start = Date.now();
591
684
  const effectiveCfg = resolveConfig(ctx);
592
685
  const effectiveProvider = getProvider(effectiveCfg);
593
686
  const { query, limit, userId } = params;
@@ -601,6 +694,8 @@ function registerMemoryTools(pctx, cfg) {
601
694
  const sections = [];
602
695
  let memoryCount = 0;
603
696
  let docCount = 0;
697
+ let conflictCount = 0;
698
+ let conflictFetchFailed = false;
604
699
  let sanitizedMemories = [];
605
700
  if (memoryResult.status === "fulfilled" && memoryResult.value.length > 0) {
606
701
  const results = memoryResult.value;
@@ -621,6 +716,7 @@ ${text}`);
621
716
  const effectiveUserId = userId ?? effectiveCfg.userId;
622
717
  const conflicts = await effectiveProvider.listConflicts(conflictMemoryIds, effectiveUserId);
623
718
  if (conflicts.length > 0) {
719
+ conflictCount = conflicts.length;
624
720
  const conflictText = buildConflictContext(conflicts);
625
721
  sections.push(`## Memory Conflicts
626
722
  The following memories have unresolved conflicts. Review and help the user resolve them if relevant:
@@ -628,6 +724,7 @@ The following memories have unresolved conflicts. Review and help the user resol
628
724
  ${conflictText}`);
629
725
  }
630
726
  } catch (err) {
727
+ conflictFetchFailed = true;
631
728
  sections.push(`## Memory Conflicts
632
729
  Failed to fetch conflicts: ${String(err)}`);
633
730
  }
@@ -647,6 +744,20 @@ ${context}`);
647
744
  sections.push(`## Documents
648
745
  Document search failed: ${String(docResult.reason)}`);
649
746
  }
747
+ const allFailed = memoryResult.status === "rejected" && docResult.status === "rejected";
748
+ const partialSuccess = !allFailed && (memoryResult.status === "rejected" || docResult.status === "rejected" || conflictFetchFailed);
749
+ captureEvent(
750
+ "memorylake.tool.retrieve_context",
751
+ {
752
+ success: !allFailed,
753
+ partial_success: partialSuccess,
754
+ latency_ms: Date.now() - start,
755
+ memory_count: memoryCount,
756
+ document_count: docCount,
757
+ conflict_count: conflictCount
758
+ },
759
+ effectiveCfg
760
+ );
650
761
  if (memoryCount === 0 && docCount === 0) {
651
762
  return {
652
763
  content: [
@@ -691,6 +802,7 @@ Document search failed: ${String(docResult.reason)}`);
691
802
  )
692
803
  }),
693
804
  async execute(_toolCallId, params) {
805
+ const start = Date.now();
694
806
  const effectiveCfg = resolveConfig(ctx);
695
807
  const effectiveProvider = getProvider(effectiveCfg);
696
808
  const { text, userId } = params;
@@ -700,6 +812,15 @@ Document search failed: ${String(docResult.reason)}`);
700
812
  buildAddOptions(effectiveCfg, userId, ctx?.sessionId)
701
813
  );
702
814
  const count = result.results?.length ?? 0;
815
+ captureEvent(
816
+ "memorylake.tool.memory_store",
817
+ {
818
+ success: true,
819
+ latency_ms: Date.now() - start,
820
+ result_count: count
821
+ },
822
+ effectiveCfg
823
+ );
703
824
  return {
704
825
  content: [
705
826
  {
@@ -713,6 +834,15 @@ Document search failed: ${String(docResult.reason)}`);
713
834
  }
714
835
  };
715
836
  } catch (err) {
837
+ captureEvent(
838
+ "memorylake.tool.memory_store",
839
+ {
840
+ success: false,
841
+ latency_ms: Date.now() - start,
842
+ error: String(err)
843
+ },
844
+ effectiveCfg
845
+ );
716
846
  return {
717
847
  content: [
718
848
  {
@@ -740,12 +870,22 @@ Document search failed: ${String(docResult.reason)}`);
740
870
  )
741
871
  }),
742
872
  async execute(_toolCallId, params) {
873
+ const start = Date.now();
743
874
  const effectiveCfg = resolveConfig(ctx);
744
875
  const effectiveProvider = getProvider(effectiveCfg);
745
876
  const { userId } = params;
746
877
  try {
747
878
  const uid = userId || effectiveCfg.userId;
748
879
  const memories = await effectiveProvider.getAll({ user_id: uid });
880
+ captureEvent(
881
+ "memorylake.tool.memory_list",
882
+ {
883
+ success: true,
884
+ latency_ms: Date.now() - start,
885
+ result_count: memories.length
886
+ },
887
+ effectiveCfg
888
+ );
749
889
  if (!memories || memories.length === 0) {
750
890
  return {
751
891
  content: [
@@ -774,6 +914,15 @@ ${text}`
774
914
  details: { count: memories.length, memories: sanitized }
775
915
  };
776
916
  } catch (err) {
917
+ captureEvent(
918
+ "memorylake.tool.memory_list",
919
+ {
920
+ success: false,
921
+ latency_ms: Date.now() - start,
922
+ error: String(err)
923
+ },
924
+ effectiveCfg
925
+ );
777
926
  return {
778
927
  content: [
779
928
  {
@@ -797,11 +946,20 @@ ${text}`
797
946
  memoryId: Type.String({ description: "Memory ID to delete" })
798
947
  }),
799
948
  async execute(_toolCallId, params) {
949
+ const start = Date.now();
800
950
  const effectiveCfg = resolveConfig(ctx);
801
951
  const effectiveProvider = getProvider(effectiveCfg);
802
952
  const { memoryId } = params;
803
953
  try {
804
954
  await effectiveProvider.delete(memoryId);
955
+ captureEvent(
956
+ "memorylake.tool.memory_forget",
957
+ {
958
+ success: true,
959
+ latency_ms: Date.now() - start
960
+ },
961
+ effectiveCfg
962
+ );
805
963
  return {
806
964
  content: [
807
965
  { type: "text", text: `Memory ${memoryId} forgotten.` }
@@ -809,6 +967,15 @@ ${text}`
809
967
  details: { action: "deleted", id: memoryId }
810
968
  };
811
969
  } catch (err) {
970
+ captureEvent(
971
+ "memorylake.tool.memory_forget",
972
+ {
973
+ success: false,
974
+ latency_ms: Date.now() - start,
975
+ error: String(err)
976
+ },
977
+ effectiveCfg
978
+ );
812
979
  return {
813
980
  content: [
814
981
  {
@@ -863,6 +1030,7 @@ function registerDocumentTools(pctx) {
863
1030
  })
864
1031
  }),
865
1032
  async execute(_toolCallId, params) {
1033
+ const start = Date.now();
866
1034
  const effectiveCfg = resolveConfig(ctx);
867
1035
  const effectiveProvider = getProvider(effectiveCfg);
868
1036
  const { documentId } = params;
@@ -892,6 +1060,14 @@ function registerDocumentTools(pctx) {
892
1060
  }
893
1061
  }
894
1062
  fs3.renameSync(tempPath, localPath);
1063
+ captureEvent(
1064
+ "memorylake.tool.document_download",
1065
+ {
1066
+ success: true,
1067
+ latency_ms: Date.now() - start
1068
+ },
1069
+ effectiveCfg
1070
+ );
895
1071
  return {
896
1072
  content: [
897
1073
  {
@@ -905,6 +1081,15 @@ You MUST now call the message tool with action="send" and media set to this loca
905
1081
  details: { documentId, localPath }
906
1082
  };
907
1083
  } catch (err) {
1084
+ captureEvent(
1085
+ "memorylake.tool.document_download",
1086
+ {
1087
+ success: false,
1088
+ latency_ms: Date.now() - start,
1089
+ error: String(err)
1090
+ },
1091
+ effectiveCfg
1092
+ );
908
1093
  return {
909
1094
  content: [
910
1095
  {
@@ -973,6 +1158,7 @@ function registerSearchTools(pctx, cfg) {
973
1158
  )
974
1159
  }),
975
1160
  async execute(_toolCallId, params) {
1161
+ const start = Date.now();
976
1162
  const effectiveCfg = resolveConfig(ctx);
977
1163
  const effectiveProvider = getProvider(effectiveCfg);
978
1164
  const {
@@ -997,6 +1183,15 @@ function registerSearchTools(pctx, cfg) {
997
1183
  } : void 0
998
1184
  });
999
1185
  if (!response.results || response.results.length === 0) {
1186
+ captureEvent(
1187
+ "memorylake.tool.advanced_web_search",
1188
+ {
1189
+ success: true,
1190
+ latency_ms: Date.now() - start,
1191
+ result_count: 0
1192
+ },
1193
+ effectiveCfg
1194
+ );
1000
1195
  return {
1001
1196
  content: [
1002
1197
  { type: "text", text: "No relevant web results found." }
@@ -1005,6 +1200,15 @@ function registerSearchTools(pctx, cfg) {
1005
1200
  };
1006
1201
  }
1007
1202
  const context = buildWebSearchContext(response.results);
1203
+ captureEvent(
1204
+ "memorylake.tool.advanced_web_search",
1205
+ {
1206
+ success: true,
1207
+ latency_ms: Date.now() - start,
1208
+ result_count: response.results.length
1209
+ },
1210
+ effectiveCfg
1211
+ );
1008
1212
  return {
1009
1213
  content: [
1010
1214
  {
@@ -1021,6 +1225,15 @@ ${context}`
1021
1225
  }
1022
1226
  };
1023
1227
  } catch (err) {
1228
+ captureEvent(
1229
+ "memorylake.tool.advanced_web_search",
1230
+ {
1231
+ success: false,
1232
+ latency_ms: Date.now() - start,
1233
+ error: String(err)
1234
+ },
1235
+ effectiveCfg
1236
+ );
1024
1237
  return {
1025
1238
  content: [
1026
1239
  {
@@ -1033,7 +1246,7 @@ ${context}`
1033
1246
  }
1034
1247
  }
1035
1248
  }),
1036
- { optional: true }
1249
+ { optional: true, name: "advanced_web_search" }
1037
1250
  );
1038
1251
  api.registerTool(
1039
1252
  (ctx) => ({
@@ -1076,6 +1289,7 @@ ${context}`
1076
1289
  )
1077
1290
  }),
1078
1291
  async execute(_toolCallId, params) {
1292
+ const start = Date.now();
1079
1293
  const effectiveCfg = resolveConfig(ctx);
1080
1294
  const effectiveProvider = getProvider(effectiveCfg);
1081
1295
  const {
@@ -1087,6 +1301,15 @@ ${context}`
1087
1301
  } = params;
1088
1302
  const dataset = normalizeOpenDataCategory(rawDataset);
1089
1303
  if (!dataset) {
1304
+ captureEvent(
1305
+ "memorylake.tool.open_data_search",
1306
+ {
1307
+ success: false,
1308
+ latency_ms: Date.now() - start,
1309
+ error: "unsupported_dataset"
1310
+ },
1311
+ effectiveCfg
1312
+ );
1090
1313
  return {
1091
1314
  content: [
1092
1315
  {
@@ -1102,6 +1325,15 @@ ${context}`
1102
1325
  if (projectInfo.industries.length > 0) {
1103
1326
  const allowedIds = projectInfo.industries.map((ind) => ind.id);
1104
1327
  if (!allowedIds.includes(dataset)) {
1328
+ captureEvent(
1329
+ "memorylake.tool.open_data_search",
1330
+ {
1331
+ success: false,
1332
+ latency_ms: Date.now() - start,
1333
+ error: "dataset_not_allowed"
1334
+ },
1335
+ effectiveCfg
1336
+ );
1105
1337
  const allowed = projectInfo.industries.map((ind) => `${ind.id} (${ind.name})`).join(", ");
1106
1338
  return {
1107
1339
  content: [
@@ -1125,6 +1357,15 @@ ${context}`
1125
1357
  end_date: endDate
1126
1358
  });
1127
1359
  if (!response.results || response.results.length === 0) {
1360
+ captureEvent(
1361
+ "memorylake.tool.open_data_search",
1362
+ {
1363
+ success: true,
1364
+ latency_ms: Date.now() - start,
1365
+ result_count: 0
1366
+ },
1367
+ effectiveCfg
1368
+ );
1128
1369
  return {
1129
1370
  content: [
1130
1371
  { type: "text", text: "No relevant open data results found." }
@@ -1133,6 +1374,15 @@ ${context}`
1133
1374
  };
1134
1375
  }
1135
1376
  const context = buildOpenDataContext(response.results);
1377
+ captureEvent(
1378
+ "memorylake.tool.open_data_search",
1379
+ {
1380
+ success: true,
1381
+ latency_ms: Date.now() - start,
1382
+ result_count: response.results.length
1383
+ },
1384
+ effectiveCfg
1385
+ );
1136
1386
  return {
1137
1387
  content: [
1138
1388
  {
@@ -1149,6 +1399,15 @@ ${context}`
1149
1399
  }
1150
1400
  };
1151
1401
  } catch (err) {
1402
+ captureEvent(
1403
+ "memorylake.tool.open_data_search",
1404
+ {
1405
+ success: false,
1406
+ latency_ms: Date.now() - start,
1407
+ error: String(err)
1408
+ },
1409
+ effectiveCfg
1410
+ );
1152
1411
  return {
1153
1412
  content: [
1154
1413
  {
@@ -1160,7 +1419,8 @@ ${context}`
1160
1419
  };
1161
1420
  }
1162
1421
  }
1163
- })
1422
+ }),
1423
+ { name: "open_data_search" }
1164
1424
  );
1165
1425
  }
1166
1426
 
@@ -1175,6 +1435,7 @@ function registerCli(pctx, cfg) {
1175
1435
  ({ program }) => {
1176
1436
  const memorylake = program.command("memorylake").description("MemoryLake memory plugin commands");
1177
1437
  memorylake.command("search").description("Search memories in MemoryLake").argument("<query>", "Search query").option("--limit <n>", "Max results", String(cfg.topK)).action(async (query, opts) => {
1438
+ const start = Date.now();
1178
1439
  try {
1179
1440
  const limit = parseInt(opts.limit, 10);
1180
1441
  const results = await provider.search(
@@ -1182,6 +1443,11 @@ function registerCli(pctx, cfg) {
1182
1443
  buildSearchOptions(cfg, void 0, limit)
1183
1444
  );
1184
1445
  if (!results.length) {
1446
+ captureEvent("memorylake.cli.search", {
1447
+ success: true,
1448
+ latency_ms: Date.now() - start,
1449
+ result_count: 0
1450
+ }, cfg);
1185
1451
  console.log("No memories found.");
1186
1452
  return;
1187
1453
  }
@@ -1191,12 +1457,23 @@ function registerCli(pctx, cfg) {
1191
1457
  user_id: r.user_id,
1192
1458
  created_at: r.created_at
1193
1459
  }));
1460
+ captureEvent("memorylake.cli.search", {
1461
+ success: true,
1462
+ latency_ms: Date.now() - start,
1463
+ result_count: results.length
1464
+ }, cfg);
1194
1465
  console.log(JSON.stringify(output, null, 2));
1195
1466
  } catch (err) {
1467
+ captureEvent("memorylake.cli.search", {
1468
+ success: false,
1469
+ latency_ms: Date.now() - start,
1470
+ error: String(err)
1471
+ }, cfg);
1196
1472
  console.error(`Search failed: ${String(err)}`);
1197
1473
  }
1198
1474
  });
1199
1475
  memorylake.command("upload").description("Upload files or directories to MemoryLake").argument("<path>", "File or directory path to upload").option("--agent <id>", "Agent ID (resolves workspace and per-agent projectId)").option("--project-id <id>", "Override project ID (takes precedence over --agent)").action(async (targetPath, opts) => {
1476
+ const start = Date.now();
1200
1477
  let effectiveCfg = cfg;
1201
1478
  if (opts.agent) {
1202
1479
  try {
@@ -1216,10 +1493,20 @@ function registerCli(pctx, cfg) {
1216
1493
  }
1217
1494
  const effectiveProjectId = opts.projectId || effectiveCfg.projectId;
1218
1495
  if (!effectiveProjectId) {
1496
+ captureEvent("memorylake.cli.upload", {
1497
+ success: false,
1498
+ latency_ms: Date.now() - start,
1499
+ error: "missing_project_id"
1500
+ }, effectiveCfg);
1219
1501
  console.error("No project ID configured. Use --project-id or set up agent/workspace config.");
1220
1502
  return;
1221
1503
  }
1222
1504
  if (!effectiveCfg.host || !effectiveCfg.apiKey) {
1505
+ captureEvent("memorylake.cli.upload", {
1506
+ success: false,
1507
+ latency_ms: Date.now() - start,
1508
+ error: "missing_host_or_api_key"
1509
+ }, effectiveCfg);
1223
1510
  console.error("Missing host or apiKey in config. Check your MemoryLake configuration.");
1224
1511
  return;
1225
1512
  }
@@ -1231,6 +1518,11 @@ function registerCli(pctx, cfg) {
1231
1518
  );
1232
1519
  uploadFn = uploadModule.uploadAuto;
1233
1520
  } catch (err) {
1521
+ captureEvent("memorylake.cli.upload", {
1522
+ success: false,
1523
+ latency_ms: Date.now() - start,
1524
+ error: String(err)
1525
+ }, effectiveCfg);
1234
1526
  console.error(`Failed to load upload module: ${String(err)}`);
1235
1527
  return;
1236
1528
  }
@@ -1243,15 +1535,34 @@ function registerCli(pctx, cfg) {
1243
1535
  filePath: absPath,
1244
1536
  fileName: path3.basename(absPath)
1245
1537
  });
1538
+ captureEvent("memorylake.cli.upload", {
1539
+ success: true,
1540
+ latency_ms: Date.now() - start,
1541
+ agent_override: !!opts.agent,
1542
+ project_override: !!opts.projectId
1543
+ }, effectiveCfg);
1246
1544
  } catch (err) {
1545
+ captureEvent("memorylake.cli.upload", {
1546
+ success: false,
1547
+ latency_ms: Date.now() - start,
1548
+ error: String(err),
1549
+ agent_override: !!opts.agent,
1550
+ project_override: !!opts.projectId
1551
+ }, effectiveCfg);
1247
1552
  console.error(`Upload failed: ${String(err)}`);
1248
1553
  }
1249
1554
  });
1250
1555
  memorylake.command("stats").description("Show memory statistics from MemoryLake").action(async () => {
1556
+ const start = Date.now();
1251
1557
  try {
1252
1558
  const memories = await provider.getAll({
1253
1559
  user_id: cfg.userId
1254
1560
  });
1561
+ captureEvent("memorylake.cli.stats", {
1562
+ success: true,
1563
+ latency_ms: Date.now() - start,
1564
+ result_count: Array.isArray(memories) ? memories.length : 0
1565
+ }, cfg);
1255
1566
  console.log(`User: ${cfg.userId}`);
1256
1567
  console.log(
1257
1568
  `Total memories: ${Array.isArray(memories) ? memories.length : "unknown"}`
@@ -1260,6 +1571,11 @@ function registerCli(pctx, cfg) {
1260
1571
  `Auto-recall: ${cfg.autoRecall}, Auto-capture: ${cfg.autoCapture}`
1261
1572
  );
1262
1573
  } catch (err) {
1574
+ captureEvent("memorylake.cli.stats", {
1575
+ success: false,
1576
+ latency_ms: Date.now() - start,
1577
+ error: String(err)
1578
+ }, cfg);
1263
1579
  console.error(`Stats failed: ${String(err)}`);
1264
1580
  }
1265
1581
  });
@@ -1273,8 +1589,10 @@ import path5 from "path";
1273
1589
 
1274
1590
  // lib/helpers/upload-record.ts
1275
1591
  import fs4 from "fs";
1592
+ import os4 from "os";
1276
1593
  import path4 from "path";
1277
1594
  var UPLOADED_RECORD_FILE = "uploaded.json";
1595
+ var MEDIA_URI_PREFIX = "media://inbound/";
1278
1596
  function getUploadedRecord(workspaceDir) {
1279
1597
  const filePath = path4.join(workspaceDir, ".memorylake", UPLOADED_RECORD_FILE);
1280
1598
  try {
@@ -1300,14 +1618,22 @@ function needsUpload(record, filePath) {
1300
1618
  const prev = record[filePath];
1301
1619
  return !prev || prev.mtimeMs !== stat.mtimeMs ? stat : null;
1302
1620
  }
1621
+ function resolveInboundMediaUri(value) {
1622
+ if (value.startsWith(MEDIA_URI_PREFIX)) {
1623
+ return path4.join(os4.homedir(), ".openclaw", "media", "inbound", value.slice(MEDIA_URI_PREFIX.length));
1624
+ }
1625
+ return value;
1626
+ }
1303
1627
  function extractInboundPaths(prompt) {
1304
1628
  const sep = "[/\\\\]";
1305
- const regex = new RegExp(
1629
+ const absRegex = new RegExp(
1306
1630
  `(?:[A-Za-z]:${sep}|/)\\S*?media${sep}inbound${sep}.+?\\.[a-zA-Z0-9]{1,6}(?=[^a-zA-Z0-9]|$)`,
1307
1631
  "g"
1308
1632
  );
1309
- const matches = prompt.match(regex) || [];
1310
- return [...new Set(matches)];
1633
+ const uriRegex = /media:\/\/inbound\/.+?\.[a-zA-Z0-9]{1,6}(?=[^a-zA-Z0-9]|$)/g;
1634
+ const absMatches = prompt.match(absRegex) || [];
1635
+ const uriMatches = (prompt.match(uriRegex) || []).map(resolveInboundMediaUri);
1636
+ return [.../* @__PURE__ */ new Set([...absMatches, ...uriMatches])];
1311
1637
  }
1312
1638
 
1313
1639
  // lib/hooks/auto-upload.ts
@@ -1341,6 +1667,7 @@ function registerAutoUpload(pctx) {
1341
1667
  uploadAutoFn = uploadModule.uploadAuto;
1342
1668
  }
1343
1669
  for (const { filePath, stat } of filesToUpload) {
1670
+ const uploadStart = Date.now();
1344
1671
  try {
1345
1672
  await uploadAutoFn({
1346
1673
  host: effectiveCfg.host,
@@ -1352,10 +1679,27 @@ function registerAutoUpload(pctx) {
1352
1679
  const current = getUploadedRecord(workspaceDir);
1353
1680
  current[filePath] = { mtimeMs: stat.mtimeMs };
1354
1681
  saveUploadedRecord(workspaceDir, current);
1682
+ captureEvent(
1683
+ "memorylake.hook.upload",
1684
+ {
1685
+ success: true,
1686
+ latency_ms: Date.now() - uploadStart
1687
+ },
1688
+ effectiveCfg
1689
+ );
1355
1690
  api.logger.info(
1356
1691
  `memorylake-openclaw: auto-uploaded ${path5.basename(filePath)}`
1357
1692
  );
1358
1693
  } catch (err) {
1694
+ captureEvent(
1695
+ "memorylake.hook.upload",
1696
+ {
1697
+ success: false,
1698
+ latency_ms: Date.now() - uploadStart,
1699
+ error: String(err)
1700
+ },
1701
+ effectiveCfg
1702
+ );
1359
1703
  api.logger.warn(
1360
1704
  `memorylake-openclaw: auto-upload failed for ${filePath}: ${String(err)}`
1361
1705
  );
@@ -1385,12 +1729,15 @@ function registerAutoRecall(pctx) {
1385
1729
  return;
1386
1730
  }
1387
1731
  if (!event.prompt) return;
1732
+ const recallStart = Date.now();
1388
1733
  const effectiveCfg = resolveConfig(ctx);
1389
1734
  const effectiveProvider = getProvider(effectiveCfg);
1390
1735
  const sessionId = ctx?.sessionId ?? void 0;
1391
1736
  let industries;
1737
+ let cacheHit = false;
1392
1738
  if (sessionId && sessionIndustriesCache.has(sessionId)) {
1393
1739
  industries = sessionIndustriesCache.get(sessionId);
1740
+ cacheHit = true;
1394
1741
  } else {
1395
1742
  try {
1396
1743
  const projectInfo = await effectiveProvider.getProject();
@@ -1441,6 +1788,16 @@ When the user's question relates to any of these categories, use the open_data_s
1441
1788
  if (appendParts.length > 0) {
1442
1789
  result.appendSystemContext = appendParts.join("\n\n");
1443
1790
  }
1791
+ captureEvent(
1792
+ "memorylake.hook.recall",
1793
+ {
1794
+ success: true,
1795
+ cache_hit: cacheHit,
1796
+ industries_count: industries?.length ?? 0,
1797
+ latency_ms: Date.now() - recallStart
1798
+ },
1799
+ effectiveCfg
1800
+ );
1444
1801
  return result;
1445
1802
  });
1446
1803
  }
@@ -1716,6 +2073,7 @@ function registerAutoCapture(pctx) {
1716
2073
  const effectiveCfg = resolveConfig(ctx);
1717
2074
  const effectiveProvider = getProvider(effectiveCfg);
1718
2075
  const lastSent = sessionWatermarks.get(sessionId) ?? 0;
2076
+ const captureStart = Date.now();
1719
2077
  try {
1720
2078
  const formattedMessages = [];
1721
2079
  let maxTimestamp = lastSent;
@@ -1742,12 +2100,31 @@ function registerAutoCapture(pctx) {
1742
2100
  sessionWatermarks.set(sessionId, maxTimestamp);
1743
2101
  }
1744
2102
  const capturedCount = result.results?.length ?? 0;
2103
+ captureEvent(
2104
+ "memorylake.hook.capture",
2105
+ {
2106
+ success: true,
2107
+ captured_count: capturedCount,
2108
+ latency_ms: Date.now() - captureStart,
2109
+ message_count: formattedMessages.length
2110
+ },
2111
+ effectiveCfg
2112
+ );
1745
2113
  if (capturedCount > 0) {
1746
2114
  api.logger.info(
1747
2115
  `memorylake-openclaw: auto-captured ${capturedCount} memories from ${formattedMessages.length} new message(s)`
1748
2116
  );
1749
2117
  }
1750
2118
  } catch (err) {
2119
+ captureEvent(
2120
+ "memorylake.hook.capture",
2121
+ {
2122
+ success: false,
2123
+ latency_ms: Date.now() - captureStart,
2124
+ error: String(err)
2125
+ },
2126
+ effectiveCfg
2127
+ );
1751
2128
  api.logger.warn(`memorylake-openclaw: capture failed: ${String(err)}`);
1752
2129
  }
1753
2130
  });
@@ -1764,6 +2141,15 @@ var memoryPlugin = {
1764
2141
  const cfg = memoryLakeConfigSchema.parse(api.pluginConfig);
1765
2142
  const pctx = createPluginContext(api, cfg);
1766
2143
  registerMemoryPromptSection(pctx, cfg);
2144
+ captureEvent(
2145
+ "memorylake.plugin.registered",
2146
+ {
2147
+ auto_recall: cfg.autoRecall,
2148
+ auto_capture: cfg.autoCapture,
2149
+ auto_upload: cfg.autoUpload
2150
+ },
2151
+ cfg
2152
+ );
1767
2153
  api.logger.info(
1768
2154
  `memorylake-openclaw: registered (user: ${cfg.userId}, autoRecall: ${cfg.autoRecall}, autoCapture: ${cfg.autoCapture}, autoUpload: ${cfg.autoUpload})`
1769
2155
  );
@@ -1777,6 +2163,15 @@ var memoryPlugin = {
1777
2163
  api.registerService({
1778
2164
  id: "memorylake-openclaw",
1779
2165
  start: () => {
2166
+ captureEvent(
2167
+ "memorylake.plugin.initialized",
2168
+ {
2169
+ auto_recall: cfg.autoRecall,
2170
+ auto_capture: cfg.autoCapture,
2171
+ auto_upload: cfg.autoUpload
2172
+ },
2173
+ cfg
2174
+ );
1780
2175
  api.logger.info(
1781
2176
  `memorylake-openclaw: initialized (user: ${cfg.userId}, autoRecall: ${cfg.autoRecall}, autoCapture: ${cfg.autoCapture}, autoUpload: ${cfg.autoUpload})`
1782
2177
  );