@joshuaswarren/openclaw-engram 8.3.82 → 8.3.84

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
@@ -3655,6 +3655,9 @@ function scoreImportance(content, category, tags = []) {
3655
3655
  keywords
3656
3656
  };
3657
3657
  }
3658
+ function rescoreMemoryImportance(memory) {
3659
+ return scoreImportance(memory.content, memory.frontmatter.category, memory.frontmatter.tags ?? []);
3660
+ }
3658
3661
 
3659
3662
  // src/qmd.ts
3660
3663
  import { spawn } from "child_process";
@@ -6039,6 +6042,46 @@ ${memory.content}
6039
6042
  await appendFile(this.behaviorSignalsPath, payload, "utf-8");
6040
6043
  return deduped.length;
6041
6044
  }
6045
+ async appendReextractJobs(events) {
6046
+ if (events.length === 0) return 0;
6047
+ await this.ensureDirectories();
6048
+ const filePath = path4.join(this.stateDir, "reextract-jobs.jsonl");
6049
+ const lines = events.map((event) => JSON.stringify(event)).join("\n") + "\n";
6050
+ try {
6051
+ await appendFile(filePath, lines, "utf-8");
6052
+ return events.length;
6053
+ } catch {
6054
+ return 0;
6055
+ }
6056
+ }
6057
+ async readReextractJobs(limit = 200) {
6058
+ const safeLimit = Number.isFinite(limit) ? Math.max(1, Math.min(1e3, Math.floor(limit))) : 200;
6059
+ const filePath = path4.join(this.stateDir, "reextract-jobs.jsonl");
6060
+ try {
6061
+ const raw = await readFile2(filePath, "utf-8");
6062
+ const lines = raw.split("\n").filter((line) => line.trim().length > 0);
6063
+ const parsed = [];
6064
+ for (const line of lines) {
6065
+ try {
6066
+ const record = JSON.parse(line);
6067
+ if (typeof record.memoryId !== "string" || record.memoryId.length === 0 || typeof record.model !== "string" || record.model.length === 0 || typeof record.requestedAt !== "string" || record.requestedAt.length === 0 || record.source !== "cli-migrate") {
6068
+ continue;
6069
+ }
6070
+ parsed.push({
6071
+ memoryId: record.memoryId,
6072
+ model: record.model,
6073
+ requestedAt: record.requestedAt,
6074
+ source: "cli-migrate"
6075
+ });
6076
+ } catch {
6077
+ continue;
6078
+ }
6079
+ }
6080
+ return parsed.slice(-safeLimit);
6081
+ } catch {
6082
+ return [];
6083
+ }
6084
+ }
6042
6085
  async readBehaviorSignals(limit = 200) {
6043
6086
  const cappedLimit = Math.max(0, Math.floor(limit));
6044
6087
  if (cappedLimit === 0) return [];
@@ -12108,6 +12151,58 @@ function safeSlug(s) {
12108
12151
  function ymd(d) {
12109
12152
  return d.toISOString().slice(0, 10);
12110
12153
  }
12154
+ var CROSS_SIGNAL_STOPWORDS = /* @__PURE__ */ new Set([
12155
+ "a",
12156
+ "an",
12157
+ "and",
12158
+ "are",
12159
+ "as",
12160
+ "at",
12161
+ "be",
12162
+ "by",
12163
+ "for",
12164
+ "from",
12165
+ "has",
12166
+ "have",
12167
+ "in",
12168
+ "is",
12169
+ "it",
12170
+ "of",
12171
+ "on",
12172
+ "or",
12173
+ "that",
12174
+ "the",
12175
+ "this",
12176
+ "to",
12177
+ "was",
12178
+ "were",
12179
+ "with",
12180
+ "agent",
12181
+ "output",
12182
+ "today",
12183
+ "daily",
12184
+ "notes",
12185
+ "note",
12186
+ "summary"
12187
+ ]);
12188
+ function extractTopicTokens(text, maxTokens = 12) {
12189
+ const seen = /* @__PURE__ */ new Set();
12190
+ const out = [];
12191
+ const tokens = text.toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).map((token) => token.trim()).filter((token) => token.length >= 4).filter((token) => !CROSS_SIGNAL_STOPWORDS.has(token));
12192
+ for (const token of tokens) {
12193
+ if (seen.has(token)) continue;
12194
+ seen.add(token);
12195
+ out.push(token);
12196
+ if (out.length >= maxTokens) break;
12197
+ }
12198
+ return out;
12199
+ }
12200
+ function stripYamlFrontmatter(text) {
12201
+ if (!text.startsWith("---\n")) return text;
12202
+ const closing = text.indexOf("\n---\n", 4);
12203
+ if (closing === -1) return text;
12204
+ return text.slice(closing + 5);
12205
+ }
12111
12206
  var SharedContextManager = class {
12112
12207
  constructor(config) {
12113
12208
  this.config = config;
@@ -12238,7 +12333,7 @@ title: ${opts.title.replace(/\n/g, " ").slice(0, 200)}
12238
12333
  const p = path22.join(dayDir, f);
12239
12334
  const raw = await readFile15(p, "utf-8");
12240
12335
  const title = (raw.match(/^title:\s*(.+)$/m)?.[1] ?? f).trim();
12241
- outputs.push({ path: p, title });
12336
+ outputs.push({ agent: a.name, path: p, title, raw });
12242
12337
  }
12243
12338
  } catch {
12244
12339
  }
@@ -12260,25 +12355,86 @@ title: ${opts.title.replace(/\n/g, " ").slice(0, 200)}
12260
12355
  }
