@agenr/agenr-plugin 1.7.4 → 1.8.0

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
@@ -1,37 +1,33 @@
1
1
  import {
2
2
  OpenClawTranscriptParser,
3
+ createOpenClawRepository,
3
4
  deriveOpenClawSessionIdFromFilePath,
4
5
  ingestEpisodeTranscript,
5
- normalizeClaimKey,
6
6
  openClawTranscriptParser,
7
7
  parseTuiSessionKey,
8
8
  readOpenClawSessionsStore,
9
9
  storeEntriesDetailed
10
- } from "./chunk-NXCCTZ4G.js";
11
- import {
12
- runUnifiedRecall
13
- } from "./chunk-IZDGXMTQ.js";
10
+ } from "./chunk-6CEKKEFZ.js";
14
11
  import {
15
12
  EMBEDDING_DIMENSIONS,
16
13
  ENTRY_TYPES,
17
14
  EXPIRY_LEVELS,
18
- VECTOR_INDEX_NAME,
19
- buildActiveEntryClause,
20
15
  createDatabase,
21
16
  createEmbeddingClient,
22
17
  createRecallAdapter,
23
- mapEntryRow,
18
+ normalizeManualClaimKeyUpdate,
24
19
  readConfig,
25
- readNumber,
26
- readOptionalString,
27
- readRequiredString,
28
20
  resolveClaimExtractionConfig,
29
21
  resolveConfigPath,
30
22
  resolveDbPath,
31
23
  resolveEmbeddingApiKey,
32
- resolveEmbeddingModel
33
- } from "./chunk-NIQKTINU.js";
34
- import "./chunk-7WL5EAQZ.js";
24
+ resolveEmbeddingModel,
25
+ runUnifiedRecall,
26
+ validateTemporalValidityRange
27
+ } from "./chunk-LVDQXSHP.js";
28
+ import {
29
+ resolveClaimSlotPolicy
30
+ } from "./chunk-GUDCFFRV.js";
35
31
 
36
32
  // src/adapters/openclaw/index.ts
37
33
  import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
@@ -123,6 +119,14 @@ function buildSessionSourceFile(ctx) {
123
119
  const target = ctx.sessionKey ?? ctx.sessionId ?? ctx.agentId ?? "unknown";
124
120
  return `openclaw-session:${target}`;
125
121
  }
