@remnic/plugin-openclaw 1.0.34 → 1.0.36

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.
Files changed (87) hide show
  1. package/README.md +59 -10
  2. package/dist/{calibration-JD4AU7FB.js → calibration-RKL2LRW4.js} +4 -4
  3. package/dist/{capsule-cli-GBM3WPAM.js → capsule-cli-EHZPMXBC.js} +2 -2
  4. package/dist/{capsule-crypto-K3IRTKRH.js → capsule-crypto-JS67OSWM.js} +3 -3
  5. package/dist/capsule-export-YPDWRB3C.js +17 -0
  6. package/dist/capsule-import-SWPOFG6F.js +16 -0
  7. package/dist/{capsule-merge-IWOQ34KL.js → capsule-merge-YXAF7ZJW.js} +7 -7
  8. package/dist/{causal-chain-WYN5QOPS.js → causal-chain-BVTOWZKC.js} +4 -4
  9. package/dist/{causal-consolidation-DSLFN64P.js → causal-consolidation-DRPM2KOE.js} +13 -10
  10. package/dist/{causal-retrieval-NZHQOZOE.js → causal-retrieval-XAP6QKHZ.js} +4 -5
  11. package/dist/{causal-trajectory-graph-VBPE2WPM.js → causal-trajectory-graph-ZWQWZ7N5.js} +2 -2
  12. package/dist/{chunk-5LE4HTVL.js → chunk-25J4PXDH.js} +0 -18
  13. package/dist/{chunk-FGTYFLL5.js → chunk-3IHGISUN.js} +29 -32
  14. package/dist/{chunk-6UFI73TJ.js → chunk-3IKMUNW5.js} +53 -46
  15. package/dist/{chunk-EXDYWXMB.js → chunk-4XDQ3KEC.js} +1 -2
  16. package/dist/{chunk-4UA6KMRO.js → chunk-6O3H3DPL.js} +2 -2
  17. package/dist/{chunk-7NMHI4IC.js → chunk-BLC3RQNV.js} +5 -555
  18. package/dist/{chunk-4G2XCSD2.js → chunk-BZ4EYURA.js} +0 -5
  19. package/dist/{chunk-4LYQ4ONL.js → chunk-E4RM7637.js} +1 -1
  20. package/dist/{chunk-TDRJVMUP.js → chunk-EH4AXGRO.js} +0 -12
  21. package/dist/{chunk-ZXLYEVOP.js → chunk-G3CZA4SD.js} +60 -362
  22. package/dist/chunk-I2KLQ2HA.js +22 -0
  23. package/dist/chunk-IO5WWY6A.js +156 -0
  24. package/dist/{contradiction-scan-U3QKHWQN.js → chunk-JC3FCKYL.js} +191 -87
  25. package/dist/{chunk-SVSQAG6M.js → chunk-KC7KSQR4.js} +47 -28
  26. package/dist/chunk-LZCGPRHS.js +228 -0
  27. package/dist/{chunk-CXM7EBAO.js → chunk-MXFJXUHC.js} +1 -1
  28. package/dist/{chunk-L6I4MQKO.js → chunk-NNAN63QK.js} +6 -6
  29. package/dist/{chunk-VRGUUHBV.js → chunk-NUWDSTP7.js} +1 -1
  30. package/dist/{chunk-6OJAU466.js → chunk-QMUQV5NP.js} +0 -1
  31. package/dist/{chunk-LLUROTZJ.js → chunk-QQXJODFL.js} +9 -9
  32. package/dist/{chunk-6F6EKSVP.js → chunk-QXXEF7VI.js} +1 -1
  33. package/dist/{chunk-NDZNURDM.js → chunk-SEGEX7W4.js} +73 -241
  34. package/dist/{chunk-7NUFIRM3.js → chunk-SWOYEQN2.js} +97 -21
  35. package/dist/chunk-TH5FF5SC.js +16 -0
  36. package/dist/chunk-UZJ7EERS.js +272 -0
  37. package/dist/chunk-YJYZMLD5.js +360 -0
  38. package/dist/{chunk-NKVIN6RD.js → chunk-YKV4EFUI.js} +84 -2
  39. package/dist/{chunk-SSFTU6LP.js → chunk-ZS6VABML.js} +4 -4
  40. package/dist/{cipher-VHAFCG7Z.js → cipher-E23BHBSO.js} +1 -1
  41. package/dist/{consolidation-undo-5ZSX4MWO.js → consolidation-undo-FKJZCJHS.js} +2 -2
  42. package/dist/contradiction-review-WJRWNQ5N.js +29 -0
  43. package/dist/contradiction-scan-5X423QGT.js +12 -0
  44. package/dist/{dreams-ledger-3I52ISYR.js → dreams-ledger-KDX44I7R.js} +1 -1
  45. package/dist/{engine-57HLTQBN.js → engine-5P774HTZ.js} +6 -6
  46. package/dist/{extraction-judge-telemetry-GHOTVYMP.js → extraction-judge-telemetry-O4ZVGLTU.js} +1 -1
  47. package/dist/{fallback-llm-33SPYXQY.js → fallback-llm-43UMEXNJ.js} +3 -3
  48. package/dist/{first-start-migration-I24M2JEE.js → first-start-migration-H2SAXAGR.js} +4 -4
  49. package/dist/{forget-NI4RBDPB.js → forget-ZECIDNL5.js} +1 -1
  50. package/dist/{fs-utils-PZRI2HDZ.js → fs-utils-OYXSZSVV.js} +12 -2
  51. package/dist/{graph-edge-decay-5CVKWBYH.js → graph-edge-decay-24ZKD5QL.js} +5 -5
  52. package/dist/index.js +7187 -71983
  53. package/dist/{kdf-H5B23ZM2.js → kdf-RXKIWHRU.js} +1 -1
  54. package/dist/legacy-hook-compat-QHHKF4GK.js +2 -0
  55. package/dist/{logger-TNOKCH7X.js → logger-XG7JKLPS.js} +1 -1
  56. package/dist/{memory-governance-FEQCA35V.js → memory-governance-6K4M4YXD.js} +5 -5
  57. package/dist/{metadata-JAGIWHEA.js → metadata-WK2TRPYZ.js} +1 -1
  58. package/dist/{migrate-from-identity-anchor-7MMSPEUM.js → migrate-from-identity-anchor-SNDNKHZD.js} +1 -1
  59. package/dist/path-ZKO74XXC.js +7 -0
  60. package/dist/{peers-KRFXWRQ6.js → peers-W53WSDXG.js} +1 -1
  61. package/dist/{purge-XN2VSPZ2.js → purge-IKJISXEQ.js} +1 -1
  62. package/dist/resolution-BN35OXDS.js +11 -0
  63. package/dist/{secure-store-A4NGCNXV.js → secure-store-F75I54O5.js} +3 -3
  64. package/dist/{state-PVISYXRH.js → state-4ITLYMAU.js} +1 -1
  65. package/dist/{state-store-N6TFBFSP.js → state-store-ET3ADVY5.js} +3 -3
  66. package/dist/{storage-R3V6ZFQT.js → storage-5EY6T7ON.js} +3 -3
  67. package/dist/{tier-stats-IZNW66NC.js → tier-stats-ZRQBV6G2.js} +4 -4
  68. package/dist/{trace-NJESSGH7.js → trace-IL2Y34EH.js} +1 -1
  69. package/dist/{tui-MGK2LYJY.js → tui-7KRDCMYK.js} +1 -1
  70. package/dist/{types-R4DO7AKM.js → types-7L34HYDW.js} +3 -3
  71. package/openclaw.plugin.json +153 -20
  72. package/package.json +18 -9
  73. package/scripts/faiss_index.py +756 -0
  74. package/scripts/faiss_requirements.txt +3 -0
  75. package/dist/capsule-export-IXVERCQG.js +0 -17
  76. package/dist/capsule-import-IA6VIOPQ.js +0 -16
  77. package/dist/chunk-3GUF7RQI.js +0 -559
  78. package/dist/chunk-7OQEPGQF.js +0 -533
  79. package/dist/chunk-DIZW6H5J.js +0 -136
  80. package/dist/chunk-FQRSVYY4.js +0 -110
  81. package/dist/chunk-GUSMRW4H.js +0 -12
  82. package/dist/chunk-MLKGABMK.js +0 -9
  83. package/dist/chunk-WPINX4MF.js +0 -380
  84. package/dist/contradiction-review-SVGBS3V5.js +0 -21
  85. package/dist/legacy-hook-compat-XQ7FP6FV.js +0 -35
  86. package/dist/path-JIEGNWFL.js +0 -7
  87. package/dist/resolution-YITUVUTH.js +0 -100