12261
12356
  } catch {
12262
12357
  }
12358
+ const sources = outputs.map((output) => {
12359
+ const body = stripYamlFrontmatter(output.raw);
12360
+ return {
12361
+ agent: output.agent,
12362
+ path: output.path,
12363
+ title: output.title,
12364
+ topics: extractTopicTokens(`${output.title}
12365
+ ${body}`)
12366
+ };
12367
+ });
12368
+ const overlapMap = /* @__PURE__ */ new Map();
12369
+ for (const source of sources) {
12370
+ for (const token of source.topics) {
12371
+ const existing = overlapMap.get(token);
12372
+ if (existing) {
12373
+ existing.agents.add(source.agent);
12374
+ existing.sourcePaths.add(source.path);
12375
+ } else {
12376
+ overlapMap.set(token, {
12377
+ agents: /* @__PURE__ */ new Set([source.agent]),
12378
+ sourcePaths: /* @__PURE__ */ new Set([source.path])
12379
+ });
12380
+ }
12381
+ }
12382
+ }
12383
+ const overlaps = [...overlapMap.entries()].map(([token, v]) => ({
12384
+ token,
12385
+ agents: [...v.agents].sort(),
12386
+ sourcePaths: [...v.sourcePaths].sort(),
12387
+ agentCount: v.agents.size
12388
+ })).filter((entry) => entry.agentCount >= 2).sort((a, b) => b.agentCount - a.agentCount || a.token.localeCompare(b.token));
12389
+ const feedbackByDecision = {
12390
+ approved: 0,
12391
+ approved_with_feedback: 0,
12392
+ rejected: 0
12393
+ };
12394
+ for (const entry of feedback) {
12395
+ feedbackByDecision[entry.decision] += 1;
12396
+ }
12397
+ const crossSignalReport = {
12398
+ date,
12399
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
12400
+ sourceCount: sources.length,
12401
+ feedbackCount: feedback.length,
12402
+ feedbackByDecision,
12403
+ sources,
12404
+ overlaps
12405
+ };
12406
+ const crossSignalsPath = path22.join(this.crossSignalsDir, `${date}.json`);
12407
+ await writeFile14(crossSignalsPath, `${JSON.stringify(crossSignalReport, null, 2)}
12408
+ `, "utf-8");
12409
+ const overlapBullets = overlaps.length === 0 ? ["- No multi-agent topic overlap detected."] : overlaps.slice(0, 8).map((entry) => `- \`${entry.token}\` (${entry.agentCount} agents: ${entry.agents.join(", ")})`);
12263
12410
  const md = [
12264
12411
  `# Roundtable \u2014 ${date}`,
12265
12412
  "",
12266
12413
  "## Notable Agent Outputs",
12267
- ...outputs.length === 0 ? ["- (none)"] : outputs.map((o) => `- ${o.title} (${o.path})`),
12414
+ ...sources.length === 0 ? ["- (none)"] : sources.map((o) => `- ${o.title} (${o.path})`),
12268
12415
  "",
12269
12416
  "## Feedback (Approve/Reject)",
12270
12417
  ...feedback.length === 0 ? ["- (none)"] : feedback.map((f) => `- [${f.agent}] ${f.decision}: ${f.reason}`),
12271
12418
  "",
12272
12419
  "## Cross-Signals",
12273
- "- (baseline) not computed in deterministic mode",
12420
+ `- Source outputs analyzed: ${sources.length}`,
12421
+ `- Feedback entries analyzed: ${feedback.length}`,
12422
+ `- Decision totals: approved=${feedbackByDecision.approved}, approved_with_feedback=${feedbackByDecision.approved_with_feedback}, rejected=${feedbackByDecision.rejected}`,
12423
+ `- Cross-signals file: ${crossSignalsPath}`,
12424
+ ...overlapBullets,
12274
12425
  ""
12275
12426
  ];