122
+ function buildToolCallClaimSupport(ctx, toolName, observedAt) {
123
+ return {
124
+ claim_support_source_kind: "tool_call",
125
+ claim_support_locator: `${buildSessionSourceFile(ctx)}#${toolName}`,
126
+ claim_support_observed_at: observedAt,
127
+ claim_support_mode: "explicit"
128
+ };
129
+ }
126
130
  function logToolCall(logger, toolName, ctx, summary, sanitizedParams) {
127
131
  logger.info(`[agenr] tool=${toolName} ${formatToolSessionContext(ctx)} ${summary}`);
128
132
  logger.info(`[agenr] tool=${toolName} ${formatToolSessionContext(ctx)} params=${JSON.stringify(sanitizedParams)}`);
@@ -171,6 +175,9 @@ function formatRecallToolSummary(params) {
171
175
  if (params.tags.length > 0) {
172
176
  parts.push(`tags=${JSON.stringify(params.tags)}`);
173
177
  }
178
+ if (params.asOf) {
179
+ parts.push(`as_of=${JSON.stringify(params.asOf)}`);
180
+ }
174
181
  return parts.join(" ");
175
182
  }
176
183
  function sanitizeRecallToolParams(params) {
@@ -180,7 +187,8 @@ function sanitizeRecallToolParams(params) {
180
187
  ...params.limit !== void 0 ? { limit: params.limit } : {},
181
188
  ...params.threshold !== void 0 ? { threshold: params.threshold } : {},
182
189
  ...params.types.length > 0 ? { types: params.types } : {},
183
- ...params.tags.length > 0 ? { tags: params.tags } : {}
190
+ ...params.tags.length > 0 ? { tags: params.tags } : {},
191
+ ...params.asOf ? { asOf: params.asOf } : {}
184
192
  };
185
193
  }
186
194
  function sanitizeRetireToolParams(params) {
@@ -220,15 +228,24 @@ function formatUnifiedRecallResults(result) {
220
228
  lines.push(`${result.timeWindow.start} -> ${result.timeWindow.end} (${result.timeWindow.timezone}) from ${JSON.stringify(result.timeWindow.resolvedFrom)}`);
221
229
  lines.push("");
222
230
  }
231
+ if (result.asOf) {
232
+ lines.push("As Of");
233
+ lines.push(result.asOf);
234
+ lines.push("");
235
+ }
223
236
  const renderEntriesFirst = result.routing.detectedIntent === "historical_state";
224
237
  if (renderEntriesFirst) {
225
238
  appendEntryMatches(lines, result);
226
239
  lines.push("");
240
+ appendClaimTransitions(lines, result);
241
+ lines.push("");
227
242
  appendEpisodeMatches(lines, result);
228
243
  } else {
229
244
  appendEpisodeMatches(lines, result);
230
245
  lines.push("");
231
246
  appendEntryMatches(lines, result);
247
+ lines.push("");
248
+ appendClaimTransitions(lines, result);
232
249
  }
233
250
  if (result.notices.length > 0) {
234
251
  lines.push("");
@@ -241,15 +258,26 @@ function formatUnifiedRecallResults(result) {
241
258
  }
242
259
  function appendEntryMatches(lines, result) {
243
260
  lines.push("Entry Matches");
244
- if (result.entries.length === 0) {
261
+ if (result.projectedEntries.length === 0) {
245
262
  lines.push("None.");
246
263
  return;
247
264
  }
248
- for (const [index, entry] of result.entries.entries()) {
265
+ for (const [familyIndex, family] of result.entryFamilies.entries()) {
249
266
  lines.push(
250
- `${index + 1}. ${entry.entry.id} | ${entry.entry.type} | ${entry.entry.subject} | score ${entry.score.toFixed(2)} | importance ${entry.entry.importance}`
267
+ family.claimKey ? `Family ${familyIndex + 1}. claim_key=${family.claimKey} | slot_policy=${family.slotPolicy} | primary=${family.primary.entryId} | subject=${family.subject}` : `Standalone ${familyIndex + 1}. ${family.primary.entryId} | subject=${family.subject}`
251
268
  );
252
- lines.push(` ${truncate(entry.entry.content, 220)}`);
269
+ for (const [entryIndex, entry] of family.entries.entries()) {
270
+ lines.push(
271
+ ` ${entryIndex + 1}. ${entry.entryId} | ${entry.recall.entry.type} | ${entry.recall.entry.subject} | score ${entry.recall.score.toFixed(2)} | state=${entry.memoryState} | claim_status=${formatClaimStatus(entry.claimStatus)}`
272
+ );
273
+ lines.push(` ${truncate(entry.recall.entry.content, 220)}`);
274
+ lines.push(` freshness=${entry.freshness.label}`);
275
+ const provenance = formatProjectedEntryProvenance(entry);
276
+ if (provenance) {
277
+ lines.push(` provenance=${provenance}`);
278
+ }
279
+ lines.push(` why_surfaced=${entry.whySurfaced.summary}`);
280
+ }
253
281
  }
254
282
  }
255
283
  function appendEpisodeMatches(lines, result) {
@@ -266,6 +294,25 @@ function appendEpisodeMatches(lines, result) {
266
294
  lines.push(` why_matched=${describeEpisodeMatch(episode)}`);
267
295
  }
268
296
  }
297
+ function appendClaimTransitions(lines, result) {
298
+ lines.push("Claim Transitions");
299
+ if (result.claimTransitions.length === 0) {
300
+ lines.push("None.");
301
+ return;
302
+ }
303
+ for (const [index, transition] of result.claimTransitions.entries()) {
304
+ lines.push(
305
+ `${index + 1}. family=${transition.claimKey ?? transition.familyKey} | slot_policy=${transition.slotPolicy}${transition.currentEntryId ? ` | current=${transition.currentEntryId}` : ""}${transition.priorEntryId ? ` | prior=${transition.priorEntryId}` : ""}`
306
+ );
307
+ lines.push(` ${transition.summary}`);
308
+ if (transition.episodeContext) {
309
+ lines.push(
310
+ ` episode=${transition.episodeContext.episodeId} | ${transition.episodeContext.startedAt} -> ${transition.episodeContext.endedAt ?? transition.episodeContext.startedAt}`
311
+ );
312
+ lines.push(` ${truncate(transition.episodeContext.summary.trim(), 220)}`);
313
+ }
314
+ }
315
+ }
269
316
  function formatUnifiedRecallLogSummary(result) {
270
317
  const entrySubjects = result.entries.map((entry) => entry.entry.subject.trim()).filter((subject) => subject.length > 0);
271
318
  const displayed = entrySubjects.slice(0, RESULT_SUBJECT_LOG_LIMIT).map((subject) => JSON.stringify(truncate(subject, 80)));
@@ -273,7 +320,11 @@ function formatUnifiedRecallLogSummary(result) {
273
320
  const suffix = displayed.length === 0 ? "" : ` [entry subjects: ${displayed.join(", ")}${remaining > 0 ? `, ... and ${remaining} more` : ""}]`;
274
321
  return `${result.episodes.length} episode${result.episodes.length === 1 ? "" : "s"}, ${result.entries.length} entr${result.entries.length === 1 ? "y" : "ies"}${suffix}`;
275
322
  }
276
- function formatTrace(entry, supersededBy, supersedes, recallEvents) {
323
+ function formatTrace(entry, supersededBy, supersedes, claimFamily, recallEvents) {
324
+ const slotPolicy = entry.claim_key ? claimFamily ? {
325
+ policy: claimFamily.slotPolicy ?? resolveClaimSlotPolicy(claimFamily.claimKey).policy,
326
+ reason: claimFamily.slotPolicyReason ?? resolveClaimSlotPolicy(claimFamily.claimKey).reason
327
+ } : resolveClaimSlotPolicy(entry.claim_key) : void 0;
277
328
  const lines = [
278
329
  `Trace for ${entry.id} | ${entry.subject}`,
279
330
  `type=${entry.type} expiry=${entry.expiry} importance=${entry.importance} retired=${entry.retired}`,
@@ -287,6 +338,22 @@ function formatTrace(entry, supersededBy, supersedes, recallEvents) {
287
338
  }
288
339
  if (entry.claim_key) {
289
340
  lines.push(`claim_key=${entry.claim_key}`);
341
+ if (slotPolicy) {
342
+ lines.push(`slot_policy=${slotPolicy.policy}`);
343
+ lines.push(`slot_policy_reason=${slotPolicy.reason}`);
344
+ }
345
+ }
346
+ if (claimFamily && claimFamily.entries.length > 0) {
347
+ lines.push(
348
+ `claim_family=${claimFamily.claimKey} | slot_policy=${slotPolicy?.policy ?? "exclusive"} | ${claimFamily.entries.map((item) => `${item.id}:${describeTraceEntryState(item)}:${formatClaimLifecycleLabel(item)}`).join(", ")}`
349
+ );
350
+ if (slotPolicy) {
351
+ lines.push(`claim_family_policy_reason=${slotPolicy.reason}`);
352
+ }
353
+ const transitionSummary = summarizeTraceClaimFamilyTransition(claimFamily.entries);
354
+ if (transitionSummary) {
355
+ lines.push(`transition=${transitionSummary}`);
356
+ }
290
357
  }
291
358
  if (entry.valid_from || entry.valid_to) {
292
359
  lines.push(`validity=${entry.valid_from ?? "?"} -> ${entry.valid_to ?? "ongoing"}`);
@@ -350,6 +417,50 @@ function describeEpisodeMatch(result) {
350
417
  }
351
418
  return "Matched episodic recall ranking.";
352
419
  }
420
+ function formatClaimStatus(status) {
421
+ return status === "no_key" ? "no-key" : status;
422
+ }
423
+ function formatProjectedEntryProvenance(entry) {
424
+ const parts = [
425
+ entry.provenance.supersededById ? `superseded_by=${entry.provenance.supersededById}` : void 0,
426
+ entry.provenance.supersessionKind ? `kind=${entry.provenance.supersessionKind}` : void 0,
427
+ entry.provenance.supersessionReason ? `reason=${truncate(entry.provenance.supersessionReason, 120)}` : void 0,
428
+ entry.provenance.supportSourceKind ? `support=${entry.provenance.supportSourceKind}` : void 0,
429
+ entry.provenance.supportMode ? `support_mode=${entry.provenance.supportMode}` : void 0,
430
+ entry.provenance.supportObservedAt ? `observed=${entry.provenance.supportObservedAt}` : void 0,
431
+ entry.provenance.supportLocator ? `locator=${truncate(entry.provenance.supportLocator, 120)}` : void 0
432
+ ].filter((value) => value !== void 0);
433
+ return parts.join(" | ");
434
+ }
435
+ function describeTraceEntryState(entry) {
436
+ if (entry.superseded_by) {
437
+ return "superseded";
438
+ }
439
+ if (entry.retired || entry.valid_to) {
440
+ return "historical";
441
+ }
442
+ return "current";
443
+ }
444
+ function formatClaimLifecycleLabel(entry) {
445
+ if (!entry.claim_key) {
446
+ return "no-key";
447
+ }
448
+ return entry.claim_key_status ?? "legacy";
449
+ }
450
+ function summarizeTraceClaimFamilyTransition(entries) {
451
+ const current = entries.find((entry) => !entry.retired && !entry.superseded_by);
452
+ const prior = [...entries].reverse().find((entry) => entry.id !== current?.id && (entry.superseded_by !== void 0 || entry.retired || entry.valid_to !== void 0));
453
+ if (current && prior) {
454
+ return `${prior.id} -> ${current.id}`;
455
+ }
456
+ if (prior) {
457
+ return `${prior.id} is historical with no current sibling in the traced family`;
458
+ }
459
+ if (current) {
460
+ return `${current.id} is the only current sibling in the traced family`;
461
+ }
462
+ return void 0;
463
+ }
353
464
 
354
465
  // src/adapters/openclaw/tools/recall.ts
355
466
  var RECALL_TOOL_PARAMETERS = {
@@ -389,6 +500,10 @@ var RECALL_TOOL_PARAMETERS = {
389
500
  type: "array",
390
501
  items: { type: "string" },
391
502
  description: "Optional tags to filter by once you already know the relevant entity, system, or theme."
503
+ },
504
+ asOf: {
505
+ type: "string",
506
+ description: "Optional reference time for current-vs-prior resolution. Supports ISO timestamps and the same natural-language date phrases used elsewhere in recall."
392
507
  }
393
508
  },
394
509
  required: ["query"]
@@ -409,6 +524,7 @@ function createAgenrRecallTool(ctx, servicesPromise, logger) {
409
524
  const services = await servicesPromise;
410
525
  const types = parseEntryTypes(readStringArrayParam(params, "types"));
411
526
  const tags = normalizeStringArray(readStringArrayParam(params, "tags"));
527
+ const asOf = readStringParam2(params, "asOf");
412
528
  const request = {
413
529
  text: query,
414
530
  ...mode ? { mode } : {},
@@ -416,6 +532,7 @@ function createAgenrRecallTool(ctx, servicesPromise, logger) {
416
532
  ...threshold !== void 0 ? { threshold } : {},
417
533
  ...types.length > 0 ? { types } : {},
418
534
  ...tags.length > 0 ? { tags } : {},
535
+ ...asOf ? { asOf } : {},
419
536
  sessionKey: ctx.sessionKey
420
537
  };
421
538
  logToolCall(
@@ -427,7 +544,8 @@ function createAgenrRecallTool(ctx, servicesPromise, logger) {
427
544
  mode,
428
545
  limit,
429
546
  types,
430
- tags
547
+ tags,
548
+ ...asOf ? { asOf } : {}
431
549
  }),
432
550
  sanitizeRecallToolParams({
433
551
  query,
@@ -435,7 +553,8 @@ function createAgenrRecallTool(ctx, servicesPromise, logger) {
435
553
  limit,
436
554
  threshold,
437
555
  types,
438
- tags
556
+ tags,
557
+ ...asOf ? { asOf } : {}
439
558
  })
440
559
  );
441
560
  const result = await runUnifiedRecall(request, {
@@ -443,13 +562,17 @@ function createAgenrRecallTool(ctx, servicesPromise, logger) {
443
562
  recall: services.recall,
444
563
  embeddingAvailable: services.embeddingStatus.available,
445
564
  embeddingError: services.embeddingStatus.error,
565
+ claimSlotPolicyConfig: services.pluginConfig.memoryPolicy?.slotPolicies,
446
566
  debugLog: (message) => {
447
567
  logger.debug?.(message);
448
568
  },
449
569
  embedQuery: services.embeddingStatus.available ? async (text) => {
450
570
  const vectors = await services.embedding.embed([text]);
451
571
  return vectors[0] ?? [];
452
- } : void 0
572
+ } : void 0,
573
+ recallOptions: {
574
+ slotPolicyConfig: services.pluginConfig.memoryPolicy?.slotPolicies
575
+ }
453
576
  });
454
577
  logger.info(
455
578
  `[agenr] tool=agenr_recall session=${ctx.sessionId ?? "unknown"} key=${ctx.sessionKey ?? "unknown"} result: ${formatUnifiedRecallLogSummary(result)}`
@@ -463,6 +586,7 @@ function createAgenrRecallTool(ctx, servicesPromise, logger) {
463
586
  queried: result.routing.queried,
464
587
  reason: result.routing.reason
465
588
  },
589
+ ...result.asOf ? { asOf: result.asOf } : {},
466
590
  ...result.timeWindow ? { timeWindow: result.timeWindow } : {},
467
591
  episodes: result.episodes.map((episode) => ({
468
592
  id: episode.episode.id,
@@ -486,6 +610,30 @@ function createAgenrRecallTool(ctx, servicesPromise, logger) {
486
610
  tags: entry.entry.tags,
487
611
  content: entry.entry.content
488
612
  })),
613
+ projectedEntries: result.projectedEntries.map((entry) => ({
614
+ id: entry.entryId,
615
+ familyKey: entry.familyKey,
616
+ claimKey: entry.claimKey,
617
+ slotPolicy: entry.slotPolicy,
618
+ memoryState: entry.memoryState,
619
+ claimStatus: entry.claimStatus,
620
+ freshness: entry.freshness,
621
+ provenance: entry.provenance,
622
+ whySurfaced: entry.whySurfaced
623
+ })),
624
+ entryFamilies: result.entryFamilies.map((family) => ({
625
+ familyKey: family.familyKey,
626
+ claimKey: family.claimKey,
627
+ slotPolicy: family.slotPolicy,
628
+ subject: family.subject,
629
+ primaryEntryId: family.primary.entryId,
630
+ entries: family.entries.map((entry) => ({
631
+ id: entry.entryId,
632
+ memoryState: entry.memoryState,
633
+ claimStatus: entry.claimStatus
634
+ }))
635
+ })),
636
+ claimTransitions: result.claimTransitions,
489
637
  notices: result.notices
490
638
  });
491
639
  } catch (error) {
@@ -627,9 +775,10 @@ function createAgenrStoreTool(ctx, servicesPromise, logger) {
627
775
  const tags = normalizeStringArray(readStringArrayParam2(params, "tags"));
628
776
  const sourceContext = readStringParam4(params, "sourceContext");
629
777
  const supersedes = readStringParam4(params, "supersedes");
630
- const claimKey = readStringParam4(params, "claimKey");
778
+ const claimKey = readStringParam4(params, "claimKey", { trim: false });
631
779
  const validFrom = readStringParam4(params, "validFrom");
632
780
  const validTo = readStringParam4(params, "validTo");
781
+ const claimSupportObservedAt = (/* @__PURE__ */ new Date()).toISOString();
633
782
  logToolCall(
634
783
  logger,
635
784
  "agenr_store",
@@ -660,7 +809,11 @@ function createAgenrStoreTool(ctx, servicesPromise, logger) {
660
809
  ...expiry !== void 0 ? { expiry } : {},
661
810
  ...tags.length > 0 ? { tags } : {},
662
811
  ...supersedes ? { supersedes } : {},
663
- ...claimKey ? { claim_key: claimKey } : {},
812
+ ...claimKey ? {
813
+ claim_key: claimKey,
814
+ claim_key_raw: claimKey,
815
+ ...buildToolCallClaimSupport(ctx, "agenr_store", claimSupportObservedAt)
816
+ } : {},
664
817
  ...validFrom ? { valid_from: validFrom } : {},
665
818
  ...validTo ? { valid_to: validTo } : {},
666
819
  source_file: buildSessionSourceFile(ctx),
@@ -752,13 +905,14 @@ function createAgenrTraceTool(ctx, servicesPromise, logger) {
752
905
  entryId: entry.id
753
906
  });
754
907
  }
755
- return textResult4(formatTrace(trace.entry, trace.supersededBy, trace.supersedes, trace.recallEvents), {
908
+ return textResult4(formatTrace(trace.entry, trace.supersededBy, trace.supersedes, trace.claimFamily, trace.recallEvents), {
756
909
  status: "ok",
757
910
  sessionKey: ctx.sessionKey,
758
911
  trace: {
759
912
  entry: trace.entry,
760
913
  supersededBy: trace.supersededBy,
761
914
  supersedes: trace.supersedes,
915
+ claimFamily: trace.claimFamily,
762
916
  recallEvents: trace.recallEvents
763
917
  }
764
918
  });
@@ -819,34 +973,49 @@ function createAgenrUpdateTool(ctx, servicesPromise, logger) {
819
973
  const subject = readStringParam6(params, "subject");
820
974
  const importance = readNumberParam3(params, "importance", { integer: true, strict: true });
821
975
  const expiry = parseExpiry(readStringParam6(params, "expiry"));
822
- const claimKeyInput = readStringParam6(params, "claimKey");
976
+ const claimKeyInput = readStringParam6(params, "claimKey", { trim: false });
823
977
  const validFrom = readStringParam6(params, "validFrom");
824
978
  const validTo = readStringParam6(params, "validTo");
825
- const normalizedClaimKey = claimKeyInput === void 0 ? void 0 : (() => {
826
- const claimKey = normalizeClaimKey(claimKeyInput);
827
- if (!claimKey.ok) {
979
+ const claimSupportObservedAt = (/* @__PURE__ */ new Date()).toISOString();
980
+ const claimSupport = claimKeyInput === void 0 ? void 0 : buildToolCallClaimSupport(ctx, "agenr_update", claimSupportObservedAt);
981
+ const normalizedClaimKeyUpdate = claimKeyInput === void 0 ? void 0 : (() => {
982
+ try {
983
+ return normalizeManualClaimKeyUpdate({
984
+ claimKey: claimKeyInput,
985
+ rawClaimKey: claimKeyInput,
986
+ supportSourceKind: claimSupport?.claim_support_source_kind,
987
+ supportLocator: claimSupport?.claim_support_locator,
988
+ supportObservedAt: claimSupport?.claim_support_observed_at,
989
+ supportMode: claimSupport?.claim_support_mode
990
+ });
991
+ } catch {
828
992
  throw new Error("claimKey must use canonical entity/attribute format.");
829
993
  }
830
- return claimKey.value.claimKey;
831
994
  })();
832
995
  logToolCall(
833
996
  logger,
834
997
  "agenr_update",
835
998
  ctx,
836
999
  `target=${formatTargetSelector(id, subject)}${importance !== void 0 ? ` importance=${importance}` : ""}${expiry !== void 0 ? ` expiry=${expiry}` : ""}`,
837
- sanitizeUpdateToolParams({ id, subject, importance, expiry, claimKey: normalizedClaimKey, validFrom, validTo })
1000
+ sanitizeUpdateToolParams({ id, subject, importance, expiry, claimKey: normalizedClaimKeyUpdate?.claimKey, validFrom, validTo })
838
1001
  );
839
1002
  const services = await servicesPromise;
840
1003
  const entry = await resolveTargetEntry(services, params);
841
- if (importance === void 0 && expiry === void 0 && normalizedClaimKey === void 0 && validFrom === void 0 && validTo === void 0) {
1004
+ if (importance === void 0 && expiry === void 0 && normalizedClaimKeyUpdate === void 0 && validFrom === void 0 && validTo === void 0) {
842
1005
  throw new Error("Provide at least one update field: importance, expiry, claimKey, validFrom, or validTo.");
843
1006
  }
1007
+ const mergedValidity = validateTemporalValidityRange(validFrom ?? entry.valid_from, validTo ?? entry.valid_to);
1008
+ if (!mergedValidity.ok) {
1009
+ throw new Error(mergedValidity.message);
1010
+ }
1011
+ const normalizedValidFrom = validFrom !== void 0 ? mergedValidity.value.validFrom : void 0;
1012
+ const normalizedValidTo = validTo !== void 0 ? mergedValidity.value.validTo : void 0;
844
1013
  const updated = await services.entries.updateEntry(entry.id, {
845
1014
  ...importance !== void 0 ? { importance } : {},
846
1015
  ...expiry !== void 0 ? { expiry } : {},
847
- ...normalizedClaimKey !== void 0 ? { claim_key: normalizedClaimKey } : {},
848
- ...validFrom !== void 0 ? { valid_from: validFrom } : {},
849
- ...validTo !== void 0 ? { valid_to: validTo } : {}
1016
+ ...normalizedClaimKeyUpdate?.updateFields ?? {},
1017
+ ...validFrom !== void 0 ? { valid_from: normalizedValidFrom } : {},
1018
+ ...validTo !== void 0 ? { valid_to: normalizedValidTo } : {}
850
1019
  });
851
1020
  if (!updated) {
852
1021
  return failedTextResult5(`Entry ${entry.id} is not active, so it could not be updated.`, {
@@ -861,9 +1030,9 @@ function createAgenrUpdateTool(ctx, servicesPromise, logger) {
861
1030
  sessionKey: ctx.sessionKey,
862
1031
  ...importance !== void 0 ? { importance } : {},
863
1032
  ...expiry !== void 0 ? { expiry } : {},
864
- ...normalizedClaimKey !== void 0 ? { claimKey: normalizedClaimKey } : {},
865
- ...validFrom !== void 0 ? { validFrom } : {},
866
- ...validTo !== void 0 ? { validTo } : {}
1033
+ ...normalizedClaimKeyUpdate !== void 0 ? { claimKey: normalizedClaimKeyUpdate.claimKey } : {},
1034
+ ...validFrom !== void 0 ? { validFrom: normalizedValidFrom } : {},
1035
+ ...validTo !== void 0 ? { validTo: normalizedValidTo } : {}
867
1036
  });
868
1037
  } catch (error) {
869
1038
  logToolFailure(logger, "agenr_update", ctx, error);
@@ -886,17 +1055,11 @@ function registerAgenrOpenClawTools(api, servicesPromise, logger) {
886
1055
  var openclaw_plugin_default = {
887
1056
  id: "agenr",
888
1057
  name: "agenr",
889
- version: "1.7.2",
1058
+ version: "1.8.0",
890
1059
  description: "agenr memory plugin for OpenClaw",
891
1060
  kind: "memory",
892
1061
  contracts: {
893
- tools: [
894
- "agenr_store",
895
- "agenr_recall",
896
- "agenr_retire",
897
- "agenr_update",
898
- "agenr_trace"
899
- ]
1062
+ tools: ["agenr_store", "agenr_recall", "agenr_retire", "agenr_update", "agenr_trace"]
900
1063
  },
901
1064
  uiHints: {
902
1065
  dbPath: {
@@ -922,6 +1085,10 @@ var openclaw_plugin_default = {
922
1085
  storeNudge: {
923
1086
  label: "Store nudge",
924
1087
  help: "Optional mid-session reminder settings for prompting durable memory storage after several turns of memory silence."
1088
+ },
1089
+ memoryPolicy: {
1090
+ label: "Memory policy",
1091
+ help: "Optional runtime overrides for claim-aware read behavior such as slot-policy classes."
925
1092
  }
926
1093
  },
927
1094
  configSchema: {
@@ -971,6 +1138,31 @@ var openclaw_plugin_default = {
971
1138
  description: "Maximum nudges injected during one session lifetime. Defaults to 5."
972
1139
  }
973
1140
  }
1141
+ },
1142
+ memoryPolicy: {
1143
+ type: "object",
1144
+ additionalProperties: false,
1145
+ description: "Optional runtime overrides for claim-aware read behavior exposed by the OpenClaw adapter.",
1146
+ properties: {
1147
+ slotPolicies: {
1148
+ type: "object",
1149
+ additionalProperties: false,
1150
+ description: "Claim-slot policy overrides keyed by canonical claim-key attribute head.",
1151
+ properties: {
1152
+ attributeHeads: {
1153
+ type: "object",
1154
+ description: "Map canonical attribute heads such as `integration` or `preference` to `exclusive` or `multivalued` read-time slot behavior.",
1155
+ propertyNames: {
1156
+ pattern: "^[A-Za-z0-9][A-Za-z0-9_-]*$"
1157
+ },
1158
+ additionalProperties: {
1159
+ type: "string",
1160
+ enum: ["exclusive", "multivalued"]
1161
+ }
1162
+ }
1163
+ }
1164
+ }
1165
+ }
974
1166
  }
975
1167
  }
976
1168
  }
@@ -1023,7 +1215,11 @@ function normalizeAgenrOpenClawPluginConfig(value) {
1023
1215
  if (!storeNudgeResult.ok) {
1024
1216
  errors.push(...storeNudgeResult.errors);
1025
1217
  }
1026
- const allowedKeys = /* @__PURE__ */ new Set(["dbPath", "configPath", "continuityModel", "episodeModel", "claimExtractionModel", "storeNudge"]);
1218
+ const memoryPolicyResult = normalizeMemoryPolicyConfig(value.memoryPolicy);
1219
+ if (!memoryPolicyResult.ok) {
1220
+ errors.push(...memoryPolicyResult.errors);
1221
+ }
1222
+ const allowedKeys = /* @__PURE__ */ new Set(["dbPath", "configPath", "continuityModel", "episodeModel", "claimExtractionModel", "storeNudge", "memoryPolicy"]);
1027
1223
  for (const key of Object.keys(value)) {
1028
1224
  if (!allowedKeys.has(key)) {
1029
1225
  errors.push(`unknown config field: ${key}`);
@@ -1040,7 +1236,8 @@ function normalizeAgenrOpenClawPluginConfig(value) {
1040
1236
  ...continuityModel ? { continuityModel } : {},
1041
1237
  ...episodeModel ? { episodeModel } : {},
1042
1238
  ...claimExtractionModel ? { claimExtractionModel } : {},
1043
- ...storeNudgeResult.ok && storeNudgeResult.value ? { storeNudge: storeNudgeResult.value } : {}
1239
+ ...storeNudgeResult.ok && storeNudgeResult.value ? { storeNudge: storeNudgeResult.value } : {},
1240
+ ...memoryPolicyResult.ok && memoryPolicyResult.value ? { memoryPolicy: memoryPolicyResult.value } : {}
1044
1241
  }
1045
1242
  };
1046
1243
  }
@@ -1101,6 +1298,80 @@ function normalizeStoreNudgeConfig(value) {
1101
1298
  value: Object.keys(normalizedValue).length > 0 ? resolveStoreNudgeConfig(normalizedValue) : void 0
1102
1299
  };
1103
1300
  }
1301
+ function normalizeMemoryPolicyConfig(value) {
1302
+ if (value === void 0) {
1303
+ return { ok: true, value: void 0 };
1304
+ }
1305
+ if (!isRecord(value)) {
1306
+ return { ok: false, errors: ["memoryPolicy must be an object when provided"] };
1307
+ }
1308
+ const errors = [];
1309
+ const slotPoliciesResult = normalizeClaimSlotPolicyConfig(value.slotPolicies);
1310
+ if (!slotPoliciesResult.ok) {
1311
+ errors.push(...slotPoliciesResult.errors);
1312
+ }
1313
+ const allowedKeys = /* @__PURE__ */ new Set(["slotPolicies"]);
1314
+ for (const key of Object.keys(value)) {
1315
+ if (!allowedKeys.has(key)) {
1316
+ errors.push(`unknown config field: memoryPolicy.${key}`);
1317
+ }
1318
+ }
1319
+ if (errors.length > 0) {
1320
+ return { ok: false, errors };
1321
+ }
1322
+ return {
1323
+ ok: true,
1324
+ value: slotPoliciesResult.ok && slotPoliciesResult.value ? {
1325
+ slotPolicies: slotPoliciesResult.value
1326
+ } : void 0
1327
+ };
1328
+ }
1329
+ function normalizeClaimSlotPolicyConfig(value) {
1330
+ if (value === void 0) {
1331
+ return { ok: true, value: void 0 };
1332
+ }
1333
+ if (!isRecord(value)) {
1334
+ return { ok: false, errors: ["memoryPolicy.slotPolicies must be an object when provided"] };
1335
+ }
1336
+ const errors = [];
1337
+ const attributeHeads = normalizeClaimSlotPolicyAttributeHeads(value.attributeHeads, errors);
1338
+ const allowedKeys = /* @__PURE__ */ new Set(["attributeHeads"]);
1339
+ for (const key of Object.keys(value)) {
1340
+ if (!allowedKeys.has(key)) {
1341
+ errors.push(`unknown config field: memoryPolicy.slotPolicies.${key}`);
1342
+ }
1343
+ }
1344
+ if (errors.length > 0) {
1345
+ return { ok: false, errors };
1346
+ }
1347
+ return {
1348
+ ok: true,
1349
+ value: attributeHeads ? { attributeHeads } : void 0
1350
+ };
1351
+ }
1352
+ function normalizeClaimSlotPolicyAttributeHeads(value, errors) {
1353
+ if (value === void 0) {
1354
+ return void 0;
1355
+ }
1356
+ if (!isRecord(value)) {
1357
+ errors.push("memoryPolicy.slotPolicies.attributeHeads must be an object when provided");
1358
+ return void 0;
1359
+ }
1360
+ const normalized = {};
1361
+ for (const [rawKey, rawPolicy] of Object.entries(value)) {
1362
+ const attributeHead = rawKey.trim().toLowerCase();
1363
+ if (!/^[a-z0-9][a-z0-9_-]*$/.test(attributeHead)) {
1364
+ errors.push(`memoryPolicy.slotPolicies.attributeHeads.${rawKey} must use a canonical attribute-head label`);
1365
+ continue;
1366
+ }
1367
+ if (rawPolicy !== "exclusive" && rawPolicy !== "multivalued") {
1368
+ errors.push(`memoryPolicy.slotPolicies.attributeHeads.${attributeHead} must be "exclusive" or "multivalued"`);
1369
+ continue;
1370
+ }
1371
+ normalized[attributeHead] = rawPolicy;
1372
+ }
1373
+ return Object.keys(normalized).length > 0 ? normalized : void 0;
1374
+ }
1104
1375
  function normalizeOptionalBoolean(value, label, errors) {
1105
1376
  if (value === void 0) {
1106
1377
  return void 0;
@@ -2868,6 +3139,12 @@ function createAgenrMemoryRuntime(servicesPromise) {
2868
3139
  };
2869
3140
  return {
2870
3141
  manager: {
3142
+ async search() {
3143
+ return [];
3144
+ },
3145
+ async readFile({ relPath }) {
3146
+ throw new Error(`[agenr] memory file reads are not supported for "${relPath}"`);
3147
+ },
2871
3148
  status() {
2872
3149
  return status;
2873
3150
  },
@@ -2896,213 +3173,6 @@ function createAgenrMemoryRuntime(servicesPromise) {
2896
3173
  };
2897
3174
  }
2898
3175
 
2899
- // src/adapters/db/openclaw-repository.ts
2900
- var ZERO_VECTOR = JSON.stringify(Array.from({ length: EMBEDDING_DIMENSIONS }, () => 0));
2901
- var ENTRY_SELECT_COLUMNS = `
2902
- id,
2903
- type,
2904
- subject,
2905
- content,
2906
- importance,
2907
- expiry,
2908
- tags,
2909
- source_file,
2910
- source_context,
2911
- embedding,
2912
- content_hash,
2913
- norm_content_hash,
2914
- quality_score,
2915
- recall_count,
2916
- last_recalled_at,
2917
- superseded_by,
2918
- valid_from,
2919
- valid_to,
2920
- claim_key,
2921
- supersession_kind,
2922
- supersession_reason,
2923
- cluster_id,
2924
- user_id,
2925
- project,
2926
- retired,
2927
- retired_at,
2928
- retired_reason,
2929
- created_at,
2930
- updated_at
2931
- `;
2932
- function createOpenClawRepository(executor) {
2933
- return {
2934
- listCoreEntries: async (limit) => listCoreEntries(executor, limit),
2935
- findEntryBySubject: async (subject) => findEntryBySubject(executor, subject),
2936
- findMostRecentEntry: async () => findMostRecentEntry(executor),
2937
- getEntryTrace: async (entryId) => getEntryTrace(executor, entryId),
2938
- getMemoryStatusSnapshot: async () => getMemoryStatusSnapshot(executor),
2939
- probeVectorAvailability: async () => probeVectorAvailability(executor)
2940
- };
2941
- }
2942
- async function listCoreEntries(executor, limit) {
2943
- if (limit <= 0) {
2944
- return [];
2945
- }
2946
- const result = await executor.execute({
2947
- sql: `
2948
- SELECT
2949
- ${ENTRY_SELECT_COLUMNS}
2950
- FROM entries
2951
- WHERE ${buildActiveEntryClause()}
2952
- AND expiry = 'core'
2953
- ORDER BY importance DESC, created_at DESC
2954
- LIMIT ?
2955
- `,
2956
- args: [limit]
2957
- });
2958
- return result.rows.map((row) => mapEntryRow(row));
2959
- }
2960
- async function findEntryBySubject(executor, subject) {
2961
- const normalizedSubject = subject.trim();
2962
- if (normalizedSubject.length === 0) {
2963
- return null;
2964
- }
2965
- const result = await executor.execute({
2966
- sql: `
2967
- SELECT
2968
- ${ENTRY_SELECT_COLUMNS},
2969
- CASE
2970
- WHEN lower(subject) = lower(?) THEN 0
2971
- WHEN lower(subject) LIKE lower(?) THEN 1
2972
- ELSE 2
2973
- END AS match_rank
2974
- FROM entries
2975
- WHERE lower(subject) = lower(?)
2976
- OR lower(subject) LIKE lower(?)
2977
- ORDER BY match_rank ASC, created_at DESC
2978
- LIMIT 1
2979
- `,
2980
- args: [normalizedSubject, `%${normalizedSubject}%`, normalizedSubject, `%${normalizedSubject}%`]
2981
- });
2982
- const row = result.rows[0];
2983
- return row ? mapEntryRow(row) : null;
2984
- }
2985
- async function findMostRecentEntry(executor) {
2986
- const result = await executor.execute({
2987
- sql: `
2988
- SELECT
2989
- ${ENTRY_SELECT_COLUMNS}
2990
- FROM entries
2991
- ORDER BY created_at DESC
2992
- LIMIT 1
2993
- `
2994
- });
2995
- const row = result.rows[0];
2996
- return row ? mapEntryRow(row) : null;
2997
- }
2998
- async function getEntryTrace(executor, entryId) {
2999
- const entry = await getEntryByIdIncludingInactive(executor, entryId);
3000
- if (!entry) {
3001
- return null;
3002
- }
3003
- const [supersededBy, supersedes, recallEvents] = await Promise.all([
3004
- entry.superseded_by ? getEntryByIdIncludingInactive(executor, entry.superseded_by) : Promise.resolve(null),
3005
- listSupersededEntries(executor, entry.id),
3006
- listRecallEvents(executor, entry.id)
3007
- ]);
3008
- return {
3009
- entry,
3010
- ...supersededBy ? { supersededBy } : {},
3011
- supersedes,
3012
- recallEvents
3013
- };
3014
- }
3015
- async function getMemoryStatusSnapshot(executor) {
3016
- const result = await executor.execute({
3017
- sql: `
3018
- SELECT
3019
- COUNT(*) AS active_entries,
3020
- SUM(CASE WHEN expiry = 'core' THEN 1 ELSE 0 END) AS core_entries,
3021
- COUNT(DISTINCT source_file) AS source_files
3022
- FROM entries
3023
- WHERE ${buildActiveEntryClause()}
3024
- `
3025
- });
3026
- const row = result.rows[0];
3027
- if (!row) {
3028
- return {
3029
- activeEntries: 0,
3030
- coreEntries: 0,
3031
- sourceFiles: 0
3032
- };
3033
- }
3034
- return {
3035
- activeEntries: readNumber(row, "active_entries", 0),
3036
- coreEntries: readNumber(row, "core_entries", 0),
3037
- sourceFiles: readNumber(row, "source_files", 0)
3038
- };
3039
- }
3040
- async function probeVectorAvailability(executor) {
3041
- try {
3042
- await executor.execute({
3043
- sql: `
3044
- SELECT COUNT(*) AS matches
3045
- FROM vector_top_k('${VECTOR_INDEX_NAME}', vector32(?), ?) AS matches
3046
- `,
3047
- args: [ZERO_VECTOR, 1]
3048
- });
3049
- return true;
3050
- } catch {
3051
- return false;
3052
- }
3053
- }
3054
- async function getEntryByIdIncludingInactive(executor, entryId) {
3055
- const normalizedId = entryId.trim();
3056
- if (normalizedId.length === 0) {
3057
- return null;
3058
- }
3059
- const result = await executor.execute({
3060
- sql: `
3061
- SELECT
3062
- ${ENTRY_SELECT_COLUMNS}
3063
- FROM entries
3064
- WHERE id = ?
3065
- LIMIT 1
3066
- `,
3067
- args: [normalizedId]
3068
- });
3069
- const row = result.rows[0];
3070
- return row ? mapEntryRow(row) : null;
3071
- }
3072
- async function listSupersededEntries(executor, entryId) {
3073
- const result = await executor.execute({
3074
- sql: `
3075
- SELECT
3076
- ${ENTRY_SELECT_COLUMNS}
3077
- FROM entries
3078
- WHERE superseded_by = ?
3079
- ORDER BY created_at DESC
3080
- `,
3081
- args: [entryId]
3082
- });
3083
- return result.rows.map((row) => mapEntryRow(row));
3084
- }
3085
- async function listRecallEvents(executor, entryId) {
3086
- const result = await executor.execute({
3087
- sql: `
3088
- SELECT
3089
- query,
3090
- session_key,
3091
- recalled_at
3092
- FROM recall_events
3093
- WHERE entry_id = ?
3094
- ORDER BY recalled_at DESC
3095
- LIMIT 10
3096
- `,
3097
- args: [entryId]
3098
- });
3099
- return result.rows.map((row) => ({
3100
- query: readOptionalString(row, "query"),
3101
- sessionKey: readOptionalString(row, "session_key"),
3102
- recalledAt: readRequiredString(row, "recalled_at")
3103
- }));
3104
- }
3105
-
3106
3176
  // src/app/openclaw/runtime.ts
3107
3177
  async function createAgenrOpenClawServices(config, options) {
3108
3178
  const { resolvedConfig, agenrConfig: loadedAgenrConfig } = resolveRuntimeConfig(config, options.resolvePath);
@@ -3159,7 +3229,9 @@ async function createRuntimeServices(dbPath, config, embeddingStatus, openClawCo
3159
3229
  return {
3160
3230
  entries: database,
3161
3231
  episodes: database,
3162
- memory: createOpenClawRepository(database),
3232
+ memory: createOpenClawRepository(database, {
3233
+ claimSlotPolicyConfig: openClawContext.pluginConfig.memoryPolicy?.slotPolicies
3234
+ }),
3163
3235
  embedding,
3164
3236
  recall: createRecallAdapter(database, embedding),
3165
3237
  claimExtraction,
@@ -3244,7 +3316,6 @@ var openclaw_default = definePluginEntry({
3244
3316
  kind: "memory",
3245
3317
  configSchema: createAgenrOpenClawPluginConfigSchema(),
3246
3318
  register(api) {
3247
- const memoryApi = api;
3248
3319
  const sessionStartTracker = createSessionStartTracker();
3249
3320
  const midSessionTracker = createMidSessionTracker();
3250
3321
  const pluginConfig = coerceAgenrOpenClawPluginConfig(api.pluginConfig);
@@ -3260,9 +3331,11 @@ var openclaw_default = definePluginEntry({
3260
3331
  },
3261
3332
  resolvePath: api.resolvePath
3262
3333
  });
3263
- api.registerMemoryPromptSection(buildAgenrMemoryPromptSection);
3264
- memoryApi.registerMemoryFlushPlan?.((params) => buildAgenrMemoryFlushPlan(params, api.logger));
3265
- memoryApi.registerMemoryRuntime?.(createAgenrMemoryRuntime(servicesPromise));
3334
+ api.registerMemoryCapability({
3335
+ promptBuilder: buildAgenrMemoryPromptSection,
3336
+ flushPlanResolver: (params) => buildAgenrMemoryFlushPlan(params, api.logger),
3337
+ runtime: createAgenrMemoryRuntime(servicesPromise)
3338
+ });
3266
3339
  registerAgenrOpenClawTools(api, servicesPromise, api.logger);
3267
3340
  api.on(
3268
3341
  "before_prompt_build",