agenr 0.9.71 → 0.9.72

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
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.9.72] - 2026-03-06
4
+
5
+ ### Fixed
6
+
7
+ - Online dedup no longer allows a higher-tier incoming entry (`core > permanent > temporary`) to end in `SKIP` against a lower-tier existing row. Higher-tier `SKIP` outcomes are now overridden to a safe `UPDATE` that preserves existing content while promoting the stored expiry.
8
+ - The near-exact semantic duplicate fast path now blocks `SKIP` for higher-tier incoming entries and only auto-promotes when the subjects still align; otherwise it falls through to a non-skip path instead of swallowing the stronger lifecycle.
9
+ - Added regression coverage for higher-tier `SKIP` overrides in both the store pipeline and the CLI `store` path, while preserving allowed equal-tier and lower-tier `SKIP` behavior.
10
+ - Extended `verify:dist` to fail release builds unless the bundled runtime contains the higher-tier `SKIP` guard and reproduces the live failure shape as an `UPDATE` in the built artifact.
11
+
3
12
  ## [0.9.71] - 2026-03-06
4
13
 
5
14
  ### Fixed
@@ -6693,6 +6693,31 @@ var TODO_COMPLETION_NEGATION_PATTERN = /\b(?:not|never)\b(?:\W+\w+){0,2}\W*$|\bn
6693
6693
  var TODO_COMPLETION_SIGNAL_PATTERNS = TODO_COMPLETION_SIGNALS.map(
6694
6694
  (signal) => new RegExp(`\\b${signal.split(/\s+/).map(escapeRegExp).join("\\s+")}\\b`, "gi")
6695
6695
  );
6696
+ function incomingExpiryOutranksExisting(existingExpiry, incomingExpiry) {
6697
+ return incomingExpiry !== existingExpiry && resolveHigherExpiry(existingExpiry, incomingExpiry) === incomingExpiry;
6698
+ }
6699
+ function buildExpiryPromotionUpdate(params) {
6700
+ return {
6701
+ decision: {
6702
+ entry: params.entry,
6703
+ action: "updated",
6704
+ reason: params.reason,
6705
+ similarity: params.candidate.similarity,
6706
+ matchedEntryId: params.candidate.entry.id,
6707
+ matchedEntry: params.candidate.entry,
6708
+ sameSubject: resolveSameSubject(params.entry.subject, params.candidate.entry.subject),
6709
+ llm_action: params.llmDecision?.action,
6710
+ llm_reasoning: params.llmDecision?.reasoning
6711
+ },
6712
+ mutation: {
6713
+ kind: "update",
6714
+ matchedEntry: params.candidate.entry,
6715
+ similarity: params.candidate.similarity,
6716
+ mergedContent: params.candidate.entry.content,
6717
+ llmDecision: params.llmDecision
6718
+ }
6719
+ };
6720
+ }
6696
6721
  function stripPossessives(s) {
6697
6722
  return s.replace(/['\u2018\u2019]s\b/g, "").replace(/['\u2018\u2019](?=\s|$)/g, "");
6698
6723
  }