@@ -1,29 +1,42 @@
1
1
  import {
2
- countRecallTokenOverlap,
3
- normalizeRecallTokens
4
- } from "./chunk-WPINX4MF.js";
2
+ isSafeRouteNamespace
3
+ } from "./chunk-TH5FF5SC.js";
5
4
  import {
6
- StorageManager,
7
- isSemanticConsolidationLlmOperator
8
- } from "./chunk-ZXLYEVOP.js";
5
+ StorageManager
6
+ } from "./chunk-G3CZA4SD.js";
9
7
  import {
10
8
  log
11
9
  } from "./chunk-UFU5GGGA.js";
12
10
  import {
13
11
  readEnvVar,
14
12
  resolveHomeDir
15
- } from "./chunk-TDRJVMUP.js";
13
+ } from "./chunk-EH4AXGRO.js";
16
14
 
17
15
  // ../remnic-core/src/connectors/codex-materialize-runner.ts
18
- import path2 from "path";
19
16
  import { existsSync } from "fs";
20
17
 
18
+ // ../remnic-core/src/namespaces/path.ts
19
+ import path from "path";
20
+ function assertNamespacePathInsideRoot(root, candidate, namespace, label = "namespace path") {
21
+ const relative = path.relative(root, candidate);
22
+ if (relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative)) {
23
+ return;
24
+ }
25
+ throw new Error(`invalid ${label}: ${namespace}`);
26
+ }
27
+ function resolveNamespaceChildRoot(memoryDir, namespace, label = "namespace path") {
28
+ const namespaceRoot = path.resolve(memoryDir, "namespaces");
29
+ const candidate = path.resolve(namespaceRoot, namespace);
30
+ assertNamespacePathInsideRoot(namespaceRoot, candidate, namespace, label);
31
+ return candidate;
32
+ }
33
+
21
34
  // ../remnic-core/src/connectors/codex-materialize.ts
22
35
  import {
23
36
  createHash
24
37
  } from "crypto";
25
38
  import fs from "fs";
26
- import path from "path";
39
+ import path2 from "path";
27
40
  var MATERIALIZE_VERSION = 1;
28
41
  var SENTINEL_FILE = ".remnic-managed";
29
42
  var TMP_DIR = ".remnic-tmp";