12276
12427
  const out = md.join("\n");
12277
12428
  const trimmed = out.length > maxChars ? out.slice(0, maxChars) + "\n\n...(trimmed)\n" : out;
12278
- const fp = path22.join(this.roundtableDir, `${date}.md`);
12279
- await writeFile14(fp, trimmed, "utf-8");
12280
- log.info(`shared-context curated daily roundtable: ${fp}`);
12281
- return fp;
12429
+ const roundtablePath = path22.join(this.roundtableDir, `${date}.md`);
12430
+ await writeFile14(roundtablePath, trimmed, "utf-8");
12431
+ log.info(`shared-context curated daily roundtable: ${roundtablePath}`);
12432
+ return {
12433
+ date,
12434
+ roundtablePath,
12435
+ crossSignalsPath,
12436
+ overlapCount: overlaps.length
12437
+ };
12282
12438
  }
12283
12439
  };
12284
12440
 
@@ -19790,8 +19946,14 @@ Best for:
19790
19946
  );
19791
19947
  }
19792
19948
  const { date } = params;
19793
- const fp = await orchestrator.sharedContext.curateDaily({ date });
19794
- return toolResult(`Wrote: ${fp}`);
19949
+ const result = await orchestrator.sharedContext.curateDaily({ date });
19950
+ return toolResult(
19951
+ [
19952
+ `Roundtable: ${result.roundtablePath}`,
19953
+ `Cross-signals: ${result.crossSignalsPath}`,
19954
+ `Overlap count: ${result.overlapCount}`
19955
+ ].join("\n")
19956
+ );
19795
19957
  }
19796
19958
  },
19797
19959
  { name: "shared_context_curate_daily" }
@@ -22122,6 +22284,187 @@ async function runTierMigrateCliCommand(orchestrator, options = {}) {
22122
22284
  limit: options.limit
22123
22285
  });
22124
22286
  }