@@ -6766,19 +6791,30 @@ async function planEntryAction(db, entry, embedding, contentHash, options) {
6766
6791
  const similarity = topMatch?.similarity ?? 0;
6767
6792
  const sameSubject = topMatch ? resolveSameSubject(entry.subject, topMatch.entry.subject) : false;
6768
6793
  const sameType = topMatch ? entry.type === topMatch.entry.type : false;
6794
+ const topMatchNeedsExpiryPromotion = topMatch !== void 0 && incomingExpiryOutranksExisting(topMatch.entry.expiry, entry.expiry);
6769
6795
  if (topMatch && similarity >= AUTO_SKIP_THRESHOLD && sameType) {
6770
- return {
6771
- decision: {
6772
- entry,
6773
- action: "skipped",
6774
- reason: "near-exact semantic duplicate",
6775
- similarity,
6776
- matchedEntryId: topMatch.entry.id,
6777
- matchedEntry: topMatch.entry,
6778
- sameSubject
6779
- },
6780
- mutation: { kind: "none" }
6781
- };
6796
+ if (topMatchNeedsExpiryPromotion) {
6797
+ if (sameSubject) {
6798
+ return buildExpiryPromotionUpdate({
6799
+ entry,
6800
+ candidate: topMatch,
6801
+ reason: "near-exact duplicate promoted existing entry expiry"
6802
+ });
6803
+ }
6804
+ } else {
6805
+ return {
6806
+ decision: {
6807
+ entry,
6808
+ action: "skipped",
6809
+ reason: "near-exact semantic duplicate",
6810
+ similarity,
6811
+ matchedEntryId: topMatch.entry.id,
6812
+ matchedEntry: topMatch.entry,
6813
+ sameSubject
6814
+ },
6815
+ mutation: { kind: "none" }
6816
+ };
6817
+ }
6782
6818
  }
6783
6819
  if (topMatch && similarity >= SMART_DEDUP_THRESHOLD && sameSubject && sameType) {
6784
6820
  return {
@@ -6953,6 +6989,23 @@ async function planEntryAction(db, entry, embedding, contentHash, options) {
6953
6989
  };
6954
6990
  }
6955
6991
  if (llmDecision.action === "SKIP") {
6992
+ if (incomingExpiryOutranksExisting(candidate.entry.expiry, entry.expiry)) {
6993
+ const overrideReasoning = [
6994
+ llmDecision.reasoning,
6995
+ `SKIP is disallowed because incoming expiry ${entry.expiry} outranks existing ${candidate.entry.expiry}; preserving existing content and promoting expiry via UPDATE.`
6996
+ ].filter((part) => part.trim().length > 0).join(" ");
6997
+ return buildExpiryPromotionUpdate({
6998
+ entry,
6999
+ candidate,
7000
+ reason: "online dedup SKIP overridden to UPDATE for higher-tier incoming entry",
7001
+ llmDecision: {
7002
+ action: "UPDATE",
7003
+ target_id: candidate.entry.id,
7004
+ merged_content: candidate.entry.content,
7005
+ reasoning: overrideReasoning
7006
+ }
7007
+ });
7008
+ }
6956
7009
  return {
6957
7010
  decision: {
6958
7011
  entry,
package/dist/cli-main.js CHANGED
@@ -71,7 +71,7 @@ import {
71
71
  toRecord,
72
72
  updateRecallMetadata,
73
73
  warnIfLocked
74
- } from "./chunk-WTABQS3R.js";
74
+ } from "./chunk-5XOV72MD.js";
75
75
  import {
76
76
  APP_VERSION,
77
77
  DEFAULT_DB_PATH,
@@ -18142,12 +18142,12 @@ function registerMaintainCommand(program) {
18142
18142
  "--only <tasks>",
18143
18143
  "Comma-separated task names: quality,edge-decay,clusters,conflicts,consolidation,retirement,reflection"
18144
18144
  ).option("--prune-edges", "When edge decay runs, delete edges below the configured decay floor").option("--verbose", "Show detailed per-task output").action(async (opts) => {
18145
- const { runMaintainCommand } = await import("./maintain-L5UT5TA4.js");
18145
+ const { runMaintainCommand } = await import("./maintain-73ULONFM.js");
18146
18146
  const result = await runMaintainCommand(opts);
18147
18147
  process.exitCode = result.exitCode;
18148
18148
  });
18149
18149
  maintainCommand.command("history").description("Show past maintenance runs").option("--db <path>", "Database path override").option("--limit <n>", "Max runs to show (default: 10)", parseIntOption).option("--json", "Output as JSON").action(async (opts) => {
18150
- const { runMaintainHistoryCommand } = await import("./maintain-L5UT5TA4.js");
18150
+ const { runMaintainHistoryCommand } = await import("./maintain-73ULONFM.js");
18151
18151
  const result = await runMaintainHistoryCommand(opts);
18152
18152
  process.exitCode = result.exitCode;
18153
18153
  });
@@ -27,7 +27,7 @@ import {
27
27
  resolveModelForLlmClient,
28
28
  runConsolidationOrchestrator,
29
29
  toRecord
30
- } from "./chunk-WTABQS3R.js";
30
+ } from "./chunk-5XOV72MD.js";
31
31
  import {
32
32
  DEFAULT_DB_PATH,
33
33
  DEFAULT_TASK_MODEL,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agenr",
3
- "version": "0.9.71",
3
+ "version": "0.9.72",
4
4
  "openclaw": {
5
5
  "extensions": [
6
6
  "dist/openclaw-plugin/index.js"