@@ -34,12 +47,11 @@ function materializeForNamespace(namespace, options) {
34
47
  warn: (msg) => log.warn(`[codex-materialize] ${msg}`),
35
48
  debug: (msg) => log.debug(`[codex-materialize] ${msg}`)
36
49
  };
37
- const codexHome = resolveCodexHome(options.codexHome);
38
- const memoriesDir = path.join(codexHome, "memories");
50
+ const memoriesDir = resolveCodexMemoriesDir(options.codexHome);
39
51
  const now = options.now ?? /* @__PURE__ */ new Date();
40
52
  const maxSummaryTokens = typeof options.maxSummaryTokens === "number" && options.maxSummaryTokens >= 0 ? options.maxSummaryTokens : 4500;
41
53
  const rolloutRetentionDays = typeof options.rolloutRetentionDays === "number" && options.rolloutRetentionDays >= 0 ? options.rolloutRetentionDays : 30;
42
- const sentinelPath = path.join(memoriesDir, SENTINEL_FILE);
54
+ const sentinelPath = path2.join(memoriesDir, SENTINEL_FILE);
43
55
  const existingSentinel = readSentinel(sentinelPath);
44
56
  if (!existingSentinel) {
45
57
  if (fs.existsSync(memoriesDir)) {
@@ -117,10 +129,10 @@ function materializeForNamespace(namespace, options) {
117
129
  });
118
130
  if (existingSentinel.content_hash === hash) {
119
131
  const requiredFiles = [
120
- path.join(memoriesDir, "memory_summary.md"),
121
- path.join(memoriesDir, "MEMORY.md"),
122
- path.join(memoriesDir, "raw_memories.md"),
123
- ...rolloutFiles.map((r) => path.join(memoriesDir, ROLLOUT_SUBDIR, r.name))
132
+ path2.join(memoriesDir, "memory_summary.md"),
133
+ path2.join(memoriesDir, "MEMORY.md"),
134
+ path2.join(memoriesDir, "raw_memories.md"),
135
+ ...rolloutFiles.map((r) => path2.join(memoriesDir, ROLLOUT_SUBDIR, r.name))
124
136
  ];
125
137
  const allPresent = requiredFiles.every((f) => fs.existsSync(f));
126
138
  if (allPresent) {
@@ -140,14 +152,14 @@ function materializeForNamespace(namespace, options) {
140
152
  );
141
153
  }
142
154
  const runTag = `${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
143
- const tmpDir = path.join(memoriesDir, `${TMP_DIR}-${runTag}`);
155
+ const tmpDir = path2.join(memoriesDir, `${TMP_DIR}-${runTag}`);
144
156
  const TMP_STALE_MS = 60 * 60 * 1e3;
145
157
  const wallClockMs = Date.now();
146
158
  try {
147
159
  for (const entry of fs.readdirSync(memoriesDir, { withFileTypes: true })) {
148
160
  if (!entry.isDirectory()) continue;
149
161
  if (!entry.name.startsWith(TMP_DIR)) continue;
150
- const stalePath = path.join(memoriesDir, entry.name);
162
+ const stalePath = path2.join(memoriesDir, entry.name);
151
163
  try {
152
164
  const stat = fs.statSync(stalePath);
153
165
  if (wallClockMs - stat.mtimeMs < TMP_STALE_MS) continue;
@@ -158,24 +170,24 @@ function materializeForNamespace(namespace, options) {
158
170
  } catch {
159
171
  }
160
172
  fs.mkdirSync(tmpDir, { recursive: true });
161
- fs.mkdirSync(path.join(tmpDir, ROLLOUT_SUBDIR), { recursive: true });
173
+ fs.mkdirSync(path2.join(tmpDir, ROLLOUT_SUBDIR), { recursive: true });
162
174
  const filesWritten = [];
163
- fs.writeFileSync(path.join(tmpDir, "memory_summary.md"), memorySummary);
175
+ fs.writeFileSync(path2.join(tmpDir, "memory_summary.md"), memorySummary);
164
176
  filesWritten.push("memory_summary.md");
165
- fs.writeFileSync(path.join(tmpDir, "MEMORY.md"), memoryMd);
177
+ fs.writeFileSync(path2.join(tmpDir, "MEMORY.md"), memoryMd);
166
178
  filesWritten.push("MEMORY.md");
167
- fs.writeFileSync(path.join(tmpDir, "raw_memories.md"), rawMemories);
179
+ fs.writeFileSync(path2.join(tmpDir, "raw_memories.md"), rawMemories);
168
180
  filesWritten.push("raw_memories.md");
169
181
  for (const rollout of rolloutFiles) {
170
- fs.writeFileSync(path.join(tmpDir, ROLLOUT_SUBDIR, rollout.name), rollout.body);
171
- filesWritten.push(path.join(ROLLOUT_SUBDIR, rollout.name));
182
+ fs.writeFileSync(path2.join(tmpDir, ROLLOUT_SUBDIR, rollout.name), rollout.body);
183
+ filesWritten.push(path2.join(ROLLOUT_SUBDIR, rollout.name));
172
184
  }
173
185
  for (const rel of ["memory_summary.md", "MEMORY.md", "raw_memories.md"]) {
174
- const src = path.join(tmpDir, rel);
175
- const dest = path.join(memoriesDir, rel);
186
+ const src = path2.join(tmpDir, rel);
187
+ const dest = path2.join(memoriesDir, rel);
176
188
  fs.renameSync(src, dest);
177
189
  }
178
- const destRolloutsDir = path.join(memoriesDir, ROLLOUT_SUBDIR);
190
+ const destRolloutsDir = path2.join(memoriesDir, ROLLOUT_SUBDIR);
179
191
  fs.mkdirSync(destRolloutsDir, { recursive: true });
180
192
  if (rolloutsSupplied) {
181
193
  const retainedRolloutNames = new Set(rolloutFiles.map((r) => r.name));
@@ -185,7 +197,7 @@ function materializeForNamespace(namespace, options) {
185
197
  if (!entry.name.endsWith(".md")) continue;
186
198
  if (retainedRolloutNames.has(entry.name)) continue;
187
199
  try {
188
- fs.unlinkSync(path.join(destRolloutsDir, entry.name));
200
+ fs.unlinkSync(path2.join(destRolloutsDir, entry.name));
189
201
  } catch {
190
202
  }
191
203
  }
@@ -193,8 +205,8 @@ function materializeForNamespace(namespace, options) {
193
205
  }
194
206
  }
195
207
  for (const rollout of rolloutFiles) {
196
- const src = path.join(tmpDir, ROLLOUT_SUBDIR, rollout.name);
197
- const dest = path.join(destRolloutsDir, rollout.name);
208
+ const src = path2.join(tmpDir, ROLLOUT_SUBDIR, rollout.name);
209
+ const dest = path2.join(destRolloutsDir, rollout.name);
198
210
  fs.renameSync(src, dest);
199
211
  }
200
212
  const sentinel = {
@@ -419,7 +431,13 @@ function resolveCodexHome(override) {
419
431
  if (override && override.trim().length > 0) return override;
420
432
  const fromEnv = readEnvVar("CODEX_HOME");
421
433
  if (fromEnv && fromEnv.trim().length > 0) return fromEnv;
422
- return path.join(resolveHomeDir(), ".codex");
434
+ return path2.join(resolveHomeDir(), ".codex");
435
+ }
436
+ function resolveCodexMemoriesDir(codexHome) {
437
+ return path2.join(resolveCodexHome(codexHome), "memories");
438
+ }
439
+ function hasCodexMaterializeSentinel(codexHome) {
440
+ return readSentinel(path2.join(resolveCodexMemoriesDir(codexHome), SENTINEL_FILE)) !== null;
423
441
  }
424
442
  function readSentinel(sentinelPath) {
425
443
  if (!fs.existsSync(sentinelPath)) return null;
@@ -567,16 +585,19 @@ async function runCodexMaterialize(options) {
567
585
  if (options.memories) {
568
586
  memories = options.memories;
569
587
  } else {
588
+ if (!hasCodexMaterializeSentinel(options.codexHome)) {
589
+ return materializeForNamespace(namespace, {
590
+ memories: [],
591
+ codexHome: options.codexHome,
592
+ maxSummaryTokens: cfg.codexMaterializeMaxSummaryTokens,
593
+ rolloutRetentionDays: cfg.codexMaterializeRolloutRetentionDays,
594
+ rolloutSummaries: options.rolloutSummaries,
595
+ now: options.now
596
+ });
597
+ }
570
598
  const nsDir = resolveNamespaceDir(memoryDir, namespace, cfg);
571
599
  const storage = new StorageManager(nsDir);
572
- try {
573
- memories = await storage.readAllMemories();
574
- } catch (error) {
575
- log.warn(
576
- `[codex-materialize] skipped \u2014 failed to read memories from ${nsDir}: ${error instanceof Error ? error.message : String(error)}`
577
- );
578
- return null;
579
- }
600
+ memories = await storage.readAllMemories();
580
601
  }
581
602
  const result = materializeForNamespace(namespace, {
582
603
  memories,
@@ -616,16 +637,22 @@ async function runPostConsolidationMaterialize(logPrefix, options) {
616
637
  }
617
638
  function resolveNamespace(override, cfg) {
618
639
  const requested = (override ?? cfg.codexMaterializeNamespace ?? "auto").trim();
619
- if (requested.length === 0 || requested === "auto") {
620
- return cfg.defaultNamespace && cfg.defaultNamespace.length > 0 ? cfg.defaultNamespace : "default";
640
+ const defaultNamespace = (cfg.defaultNamespace ?? "").trim();
641
+ const namespace = requested.length === 0 || requested === "auto" ? defaultNamespace.length > 0 ? defaultNamespace : "default" : requested;
642
+ if (!isSafeRouteNamespace(namespace)) {
643
+ throw new Error(`invalid materialize namespace: ${namespace}`);
621
644
  }
622
- return requested;
645
+ return namespace;
623
646
  }
624
647
  function resolveNamespaceDir(memoryDir, namespace, cfg) {
625
648
  if (!cfg.namespacesEnabled) return memoryDir;
626
- const ns = namespace || cfg.defaultNamespace || "default";
627
- const namespacedRoot = path2.join(memoryDir, "namespaces", ns);
628
- if (ns === cfg.defaultNamespace) {
649
+ const defaultNamespace = (cfg.defaultNamespace ?? "").trim();
650
+ const ns = (namespace || defaultNamespace || "default").trim();
651
+ if (!isSafeRouteNamespace(ns)) {
652
+ throw new Error(`invalid materialize namespace: ${ns}`);
653
+ }
654
+ const namespacedRoot = resolveNamespaceChildRoot(memoryDir, ns, "materialize namespace path");
655
+ if (ns === defaultNamespace) {
629
656
  return existsSync(namespacedRoot) ? namespacedRoot : memoryDir;
630
657
  }
631
658
  return namespacedRoot;
@@ -853,190 +880,8 @@ ${ext.instructions}
853
880
  }
854
881
  return result;
855
882
  }
856
- function renderExtensionsFooter(extensions) {
857
- if (extensions.length === 0) return "";
858
- const names = extensions.map((ext) => ext.name).join(", ");
859
- return `Active extensions: ${names}`;
860
- }
861
883
 
862
884
  // ../remnic-core/src/semantic-consolidation.ts
863
- function findSimilarClusters(memories, config) {
864
- const excluded = new Set(config.excludeCategories);
865
- const byCategory = /* @__PURE__ */ new Map();
866
- for (const m of memories) {
867
- const cat = m.frontmatter.category;
868
- if (excluded.has(cat)) continue;
869
- if (m.frontmatter.status && m.frontmatter.status !== "active") continue;
870
- const list = byCategory.get(cat) ?? [];
871
- list.push(m);
872
- byCategory.set(cat, list);
873
- }
874
- const clusters = [];
875
- let totalCandidates = 0;
876
- for (const [category, mems] of byCategory) {
877
- if (totalCandidates >= config.maxPerRun) break;
878
- const tokenized = mems.map((m) => ({
879
- memory: m,
880
- tokens: new Set(normalizeRecallTokens(m.content, []))
881
- }));
882
- const clustered = /* @__PURE__ */ new Set();
883
- for (let i = 0; i < tokenized.length && totalCandidates < config.maxPerRun; i++) {
884
- if (clustered.has(tokenized[i].memory.frontmatter.id)) continue;
885
- const cluster = [tokenized[i].memory];
886
- let totalOverlap = 0;
887
- let comparisons = 0;
888
- for (let j = i + 1; j < tokenized.length; j++) {
889
- if (clustered.has(tokenized[j].memory.frontmatter.id)) continue;
890
- const aTokens = tokenized[i].tokens;
891
- const bTokens = tokenized[j].tokens;
892
- if (aTokens.size === 0 || bTokens.size === 0) continue;
893
- const overlap = countRecallTokenOverlap(aTokens, [...bTokens].join(" "));
894
- const maxTokens = Math.max(aTokens.size, bTokens.size);
895
- const score = maxTokens > 0 ? overlap / maxTokens : 0;
896
- if (score >= config.threshold) {
897
- cluster.push(tokenized[j].memory);
898
- totalOverlap += score;
899
- comparisons++;
900
- if (totalCandidates + cluster.length >= config.maxPerRun) break;
901
- }
902
- }
903
- if (cluster.length >= config.minClusterSize) {
904
- for (const m of cluster) clustered.add(m.frontmatter.id);
905
- clusters.push({
906
- category,
907
- memories: cluster,
908
- overlapScore: comparisons > 0 ? totalOverlap / comparisons : 0
909
- });
910
- totalCandidates += cluster.length;
911
- }
912
- }
913
- }
914
- return clusters;
915
- }
916
- function buildConsolidationPrompt(cluster) {
917
- const memoryTexts = cluster.memories.map(
918
- (m, i) => `Memory ${i + 1} (${m.frontmatter.id}, created ${m.frontmatter.created}):
919
- ${m.content}`
920
- ).join("\n\n");
921
- return `You are a memory consolidation system. The following ${cluster.memories.length} memories in the "${cluster.category}" category contain overlapping information.
922
-
923
- Synthesize them into ONE canonical memory that:
924
- 1. Preserves ALL unique information from every source memory
925
- 2. Removes redundancy and repetition
926
- 3. Uses clear, concise language
927
- 4. Maintains the same category and tone
928
- 5. Does NOT add information that isn't in the sources
929
-
930
- ${memoryTexts}
931
-
932
- Write ONLY the consolidated memory content (no metadata, no explanation, no preamble):`;
933
- }
934
- function parseConsolidationResponse(response) {
935
- return response.trim();
936
- }
937
- function chooseConsolidationOperator(cluster) {
938
- if (cluster.memories.length <= 1) return "update";
939
- return "merge";
940
- }
941
- function buildOperatorAwareConsolidationPrompt(cluster) {
942
- const memoryTexts = cluster.memories.map(
943
- (m, i) => `Memory ${i + 1} (${m.frontmatter.id}, created ${m.frontmatter.created}):
944
- ${m.content}`
945
- ).join("\n\n");
946
- return `You are a memory consolidation system. The following ${cluster.memories.length} memories in the "${cluster.category}" category contain overlapping information.
947
-
948
- Pick exactly ONE consolidation operator for this cluster and return a JSON object.
949
-
950
- Operator vocabulary:
951
- - "merge" \u2014 multiple distinct source memories overlap and should be collapsed into one canonical memory (most common).
952
- - "update" \u2014 one source memory carries a stale value that a newer source supersedes within the same logical fact.
953
- - "split" \u2014 a single logical source really encodes multiple distinct facts that should be separated (rare; if you pick split, still emit ONE canonical body \u2014 the write path will chunk it later).
954
-
955
- Output JSON ONLY, no prose before or after. The "operator" key MUST be set to exactly one of the three strings "merge", "update", or "split" \u2014 never a pipe-separated placeholder like "merge|update|split". Example shape:
956
- {
957
- "operator": "merge",
958
- "output": "<the canonical memory text>"
959
- }
960
-
961
- The "output" value must:
962
- 1. Preserve ALL unique information from every source memory
963
- 2. Remove redundancy and repetition
964
- 3. Use clear, concise language
965
- 4. Match the "${cluster.category}" category and tone
966
- 5. NOT add information that isn't in the sources
967
-
968
- ${memoryTexts}
969
-
970
- Return ONLY the JSON object:`;
971
- }
972
- function parseOperatorAwareConsolidationResponse(response, cluster) {
973
- const fallback = {
974
- operator: chooseConsolidationOperator(cluster),
975
- output: response.trim()
976
- };
977
- const trimmed = response.trim();
978
- if (trimmed.length === 0) return fallback;
979
- const fenced = /^```(?:json)?\s*([\s\S]*?)```\s*$/u.exec(trimmed);
980
- const payload = fenced ? fenced[1].trim() : trimmed;
981
- const parsed = findLastJsonObjectWithOperator(payload);
982
- if (parsed === void 0) return fallback;
983
- if (typeof parsed !== "object" || parsed === null) return fallback;
984
- const obj = parsed;
985
- const rawOperator = typeof obj.operator === "string" ? obj.operator.trim().toLowerCase() : "";
986
- const rawOutput = typeof obj.output === "string" ? obj.output : "";
987
- const operator = isSemanticConsolidationLlmOperator(rawOperator) ? rawOperator : chooseConsolidationOperator(cluster);
988
- const output = rawOutput.trim().length > 0 ? rawOutput.trim() : response.trim();
989
- return { operator, output };
990
- }
991
- function findLastJsonObjectWithOperator(text) {
992
- let searchFrom = 0;
993
- let last = void 0;
994
- while (searchFrom < text.length) {
995
- const start = text.indexOf("{", searchFrom);
996
- if (start < 0) return last;
997
- let depth = 0;
998
- let inString = false;
999
- let escape = false;
1000
- let closed = false;
1001
- let endIdx = -1;
1002
- for (let i = start; i < text.length; i++) {
1003
- const ch = text[i];
1004
- if (inString) {
1005
- if (escape) {
1006
- escape = false;
1007
- } else if (ch === "\\") {
1008
- escape = true;
1009
- } else if (ch === '"') {
1010
- inString = false;
1011
- }
1012
- continue;
1013
- }
1014
- if (ch === '"') {
1015
- inString = true;
1016
- } else if (ch === "{") {
1017
- depth += 1;
1018
- } else if (ch === "}") {
1019
- depth -= 1;
1020
- if (depth === 0) {
1021
- closed = true;
1022
- endIdx = i;
1023
- break;
1024
- }
1025
- }
1026
- }
1027
- if (!closed) return last;
1028
- const slice = text.slice(start, endIdx + 1);
1029
- try {
1030
- const parsed = JSON.parse(slice);
1031
- if (typeof parsed === "object" && parsed !== null && "operator" in parsed) {
1032
- last = parsed;
1033
- }
1034
- } catch {
1035
- }
1036
- searchFrom = endIdx + 1;
1037
- }
1038
- return last;
1039
- }
1040
885
  async function buildExtensionsBlockForConsolidation(config) {
1041
886
  if (!config.memoryExtensionsEnabled) return "";
1042
887
  const root = resolveExtensionsRoot(config);
@@ -1044,21 +889,8 @@ async function buildExtensionsBlockForConsolidation(config) {
1044
889
  if (extensions.length === 0) return "";
1045
890
  return renderExtensionsBlock(extensions);
1046
891
  }
1047
- async function materializeAfterSemanticConsolidation(options) {
1048
- return runPostConsolidationMaterialize("[semantic-consolidation]", options);
1049
- }
1050
892
 
1051
893
  export {
1052
- discoverMemoryExtensions,
1053
- resolveExtensionsRoot,
1054
- renderExtensionsFooter,
1055
894
  runPostConsolidationMaterialize,
1056
- findSimilarClusters,
1057
- buildConsolidationPrompt,
1058
- parseConsolidationResponse,
1059
- chooseConsolidationOperator,
1060
- buildOperatorAwareConsolidationPrompt,
1061
- parseOperatorAwareConsolidationResponse,
1062
- buildExtensionsBlockForConsolidation,
1063
- materializeAfterSemanticConsolidation
895
+ buildExtensionsBlockForConsolidation
1064
896
  };
@@ -8,7 +8,7 @@ import {
8
8
  expandTildePath,
9
9
  readEnvVar,
10
10
  resolveHomeDir
11
- } from "./chunk-TDRJVMUP.js";
11
+ } from "./chunk-EH4AXGRO.js";
12
12
 
13
13
  // ../remnic-core/src/fallback-llm.ts
14
14
  import path2 from "path";
@@ -66,6 +66,13 @@ var _resolverLoaded = false;
66
66
  var _resolverNextRetryAt = 0;
67
67
  var RESOLVER_RETRY_BACKOFF_MS = 6e4;
68
68
  var resolvedCache = /* @__PURE__ */ new Map();
69
+ var cacheObjectIds = /* @__PURE__ */ new WeakMap();
70
+ var nextCacheObjectId = 1;
71
+ var NON_LITERAL_AUTH_MARKERS = /* @__PURE__ */ new Set([
72
+ "secretref-managed",
73
+ "lm-studio"
74
+ ]);
75
+ var ENV_VAR_MARKER_RE = /^[A-Z][A-Z0-9_]*(?:_API_KEY|_ACCESS_TOKEN|_TOKEN|_SECRET|_CREDENTIALS|_CREDENTIALS_JSON)$/;
69
76
  async function getGatewayResolver() {
70
77
  if (_resolverLoaded) {
71
78
  return _resolveCredentialForProvider;
@@ -178,23 +185,59 @@ function findExecutableOnPath(executableName, access, stat, executableMode) {
178
185
  }
179
186
  return void 0;
180
187
  }
188
+ function isNonLiteralAuthMarker(value) {
189
+ return NON_LITERAL_AUTH_MARKERS.has(value) || value.endsWith("-oauth") || value.endsWith("-local") || value.startsWith("gcp-") || ENV_VAR_MARKER_RE.test(value);
190
+ }
191
+ function resolveFromNamedEnvVar(marker) {
192
+ if (!ENV_VAR_MARKER_RE.test(marker)) return void 0;
193
+ const value = readEnvVar(marker);
194
+ return value && value.trim().length > 0 ? value.trim() : void 0;
195
+ }
196
+ function cacheIdentity(value) {
197
+ if (value === null) return "null";
198
+ if (value === void 0) return "undefined";
199
+ if (typeof value === "string") return `string:${value}`;
200
+ if (typeof value === "number") return `number:${String(value)}`;
201
+ if (typeof value === "boolean") return `boolean:${String(value)}`;
202
+ if (typeof value === "bigint") return `bigint:${String(value)}`;
203
+ if (typeof value === "symbol" || typeof value === "function") return typeof value;
204
+ if (typeof value === "object") {
205
+ const existingId = cacheObjectIds.get(value);
206
+ if (existingId !== void 0) return `object:${existingId}`;
207
+ const newId = nextCacheObjectId++;
208
+ cacheObjectIds.set(value, newId);
209
+ return `object:${newId}`;
210
+ }
211
+ return String(value);
212
+ }
213
+ function providerSecretCacheKey(providerId, resolvedAgentDir, credentialValue, gatewayConfig) {
214
+ return [
215
+ `provider:${providerId}`,
216
+ `agentDir:${resolvedAgentDir}`,
217
+ `apiKey:${cacheIdentity(credentialValue)}`,
218
+ `cfg:${cacheIdentity(gatewayConfig)}`
219
+ ].join(":");
220
+ }
181
221
  async function resolveProviderCredential(providerId, credentialValue, gatewayConfig, agentDir) {
182
222
  const resolvedAgentDir = path.resolve(
183
223
  agentDir ?? path.join(os.homedir(), ".openclaw", "agents", "main", "agent")
184
224
  );
185
- const cacheKey = `provider:${providerId}:agentDir:${resolvedAgentDir}`;
186
- if (resolvedCache.has(cacheKey)) {
187
- return resolvedCache.get(cacheKey);
188
- }
189
225
  let resolved;
190
226
  if (typeof credentialValue === "string" && credentialValue.trim().length > 0) {
191
- if (credentialValue === "secretref-managed" || credentialValue.endsWith("-oauth") || credentialValue.endsWith("-local") || credentialValue === "lm-studio" || credentialValue.startsWith("gcp-")) {
227
+ const trimmedCredentialValue = credentialValue.trim();
228
+ if (isNonLiteralAuthMarker(trimmedCredentialValue)) {
229
+ const markerEnvValue = resolveFromNamedEnvVar(trimmedCredentialValue);
230
+ if (markerEnvValue) {
231
+ return markerEnvValue;
232
+ }
192
233
  } else {
193
- resolved = credentialValue;
194
- resolvedCache.set(cacheKey, resolved);
195
- return resolved;
234
+ return trimmedCredentialValue;
196
235
  }
197
236
  }
237
+ const cacheKey = providerSecretCacheKey(providerId, resolvedAgentDir, credentialValue, gatewayConfig);
238
+ if (resolvedCache.has(cacheKey)) {
239
+ return resolvedCache.get(cacheKey);
240
+ }
198
241
  const resolver = await getGatewayResolver();
199
242
  if (resolver) {
200
243
  try {
@@ -307,7 +350,8 @@ function normalizeCodexCliFallbackConfig(config) {
307
350
  }
308
351
  function normalizeCodexCliFallbackOptions(options) {
309
352
  return {
310
- ...options.timeoutMs !== void 0 ? { timeoutMs: normalizeCodexCliTimeoutMs(options.timeoutMs) } : {}
353
+ ...options.timeoutMs !== void 0 ? { timeoutMs: normalizeCodexCliTimeoutMs(options.timeoutMs) } : {},
354
+ ...options.signal ? { signal: options.signal } : {}
311
355
  };
312
356
  }
313
357
  function normalizeOptionalString(value, label) {
@@ -381,17 +425,20 @@ var FallbackLlmClient = class {
381
425
  * When agentId is provided, uses that agent persona's model chain instead of defaults.
382
426
  */
383
427
  async chatCompletion(messages, options = {}) {
384
- const models = this.getModelChain(options.agentId);
428
+ const models = this.getModelChain(options.agentId, options.model);
385
429
  if (models.length === 0) {
386
430
  log.warn("fallback LLM: no models configured in gateway");
387
431
  return null;
388
432
  }
389
- const runChain = async () => {
433
+ const runChain = async (runOptions) => {
390
434
  for (let i = 0; i < models.length; i++) {
435
+ if (runOptions.signal?.aborted) {
436
+ throw abortReason(runOptions.signal);
437
+ }
391
438
  const model = models[i];
392
439
  const isFallback = i > 0;
393
440
  try {
394
- const result = await this.tryModel(model, messages, options);
441
+ const result = await this.tryModel(model, messages, runOptions);
395
442
  if (result) {
396
443
  if (isFallback) {
397
444
  log.debug(`fallback LLM: succeeded using ${model.modelString} (fallback ${i})`);
@@ -403,6 +450,9 @@ var FallbackLlmClient = class {
403
450
  };
404
451
  }
405
452
  } catch (err) {
453
+ if (runOptions.signal?.aborted) {
454
+ throw abortReason(runOptions.signal);
455
+ }
406
456
  const errorMsg = err instanceof Error ? err.message : String(err);
407
457
  log.debug(`fallback LLM: ${model.modelString} failed (${errorMsg}), trying next...`);
408
458
  }
@@ -416,21 +466,37 @@ var FallbackLlmClient = class {
416
466
  return null;
417
467
  }
418
468
  let timeoutHandle;
469
+ const controller = new AbortController();
470
+ const onCallerAbort = () => {
471
+ controller.abort(abortReason(options.signal));
472
+ };
473
+ options.signal?.addEventListener("abort", onCallerAbort, { once: true });
474
+ if (options.signal?.aborted) {
475
+ onCallerAbort();
476
+ }
477
+ const timedOptions = { ...options, signal: controller.signal };
478
+ const chain = runChain(timedOptions);
479
+ chain.catch(() => {
480
+ });
419
481
  try {
420
482
  return await Promise.race([
421
- runChain(),
483
+ chain,
422
484
  new Promise((resolve) => {
423
485
  timeoutHandle = setTimeout(() => {
424
486
  log.warn(`fallback LLM: timed out after ${options.timeoutMs}ms`);
487
+ controller.abort(
488
+ new Error(`fallback LLM timed out after ${options.timeoutMs}ms`)
489
+ );
425
490
  resolve(null);
426
491
  }, options.timeoutMs);
427
492
  })
428
493
  ]);
429
494
  } finally {
430
495
  if (timeoutHandle) clearTimeout(timeoutHandle);
496
+ options.signal?.removeEventListener("abort", onCallerAbort);
431
497
  }
432
498
  }
433
- return await runChain();
499
+ return await runChain(options);
434
500
  }
435
501
  /**
436
502
  * Make a request with structured output (Zod schema).
@@ -470,7 +536,7 @@ var FallbackLlmClient = class {
470
536
  * and uses that persona's model chain. Falls back to agents.defaults.model
471
537
  * if agentId is not found or not provided.
472
538
  */
473
- getModelChain(agentId) {
539
+ getModelChain(agentId, modelOverride) {
474
540
  const chain = [];
475
541
  const providers = this.gatewayConfig?.models?.providers ?? {};
476
542
  let modelConfig;
@@ -491,8 +557,13 @@ var FallbackLlmClient = class {
491
557
  modelConfig = this.gatewayConfig?.agents?.defaults?.model;
492
558
  }
493
559
  const modelStrings = [];
560
+ if (typeof modelOverride === "string" && modelOverride.trim().length > 0) {
561
+ modelStrings.push(modelOverride.trim());
562
+ }
494
563
  if (modelConfig?.primary) {
495
- modelStrings.push(modelConfig.primary);
564
+ if (!modelStrings.includes(modelConfig.primary)) {
565
+ modelStrings.push(modelConfig.primary);
566
+ }
496
567
  }
497
568
  if (Array.isArray(modelConfig?.fallbacks)) {
498
569
  for (const fb of modelConfig.fallbacks) {
@@ -618,7 +689,7 @@ var FallbackLlmClient = class {
618
689
  effectiveConfig,
619
690
  model.modelId,
620
691
  messages,
621
- { timeoutMs: options.timeoutMs }
692
+ { timeoutMs: options.timeoutMs, signal: options.signal }
622
693
  );
623
694
  }
624
695
  if (model.providerConfig.api === "ollama-chat") {
@@ -712,6 +783,7 @@ var FallbackLlmClient = class {
712
783
  const response = await fetch(url, {
713
784
  method: "POST",
714
785
  headers,
786
+ signal: options.signal,
715
787
  body: JSON.stringify(body)
716
788
  });
717
789
  if (!response.ok) {
@@ -750,6 +822,7 @@ var FallbackLlmClient = class {
750
822
  const response = await fetch(url, {
751
823
  method: "POST",
752
824
  headers,
825
+ signal: options.signal,
753
826
  body: JSON.stringify({
754
827
  model: modelId,
755
828
  messages,
@@ -816,6 +889,7 @@ var FallbackLlmClient = class {
816
889
  const response = await fetch(url, {
817
890
  method: "POST",
818
891
  headers,
892
+ signal: options.signal,
819
893
  body: JSON.stringify(body)
820
894
  });
821
895
  if (!response.ok) {
@@ -868,6 +942,7 @@ var FallbackLlmClient = class {
868
942
  const response = await fetch(url, {
869
943
  method: "POST",
870
944
  headers,
945
+ signal: options.signal,
871
946
  body: JSON.stringify(body)
872
947
  });
873
948
  if (!response.ok) {
@@ -889,6 +964,10 @@ var FallbackLlmClient = class {
889
964
  };
890
965
  }
891
966
  };
967
+ function abortReason(signal) {
968
+ const reason = signal?.reason;
969
+ return reason instanceof Error ? reason : new Error("fallback LLM request aborted");
970
+ }
892
971
  function normalizeRuntimePath(value) {
893
972
  if (typeof value !== "string") return void 0;
894
973
  const trimmed = value.trim();
@@ -922,8 +1001,5 @@ function extractResponsesOutputText(data) {
922
1001
  }
923
1002
 
924
1003
  export {
925
- shouldAssumeOpenAiChatCompletions,
926
- buildChatCompletionTokenLimit,
927
- findGatewayRuntimeModules,
928
1004
  FallbackLlmClient
929
1005
  };