22287
+ var MIGRATE_LIMIT_CAP = 2e3;
22288
+ var REEXTRACT_LIMIT_CAP = 500;
22289
+ function clampMigrateLimit(limit, cap, fallback) {
22290
+ if (typeof limit !== "number" || !Number.isFinite(limit)) return fallback;
22291
+ return Math.max(1, Math.min(cap, Math.floor(limit)));
22292
+ }
22293
+ async function readMigrateCandidateMemories(storage, options) {
22294
+ const merged = /* @__PURE__ */ new Map();
22295
+ const addMany = (items) => {
22296
+ for (const item of items) {
22297
+ if (!item.frontmatter?.id) continue;
22298
+ merged.set(item.path, item);
22299
+ }
22300
+ };
22301
+ addMany(await storage.readAllMemories());
22302
+ if (options.includeArchived) {
22303
+ addMany(await storage.readArchivedMemories());
22304
+ }
22305
+ return [...merged.values()].sort((a, b) => a.path.localeCompare(b.path));
22306
+ }
22307
+ function sameImportance(a, b) {
22308
+ if (!a && !b) return true;
22309
+ if (!a || !b) return false;
22310
+ if (Math.abs(a.score - b.score) > 1e-6) return false;
22311
+ if (a.level !== b.level) return false;
22312
+ if (a.reasons.join("|") !== b.reasons.join("|")) return false;
22313
+ if (a.keywords.join("|") !== b.keywords.join("|")) return false;
22314
+ return true;
22315
+ }
22316
+ function sameChunkContent(existing, desired) {
22317
+ if (existing.length !== desired.length) return false;
22318
+ for (let i = 0; i < desired.length; i += 1) {
22319
+ const current = existing[i]?.content?.trim() ?? "";
22320
+ if (current !== desired[i]?.trim()) {
22321
+ return false;
22322
+ }
22323
+ }
22324
+ return true;
22325
+ }
22326
+ async function runMigrateNormalizeFrontmatterCliCommand(orchestrator, options = {}) {
22327
+ const limit = clampMigrateLimit(options.limit, MIGRATE_LIMIT_CAP, 200);
22328
+ const storage = await orchestrator.getStorage(orchestrator.config.defaultNamespace);
22329
+ const candidates = (await readMigrateCandidateMemories(storage, { includeArchived: true })).slice(0, limit);
22330
+ if (options.write === true) {
22331
+ for (const memory of candidates) {
22332
+ await storage.writeMemoryFrontmatter(memory, {});
22333
+ }
22334
+ }
22335
+ return {
22336
+ action: "normalize-frontmatter",
22337
+ dryRun: options.write !== true,
22338
+ scanned: candidates.length,
22339
+ changed: candidates.length,
22340
+ queued: 0,
22341
+ limit
22342
+ };
22343
+ }
22344
+ async function runMigrateRescoreImportanceCliCommand(orchestrator, options = {}) {
22345
+ const limit = clampMigrateLimit(options.limit, MIGRATE_LIMIT_CAP, 200);
22346
+ const storage = await orchestrator.getStorage(orchestrator.config.defaultNamespace);
22347
+ const candidates = (await readMigrateCandidateMemories(storage, { includeArchived: true })).slice(0, limit);
22348
+ let changed = 0;
22349
+ for (const memory of candidates) {
22350
+ const nextImportance = rescoreMemoryImportance(memory);
22351
+ if (sameImportance(memory.frontmatter.importance, nextImportance)) continue;
22352
+ changed += 1;
22353
+ if (options.write === true) {
22354
+ await storage.writeMemoryFrontmatter(memory, {
22355
+ importance: nextImportance,
22356
+ updated: (/* @__PURE__ */ new Date()).toISOString()
22357
+ });
22358
+ }
22359
+ }
22360
+ return {
22361
+ action: "rescore-importance",
22362
+ dryRun: options.write !== true,
22363
+ scanned: candidates.length,
22364
+ changed,
22365
+ queued: 0,
22366
+ limit
22367
+ };
22368
+ }
22369
+ async function runMigrateRechunkCliCommand(orchestrator, options = {}) {
22370
+ const limit = clampMigrateLimit(options.limit, MIGRATE_LIMIT_CAP, 200);
22371
+ const storage = await orchestrator.getStorage(orchestrator.config.defaultNamespace);
22372
+ const candidates = (await readMigrateCandidateMemories(storage, { includeArchived: false })).filter((memory) => memory.frontmatter.parentId === void 0).slice(0, limit);
22373
+ let changed = 0;
22374
+ for (const memory of candidates) {
22375
+ const existing = await storage.getChunksForParent(memory.frontmatter.id);
22376
+ const chunked = chunkContent(memory.content);
22377
+ if (!chunked.chunked) {
22378
+ if (existing.length === 0) continue;
22379
+ changed += 1;
22380
+ if (options.write === true) {
22381
+ for (const stale of existing) {
22382
+ await storage.invalidateMemory(stale.frontmatter.id);
22383
+ }
22384
+ }
22385
+ continue;
22386
+ }
22387
+ const desired = chunked.chunks.map((chunk) => chunk.content);
22388
+ if (sameChunkContent(existing, desired)) continue;
22389
+ changed += 1;
22390
+ if (options.write !== true) continue;
22391
+ const total = chunked.chunks.length;
22392
+ for (const chunk of chunked.chunks) {
22393
+ const existingChunk = existing[chunk.index];
22394
+ if (existingChunk) {
22395
+ await storage.updateMemory(existingChunk.frontmatter.id, chunk.content);
22396
+ await storage.updateMemoryFrontmatter(existingChunk.frontmatter.id, {
22397
+ chunkIndex: chunk.index,
22398
+ chunkTotal: total,
22399
+ updated: (/* @__PURE__ */ new Date()).toISOString()
22400
+ });
22401
+ continue;
22402
+ }
22403
+ await storage.writeChunk(
22404
+ memory.frontmatter.id,
22405
+ chunk.index,
22406
+ total,
22407
+ memory.frontmatter.category,
22408
+ chunk.content,
22409
+ {
22410
+ confidence: memory.frontmatter.confidence,
22411
+ tags: memory.frontmatter.tags,
22412
+ entityRef: memory.frontmatter.entityRef,
22413
+ source: "migration-rechunk",
22414
+ importance: memory.frontmatter.importance,
22415
+ intentGoal: memory.frontmatter.intentGoal,
22416
+ intentActionType: memory.frontmatter.intentActionType,
22417
+ intentEntityTypes: memory.frontmatter.intentEntityTypes,
22418
+ memoryKind: memory.frontmatter.memoryKind
22419
+ }
22420
+ );
22421
+ }
22422
+ for (let idx = total; idx < existing.length; idx += 1) {
22423
+ const stale = existing[idx];
22424
+ if (stale?.frontmatter?.id) {
22425
+ await storage.invalidateMemory(stale.frontmatter.id);
22426
+ }
22427
+ }
22428
+ }
22429
+ return {
22430
+ action: "rechunk",
22431
+ dryRun: options.write !== true,
22432
+ scanned: candidates.length,
22433
+ changed,
22434
+ queued: 0,
22435
+ limit
22436
+ };
22437
+ }
22438
+ async function runMigrateReextractCliCommand(orchestrator, options) {
22439
+ const model = options.model.trim();
22440
+ if (model.length === 0) {
22441
+ throw new Error("missing --model for migrate reextract");
22442
+ }
22443
+ const limit = clampMigrateLimit(options.limit, REEXTRACT_LIMIT_CAP, 100);
22444
+ const storage = await orchestrator.getStorage(orchestrator.config.defaultNamespace);
22445
+ const candidates = (await readMigrateCandidateMemories(storage, { includeArchived: false })).filter((memory) => memory.frontmatter.parentId === void 0);
22446
+ const selected = candidates.slice(0, limit);
22447
+ let queued = 0;
22448
+ if (options.write === true && selected.length > 0) {
22449
+ queued = await storage.appendReextractJobs(
22450
+ selected.map((memory) => ({
22451
+ memoryId: memory.frontmatter.id,
22452
+ model,
22453
+ requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
22454
+ source: "cli-migrate"
22455
+ }))
22456
+ );
22457
+ }
22458
+ return {
22459
+ action: "reextract",
22460
+ dryRun: options.write !== true,
22461
+ scanned: selected.length,
22462
+ changed: selected.length,
22463
+ queued,
22464
+ limit,
22465
+ model
22466
+ };
22467
+ }
22125
22468
  function effectivePolicyValuesForVersion(values, config) {
22126
22469
  const candidate = {
22127
22470
  recencyWeight: values.recencyWeight ?? config.recencyWeight,
@@ -23023,6 +23366,49 @@ function registerCli(api, orchestrator) {
23023
23366
  console.log(JSON.stringify(report, null, 2));
23024
23367
  console.log("OK");
23025
23368
  });
23369
+ const migrateCmd = cmd.command("migrate").description("Run memory migration helpers (dry-run by default)");
23370
+ migrateCmd.command("normalize-frontmatter").description("Normalize memory frontmatter serialization").option("--write", "Apply frontmatter rewrites (default: dry-run)").option("--limit <n>", "Maximum memories to scan", "200").action(async (...args) => {
23371
+ const options = args[0] ?? {};
23372
+ const limitRaw = parseInt(String(options.limit ?? "200"), 10);
23373
+ const report = await runMigrateNormalizeFrontmatterCliCommand(orchestrator, {
23374
+ write: options.write === true,
23375
+ limit: Number.isFinite(limitRaw) ? limitRaw : 200
23376
+ });
23377
+ console.log(JSON.stringify(report, null, 2));
23378
+ console.log("OK");
23379
+ });
23380
+ migrateCmd.command("rescore-importance").description("Recompute memory importance scores using current local heuristics").option("--write", "Apply frontmatter updates (default: dry-run)").option("--limit <n>", "Maximum memories to scan", "200").action(async (...args) => {
23381
+ const options = args[0] ?? {};
23382
+ const limitRaw = parseInt(String(options.limit ?? "200"), 10);
23383
+ const report = await runMigrateRescoreImportanceCliCommand(orchestrator, {
23384
+ write: options.write === true,
23385
+ limit: Number.isFinite(limitRaw) ? limitRaw : 200
23386
+ });
23387
+ console.log(JSON.stringify(report, null, 2));
23388
+ console.log("OK");
23389
+ });
23390
+ migrateCmd.command("rechunk").description("Rebuild chunk files from current chunking heuristics").option("--write", "Apply chunk rewrites (default: dry-run)").option("--limit <n>", "Maximum parent memories to scan", "200").action(async (...args) => {
23391
+ const options = args[0] ?? {};
23392
+ const limitRaw = parseInt(String(options.limit ?? "200"), 10);
23393
+ const report = await runMigrateRechunkCliCommand(orchestrator, {
23394
+ write: options.write === true,
23395
+ limit: Number.isFinite(limitRaw) ? limitRaw : 200
23396
+ });
23397
+ console.log(JSON.stringify(report, null, 2));
23398
+ console.log("OK");
23399
+ });
23400
+ migrateCmd.command("reextract").description("Queue bounded memory re-extraction jobs for an explicit model").option("--model <id>", "Model id used for re-extraction request").option("--write", "Queue re-extraction jobs (default: dry-run)").option("--limit <n>", "Maximum memories to queue", "100").action(async (...args) => {
23401
+ const options = args[0] ?? {};
23402
+ const model = typeof options.model === "string" ? options.model : "";
23403
+ const limitRaw = parseInt(String(options.limit ?? "100"), 10);
23404
+ const report = await runMigrateReextractCliCommand(orchestrator, {
23405
+ model,
23406
+ write: options.write === true,
23407
+ limit: Number.isFinite(limitRaw) ? limitRaw : 100
23408
+ });
23409
+ console.log(JSON.stringify(report, null, 2));
23410
+ console.log("OK");
23411
+ });
23026
23412
  cmd.command("action-audit").description("Show namespace-aware memory action policy outcomes").option("--namespace <name>", "Filter to a single namespace").option("--limit <n>", "Max events to read per namespace", "200").action(async (...args) => {
23027
23413
  const options = args[0] ?? {};
23028
23414
  const limitRaw = parseInt(String(options.limit ?? "200"), 10);