@remnic/core 9.3.657 → 9.3.658

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 (75) hide show
  1. package/dist/access-cli.js +12 -12
  2. package/dist/access-http.js +6 -6
  3. package/dist/access-mcp.js +5 -5
  4. package/dist/access-service.js +4 -4
  5. package/dist/briefing.d.ts +1 -1
  6. package/dist/briefing.js +2 -2
  7. package/dist/causal-consolidation.js +3 -3
  8. package/dist/{chunk-YEZHZCUO.js → chunk-2VCTTEJM.js} +3 -3
  9. package/dist/{chunk-DR67OK4E.js → chunk-3R6OP33G.js} +3 -3
  10. package/dist/{chunk-JSVFEHLL.js → chunk-46RXRASB.js} +2 -2
  11. package/dist/{chunk-NXCK7DO7.js → chunk-4PLOQDBB.js} +54 -18
  12. package/dist/chunk-4PLOQDBB.js.map +1 -0
  13. package/dist/{chunk-54XF2FY7.js → chunk-5PFIMBJJ.js} +12 -12
  14. package/dist/{chunk-4UL7VPTD.js → chunk-6M4LYWA2.js} +4 -4
  15. package/dist/{chunk-DIBWFCLA.js → chunk-7KSPKZIQ.js} +3 -3
  16. package/dist/{chunk-RDW5G6DO.js → chunk-7VWDC7AD.js} +10 -11
  17. package/dist/chunk-7VWDC7AD.js.map +1 -0
  18. package/dist/{chunk-SWDHVH2P.js → chunk-BKRIAXTU.js} +2 -2
  19. package/dist/{chunk-WWMHAMAY.js → chunk-BNUAOLDK.js} +2 -2
  20. package/dist/{chunk-GCYFUTUC.js → chunk-FIS5RT6K.js} +2 -2
  21. package/dist/{chunk-IOZ5WBWD.js → chunk-G2VVBWFU.js} +7 -5
  22. package/dist/chunk-G2VVBWFU.js.map +1 -0
  23. package/dist/{chunk-GSHW5VVD.js → chunk-GGL7R2L2.js} +4 -4
  24. package/dist/{chunk-WIKMCJUR.js → chunk-JI3LQFJH.js} +2 -2
  25. package/dist/{chunk-Z6UDTNY6.js → chunk-KI6QM5AV.js} +6 -6
  26. package/dist/chunk-KI6QM5AV.js.map +1 -0
  27. package/dist/{chunk-2BD7DG37.js → chunk-MBZAESQ3.js} +2 -2
  28. package/dist/{chunk-VAEAGTEQ.js → chunk-QFKRE7AU.js} +3 -3
  29. package/dist/{chunk-AGJKWOKV.js → chunk-RVT6U6PV.js} +2 -2
  30. package/dist/{chunk-TFFZUFEP.js → chunk-VJYFXDCZ.js} +2 -2
  31. package/dist/{chunk-QZRKNA5F.js → chunk-ZCMO46YY.js} +2 -2
  32. package/dist/cli.js +15 -15
  33. package/dist/compounding/engine.js +2 -2
  34. package/dist/connectors/codex-materialize-runner.js +2 -2
  35. package/dist/connectors/index.js +2 -2
  36. package/dist/entity-retrieval.js +2 -2
  37. package/dist/index.js +20 -20
  38. package/dist/maintenance/memory-governance.js +2 -2
  39. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +2 -2
  40. package/dist/maintenance/rebuild-memory-projection.js +3 -3
  41. package/dist/namespaces/migrate.js +3 -3
  42. package/dist/namespaces/storage.js +2 -2
  43. package/dist/operator-toolkit.js +5 -5
  44. package/dist/orchestrator.js +10 -10
  45. package/dist/semantic-consolidation.js +3 -3
  46. package/dist/semantic-rule-promotion.js +2 -2
  47. package/dist/semantic-rule-verifier.js +2 -2
  48. package/dist/storage.d.ts +27 -5
  49. package/dist/storage.js +1 -1
  50. package/dist/verified-recall.js +2 -2
  51. package/package.json +1 -1
  52. package/src/briefing.ts +8 -4
  53. package/src/entity-retrieval.ts +8 -3
  54. package/src/orchestrator.ts +1 -2
  55. package/src/storage.ts +79 -26
  56. package/dist/chunk-IOZ5WBWD.js.map +0 -1
  57. package/dist/chunk-NXCK7DO7.js.map +0 -1
  58. package/dist/chunk-RDW5G6DO.js.map +0 -1
  59. package/dist/chunk-Z6UDTNY6.js.map +0 -1
  60. /package/dist/{chunk-YEZHZCUO.js.map → chunk-2VCTTEJM.js.map} +0 -0
  61. /package/dist/{chunk-DR67OK4E.js.map → chunk-3R6OP33G.js.map} +0 -0
  62. /package/dist/{chunk-JSVFEHLL.js.map → chunk-46RXRASB.js.map} +0 -0
  63. /package/dist/{chunk-54XF2FY7.js.map → chunk-5PFIMBJJ.js.map} +0 -0
  64. /package/dist/{chunk-4UL7VPTD.js.map → chunk-6M4LYWA2.js.map} +0 -0
  65. /package/dist/{chunk-DIBWFCLA.js.map → chunk-7KSPKZIQ.js.map} +0 -0
  66. /package/dist/{chunk-SWDHVH2P.js.map → chunk-BKRIAXTU.js.map} +0 -0
  67. /package/dist/{chunk-WWMHAMAY.js.map → chunk-BNUAOLDK.js.map} +0 -0
  68. /package/dist/{chunk-GCYFUTUC.js.map → chunk-FIS5RT6K.js.map} +0 -0
  69. /package/dist/{chunk-GSHW5VVD.js.map → chunk-GGL7R2L2.js.map} +0 -0
  70. /package/dist/{chunk-WIKMCJUR.js.map → chunk-JI3LQFJH.js.map} +0 -0
  71. /package/dist/{chunk-2BD7DG37.js.map → chunk-MBZAESQ3.js.map} +0 -0
  72. /package/dist/{chunk-VAEAGTEQ.js.map → chunk-QFKRE7AU.js.map} +0 -0
  73. /package/dist/{chunk-AGJKWOKV.js.map → chunk-RVT6U6PV.js.map} +0 -0
  74. /package/dist/{chunk-TFFZUFEP.js.map → chunk-VJYFXDCZ.js.map} +0 -0
  75. /package/dist/{chunk-QZRKNA5F.js.map → chunk-ZCMO46YY.js.map} +0 -0
@@ -457,12 +457,17 @@ async function buildEntityMentionIndex(
457
457
  Promise.all(storages.map((scopedStorage) => scopedStorage.readAllMemories())),
458
458
  readNativeChunks(config, recallNamespaces),
459
459
  ]);
460
- const entityFiles = entityFileSets.flat();
460
+ // Pair each entity with the alias table of the store it came from (#1534):
461
+ // canonical ids must reflect the owning store's aliases, never another
462
+ // namespace's.
463
+ const entityRecords = entityFileSets.flatMap((set, index) =>
464
+ set.map((entity) => ({ entity, aliases: storages[index]!.entityAliases })),
465
+ );
461
466
  const memories = memorySets.flat();
462
467
 
463
468
  const entities = new Map<string, EntityMentionIndexEntry>();
464
- for (const entity of entityFiles) {
465
- const canonicalId = normalizeEntityName(entity.name, entity.type);
469
+ for (const { entity, aliases } of entityRecords) {
470
+ const canonicalId = normalizeEntityName(entity.name, entity.type, aliases);
466
471
  const rawStructuredSections = entity.structuredSections ?? [];
467
472
  const rawBeliefLedgerFactKeys = beliefLedgerFactKeys(rawStructuredSections);
468
473
  const sanitizedFacts = entity.facts
@@ -65,7 +65,6 @@ import {
65
65
  StorageManager,
66
66
  ContentHashIndex,
67
67
  fingerprintEntityStructuredFacts,
68
- normalizeEntityName,
69
68
  normalizeAttributePairs,
70
69
  parseEntityFile,
71
70
  } from "./storage.js";
@@ -15791,7 +15790,7 @@ export class Orchestrator {
15791
15790
  const type = (entity as any)?.type;
15792
15791
  if (typeof name !== "string" || typeof type !== "string") continue;
15793
15792
  try {
15794
- const normalized = normalizeEntityName(name, type);
15793
+ const normalized = storage.normalizeEntityName(name, type);
15795
15794
  await storage.addEntityActivity(
15796
15795
  normalized,
15797
15796
  { date: today, note: "Mentioned in conversation" },
package/src/storage.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { access, readdir, readFile, stat, writeFile, mkdir, unlink, rename, appendFile, open } from "node:fs/promises";
2
- import { appendFileSync, createReadStream, mkdirSync, statSync } from "node:fs";
2
+ import { appendFileSync, createReadStream, mkdirSync, readFileSync, statSync } from "node:fs";
3
3
  import { createHash } from "node:crypto";
4
4
  import path from "node:path";
5
5
  import { log } from "./logger.js";
@@ -935,13 +935,6 @@ function inferCurrentStateStatus(
935
935
  return inferMemoryStatus(frontmatter, pathRel, fallbackStatus);
936
936
  }
937
937
 
938
- /**
939
- * Entity alias table loaded from the user's local config.
940
- * Populated by StorageManager.loadAliases() at startup.
941
- * Falls back to built-in structural aliases (e.g. "open-claw" → "openclaw").
942
- */
943
- let userAliases: Record<string, string> = {};
944
-
945
938
  /** Built-in aliases for common structural normalizations (no personal data) */
946
939
  const BUILTIN_ALIASES: Record<string, string> = {
947
940
  openclaw: "openclaw",
@@ -953,9 +946,17 @@ const BUILTIN_ALIASES: Record<string, string> = {
953
946
  * Strips non-alphanumeric chars, collapses hyphens, removes type prefix duplication.
954
947
  * e.g. "My Project" → "my-project"
955
948
  *
956
- * Checks user-defined aliases (from config/aliases.json) first, then built-in aliases.
949
+ * Checks caller-provided user aliases first, then built-in aliases. Alias
950
+ * tables are instance state on StorageManager (issue #1534) — use
951
+ * `storageManager.normalizeEntityName(raw, type)` when normalizing within a
952
+ * store, or pass `storageManager.entityAliases` explicitly. Without an
953
+ * aliases argument only the built-in structural aliases apply.
957
954
  */
958
- export function normalizeEntityName(raw: string, type: string): string {
955
+ export function normalizeEntityName(
956
+ raw: string,
957
+ type: string,
958
+ aliases?: Readonly<Record<string, string>>,
959
+ ): string {
959
960
  // Strip type prefix if present (e.g. name="person-jane-doe", type="person")
960
961
  const rawStr = typeof raw === "string" ? raw : "";
961
962
  const typeStr = typeof type === "string" && type.trim().length > 0 ? type : "entity";
@@ -972,10 +973,14 @@ export function normalizeEntityName(raw: string, type: string): string {
972
973
  .replace(/-+/g, "-")
973
974
  .replace(/^-|-$/g, "");
974
975
 
975
- // Check user aliases first, then built-in
976
- if (userAliases[normalized]) {
977
- normalized = userAliases[normalized];
978
- } else if (BUILTIN_ALIASES[normalized]) {
976
+ // Check caller-provided user aliases first, then built-in. Own-property and
977
+ // string guards keep inherited object keys (e.g. an entity literally named
978
+ // "constructor") and malformed alias values from corrupting canonical ids.
979
+ const userAlias =
980
+ aliases !== undefined && Object.hasOwn(aliases, normalized) ? aliases[normalized] : undefined;
981
+ if (typeof userAlias === "string" && userAlias.length > 0) {
982
+ normalized = userAlias;
983
+ } else if (Object.hasOwn(BUILTIN_ALIASES, normalized)) {
979
984
  normalized = BUILTIN_ALIASES[normalized];
980
985
  }
981
986
 
@@ -2443,7 +2448,14 @@ export class StorageManager {
2443
2448
  constructor(
2444
2449
  private readonly baseDir: string,
2445
2450
  private readonly entitySchemas?: PluginConfig["entitySchemas"],
2446
- ) {}
2451
+ ) {
2452
+ // Load this store's alias table at construction (#1534): StorageManager
2453
+ // is created in a dozen places (namespace router, operator toolkit,
2454
+ // compounding engine, cold storage, ...) and most never call
2455
+ // loadAliases() explicitly — every creation path must still get the
2456
+ // store's own aliases, never an empty or foreign table.
2457
+ this.loadAliasesSync();
2458
+ }
2447
2459
 
2448
2460
  /** The root directory of this storage instance. */
2449
2461
  get dir(): string {
@@ -3197,18 +3209,59 @@ export class StorageManager {
3197
3209
  }
3198
3210
 
3199
3211
  /**
3200
- * Load user-defined entity aliases from config/aliases.json in the memory store.
3201
- * File format: { "variant": "canonical", "variant2": "canonical", ... }
3202
- * Call this once at startup (e.g. from orchestrator.initialize()).
3212
+ * Entity alias table loaded from THIS store's config/aliases.json.
3213
+ * Instance-scoped on purpose (issue #1534): multiple StorageManager
3214
+ * instances in one process (namespaces, hosted profiles, tenants) must
3215
+ * never share alias state — the previous module-level table let whichever
3216
+ * store loaded last rewrite every other store's canonical entity ids.
3217
+ */
3218
+ private userAliases: Record<string, string> = {};
3219
+
3220
+ /** Normalize an entity name using this store's alias table. */
3221
+ normalizeEntityName(raw: string, type: string): string {
3222
+ return normalizeEntityName(raw, type, this.userAliases);
3223
+ }
3224
+
3225
+ /**
3226
+ * Read-only view of this store's user alias table, for call sites that
3227
+ * normalize outside the manager (pass it to the free `normalizeEntityName`).
3228
+ */
3229
+ get entityAliases(): Readonly<Record<string, string>> {
3230
+ return this.userAliases;
3231
+ }
3232
+
3233
+ /**
3234
+ * Reload user-defined entity aliases from config/aliases.json in the memory
3235
+ * store. File format: { "variant": "canonical", ... }. The constructor
3236
+ * already loads aliases, so this is only needed to pick up file changes
3237
+ * (e.g. orchestrator.initialize() re-running on a live instance).
3238
+ * Non-object payloads and non-string or empty alias values are ignored.
3203
3239
  */
3204
3240
  async loadAliases(): Promise<void> {
3241
+ this.loadAliasesSync();
3242
+ }
3243
+
3244
+ private loadAliasesSync(): void {
3205
3245
  const aliasPath = path.join(this.baseDir, "config", "aliases.json");
3246
+ // Re-derive from the file on every call: a reload after the file was
3247
+ // fixed, emptied, or removed must never leave a previous table active.
3248
+ this.userAliases = {};
3206
3249
  try {
3207
- const raw = await readFile(aliasPath, "utf-8");
3250
+ const raw = readFileSync(aliasPath, "utf-8");
3208
3251
  const parsed = JSON.parse(raw);
3209
- if (typeof parsed === "object" && parsed !== null) {
3210
- userAliases = parsed as Record<string, string>;
3211
- log.debug(`loaded ${Object.keys(userAliases).length} entity aliases from ${aliasPath}`);
3252
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
3253
+ const cleaned: Record<string, string> = {};
3254
+ for (const [key, value] of Object.entries(parsed)) {
3255
+ if (typeof value === "string" && value.trim().length > 0) {
3256
+ cleaned[key] = value;
3257
+ }
3258
+ }
3259
+ this.userAliases = cleaned;
3260
+ log.debug(`loaded ${Object.keys(cleaned).length} entity aliases from ${aliasPath}`);
3261
+ } else {
3262
+ log.warn(
3263
+ `ignoring ${aliasPath}: payload must be a JSON object mapping variant → canonical strings`,
3264
+ );
3212
3265
  }
3213
3266
  } catch {
3214
3267
  // No aliases file — that's fine, use built-in only
@@ -3679,7 +3732,7 @@ export class StorageManager {
3679
3732
  .filter((fact) => fact.length > 0),
3680
3733
  )]
3681
3734
  : [];
3682
- let normalized = normalizeEntityName(name, type);
3735
+ let normalized = this.normalizeEntityName(name, type);
3683
3736
 
3684
3737
  // Check for fuzzy match against existing entities before creating a new file
3685
3738
  const match = await this.findMatchingEntity(name, type);
@@ -4648,7 +4701,7 @@ export class StorageManager {
4648
4701
 
4649
4702
  const typePrefix = `${type.toLowerCase()}-`;
4650
4703
  // Extract the name part from the proposed normalized name
4651
- const proposedFull = normalizeEntityName(proposedName, type);
4704
+ const proposedFull = this.normalizeEntityName(proposedName, type);
4652
4705
  const proposedNamePart = proposedFull.startsWith(typePrefix)
4653
4706
  ? proposedFull.slice(typePrefix.length)
4654
4707
  : proposedFull;
@@ -6104,7 +6157,7 @@ export class StorageManager {
6104
6157
  entity.updated = entityUpdatedAt;
6105
6158
  await this.writeStorageSecureFile(filePath, serializeEntityFile(entity, this.entitySchemas));
6106
6159
  await this.removeEntitySynthesisQueueEntries([
6107
- ...new Set([name, normalizeEntityName(entity.name, entity.type)]),
6160
+ ...new Set([name, this.normalizeEntityName(entity.name, entity.type)]),
6108
6161
  ]);
6109
6162
  this.invalidateKnowledgeIndexCache();
6110
6163
  this.bumpMemoryStatusVersion(); // invalidate entity cache
@@ -6425,7 +6478,7 @@ export class StorageManager {
6425
6478
  if (dashIdx === -1) continue;
6426
6479
  const type = baseName.slice(0, dashIdx);
6427
6480
  const restOfName = baseName.slice(dashIdx + 1);
6428
- const canonical = normalizeEntityName(restOfName, type);
6481
+ const canonical = this.normalizeEntityName(restOfName, type);
6429
6482
 
6430
6483
  if (!groups.has(canonical)) groups.set(canonical, []);
6431
6484
  groups.get(canonical)!.push(file);
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/entity-retrieval.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { sanitizeMemoryContent } from \"./sanitize.js\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { collectNativeKnowledgeChunks, type NativeKnowledgeChunk } from \"./native-knowledge.js\";\nimport { compareEntityTimestamps, normalizeEntityName, type StorageManager } from \"./storage.js\";\nimport { normalizeEntityText, resolveRequestedEntitySectionKeys } from \"./entity-schema.js\";\nimport type { EntityStructuredSection, MemoryFile, PluginConfig, TranscriptEntry } from \"./types.js\";\n\nconst ENTITY_INDEX_VERSION = 3;\nconst RECENT_TRANSCRIPT_LOOKBACK_HOURS = 24;\nconst INSTRUCTION_LIKE_RE = /\\b(always|never|must|should|remember to|do not|don't|process|workflow|template|checklist|instruction)\\b/i;\nconst METADATA_WRAPPER_RE = /^(source|context|metadata|notes?):/i;\nconst ENTITY_PRONOUN_RE = /\\b(he|him|his|she|her|they|them|their|it|its)\\b/i;\nconst BELIEF_LEDGER_SECTION_KEY = \"belief_ledger\";\nconst BELIEF_LEDGER_FACT_RE = /^claim=([^;]+);\\s*status=([^;]+);\\s*updatedAt=([^;]+);\\s*(.+)$/;\n\ntype EntityQueryMode = \"direct\" | \"timeline\" | \"follow_up\";\n\ntype EntityMentionIndexEntry = {\n canonicalId: string;\n name: string;\n type: string;\n aliases: string[];\n summary?: string;\n facts: string[];\n timelineFacts: string[];\n structuredSections: EntityStructuredSection[];\n timeline: Array<{\n timestamp: string;\n text: string;\n source?: string;\n sessionKey?: string;\n principal?: string;\n }>;\n relationships: Array<{ target: string; label: string }>;\n activity: Array<{ date: string; note: string }>;\n factCount: number;\n memorySnippets: string[];\n nativeChunks: Array<{\n chunkId: string;\n title: string;\n sourceKind: NativeKnowledgeChunk[\"sourceKind\"];\n sourcePath: string;\n snippet: string;\n derivedDate?: string;\n }>;\n};\n\ntype EntityMentionIndex = {\n version: number;\n updatedAt: string;\n entities: EntityMentionIndexEntry[];\n};\n\ntype EntityCandidate = {\n entry: EntityMentionIndexEntry;\n alias: string;\n score: number;\n source: \"query\" | \"recent_turn\";\n};\n\ntype EntityHintSnippet = {\n text: string;\n score: number;\n kind: \"summary\" | \"fact\" | \"section\" | \"relationship\" | \"activity\" | \"memory\" | \"native\";\n};\n\nexport interface BuildEntityRecallSectionOptions {\n config: PluginConfig;\n storage: StorageManager;\n namespaceStorage?: (namespace: string) => Promise<StorageManager>;\n query: string;\n recallNamespaces?: string[];\n recentTurns: number;\n maxHints: number;\n maxSupportingFacts: number;\n maxRelatedEntities: number;\n maxChars: number;\n transcriptEntries: TranscriptEntry[];\n}\n\nfunction tokenize(value: string): string[] {\n return normalizeEntityText(value).split(/\\s+/).filter((token) => token.length >= 2);\n}\n\nfunction uniqueStrings(values: string[]): string[] {\n return [...new Set(values.map((value) => value.trim()).filter((value) => value.length > 0))];\n}\n\nfunction containsPhrase(haystack: string, needle: string): boolean {\n if (!needle) return false;\n const escaped = needle.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n return new RegExp(`(^|\\\\b)${escaped}(\\\\b|$)`, \"i\").test(haystack);\n}\n\nfunction compactLine(value: string, maxLength: number = 220): string {\n const normalized = value.replace(/\\s+/g, \" \").trim();\n if (normalized.length <= maxLength) return normalized;\n return `${normalized.slice(0, Math.max(0, maxLength - 1)).trimEnd()}…`;\n}\n\nfunction dedupeHintSnippetsByText(snippets: EntityHintSnippet[]): EntityHintSnippet[] {\n const seen = new Set<string>();\n const result: EntityHintSnippet[] = [];\n for (const snippet of snippets) {\n const key = normalizeEntityText(snippet.text);\n if (seen.has(key)) continue;\n seen.add(key);\n result.push(snippet);\n }\n return result;\n}\n\nfunction isBeliefLedgerSection(section: Pick<EntityStructuredSection, \"key\">): boolean {\n return normalizeEntityText(section.key).replace(/\\s+/g, \"_\") === BELIEF_LEDGER_SECTION_KEY;\n}\n\nfunction beliefLedgerFactKeys(sections: EntityStructuredSection[]): Set<string> {\n const keys = new Set<string>();\n for (const section of sections) {\n if (!isBeliefLedgerSection(section)) continue;\n for (const fact of section.facts) {\n keys.add(normalizeEntityText(fact));\n }\n }\n return keys;\n}\n\nfunction recallFactsForStructuredSection(section: EntityStructuredSection): string[] {\n if (!isBeliefLedgerSection(section)) return section.facts;\n return currentActiveBeliefLedgerFactTexts(section.facts);\n}\n\nfunction currentActiveBeliefLedgerFactTexts(facts: string[]): string[] {\n const byClaim = new Map<string, { status: string; updatedAtMs: number; texts: string[] }>();\n for (const fact of facts) {\n const match = BELIEF_LEDGER_FACT_RE.exec(fact.trim());\n if (!match) continue;\n const [, claimId, status, updatedAt, text] = match;\n const updatedAtMs = Date.parse(updatedAt);\n if (!claimId?.trim() || !status?.trim() || !Number.isFinite(updatedAtMs) || !text?.trim()) continue;\n const normalizedStatus = status.trim().toLowerCase();\n const normalizedClaimId = claimId.trim();\n const current = byClaim.get(normalizedClaimId);\n const inactiveTieWins =\n current &&\n updatedAtMs === current.updatedAtMs &&\n current.status === \"active\" &&\n normalizedStatus !== \"active\";\n if (!current || updatedAtMs > current.updatedAtMs || inactiveTieWins) {\n byClaim.set(normalizedClaimId, { status: normalizedStatus, updatedAtMs, texts: [text.trim()] });\n continue;\n }\n if (updatedAtMs === current.updatedAtMs && current.status === normalizedStatus) {\n current.texts.push(text.trim());\n }\n }\n const result: string[] = [];\n for (const current of byClaim.values()) {\n if (current.status === \"active\") {\n result.push(...current.texts);\n }\n }\n return result;\n}\n\nfunction relationLine(entry: EntityMentionIndexEntry, relationship: { target: string; label: string }): string {\n const normalizedLabel = relationship.label.replace(/\\s+/g, \" \").trim();\n if (normalizedLabel.length === 0) return `${entry.name} is connected to ${relationship.target}`;\n return `${entry.name} ${normalizedLabel} ${relationship.target}`;\n}\n\nfunction detectEntityQueryMode(query: string): EntityQueryMode | null {\n const normalized = normalizeEntityText(query);\n if (!normalized) return null;\n if (\n /^(what about|and what about|how about|what happened (with|to) (he|him|his|she|her|they|them|their|it|its)|did (he|she|they|it)|is (he|she|they|it)|was (he|she|they|it))\\b/.test(normalized)\n ) {\n return \"follow_up\";\n }\n if (\n /^(who is|who s|what do we know about|what does|tell me about|what can you tell me about|what s new with|what happened with|what happened to|status of|where is|how is)\\b/.test(normalized)\n ) {\n if (/^what does\\b/.test(normalized)) {\n if (/^what does (?:this|that|it|the|a|an|my|our|your|their)\\b/.test(normalized)) {\n return null;\n }\n if (\n /^what does [a-z0-9-]+ (?:error|warning|exception|failure|stack|trace|code|message|log)\\b/.test(normalized)\n && /\\b(mean|means|indicate|indicates|imply|implies)\\b/.test(normalized)\n ) {\n return null;\n }\n }\n return /what happened|what s new|status of|how is|where is/.test(normalized) ? \"timeline\" : \"direct\";\n }\n if (ENTITY_PRONOUN_RE.test(normalized) && normalized.split(/\\s+/).length <= 8) {\n return \"follow_up\";\n }\n return null;\n}\n\nfunction scoreAliasMatch(query: string, alias: string): number {\n const normalizedQuery = normalizeEntityText(query);\n const normalizedAlias = normalizeEntityText(alias);\n if (!normalizedAlias) return 0;\n if (normalizedQuery === normalizedAlias) return 10;\n if (containsPhrase(normalizedQuery, normalizedAlias)) return 8 + Math.min(normalizedAlias.split(/\\s+/).length, 3);\n const queryTokens = new Set(tokenize(normalizedQuery));\n const aliasTokens = tokenize(normalizedAlias);\n if (aliasTokens.length === 0) return 0;\n const overlap = aliasTokens.filter((token) => queryTokens.has(token)).length;\n if (overlap === 0) return 0;\n // Cross-entity contamination guard (#682 PR 2/3 R-2). For multi-token\n // aliases, partial-token overlap that hits ONLY shared common tokens\n // (like \"person\" in \"Person-A1\" / \"Person-B1\") would surface every\n // similarly-prefixed entity for a query that named only one. Require\n // that the overlap include at least one DISTINCTIVE alias token —\n // i.e. a token that does not also appear in any other entity's\n // alias-token vocabulary. Since this function is pure / per-pair, we\n // approximate \"distinctive\" by requiring the overlap to include at\n // least one token that is not a substring-of common identifier\n // affixes. The aliasTokens with length > 1 must contribute at least\n // one non-affix overlap. \"Alice Example\" ([\"alice\", \"example\"])\n // matched by query \"Tell me about Alice\" → overlap = 1 on \"alice\",\n // and \"alice\" is not an affix → score = 1. \"Person-A1\" tokens\n // [\"person\", \"a1\"] matched by query \"Who is Person-B1?\" → overlap =\n // 1 on \"person\", and \"person\" IS an affix → score = 0.\n if (aliasTokens.length > 1) {\n const nonAffixOverlap = aliasTokens.filter(\n (token) => queryTokens.has(token) && !isAliasAffixToken(token),\n ).length;\n if (nonAffixOverlap === 0) return 0;\n }\n return overlap;\n}\n\n/**\n * Tokens that look like type prefixes / generic role identifiers and\n * therefore should not, on their own, count as evidence that a\n * multi-token alias matches a query (#682 PR 2/3 R-2).\n *\n * Kept as a small explicit list rather than a regex so additions are\n * deliberate. The list mirrors the entity types listed in\n * `entity-schema.ts` plus the most common generic role identifiers\n * that frequently appear as the leading token of a display name.\n */\nconst ALIAS_AFFIX_TOKENS = new Set<string>([\n \"person\", \"people\", \"user\", \"users\", \"team\", \"teams\",\n \"project\", \"projects\", \"topic\", \"topics\",\n \"org\", \"orgs\", \"organization\", \"organizations\", \"company\", \"companies\",\n \"place\", \"places\", \"tool\", \"tools\", \"service\", \"services\",\n \"system\", \"systems\", \"agent\", \"agents\", \"bot\", \"bots\",\n]);\n\nfunction isAliasAffixToken(token: string): boolean {\n return ALIAS_AFFIX_TOKENS.has(token);\n}\n\nfunction isLikelyInstructionLike(value: string): boolean {\n return INSTRUCTION_LIKE_RE.test(value) || METADATA_WRAPPER_RE.test(value);\n}\n\nfunction sanitizeEntityFact(fact: string): string {\n const sanitized = sanitizeMemoryContent(fact);\n const clean = sanitized.text.trim();\n if (!clean) return \"\";\n if (INSTRUCTION_LIKE_RE.test(clean) && clean.length > 100) return \"\";\n return clean;\n}\n\nfunction scoreHintSnippet(snippet: EntityHintSnippet, queryTokens: string[]): EntityHintSnippet | null {\n const normalized = normalizeEntityText(snippet.text);\n if (!normalized) return null;\n const scored = { ...snippet };\n if (isLikelyInstructionLike(scored.text) && scored.kind !== \"summary\") {\n scored.score -= 3;\n }\n const overlap = queryTokens.filter((token) => normalized.includes(token)).length;\n scored.score += overlap * 2;\n if (METADATA_WRAPPER_RE.test(scored.text)) scored.score -= 2;\n if (scored.text.length <= 160) scored.score += 1;\n return scored.score > 0 ? scored : null;\n}\n\nfunction sortTimelineEntriesDesc(\n left: EntityMentionIndexEntry[\"timeline\"][number],\n right: EntityMentionIndexEntry[\"timeline\"][number],\n): number {\n const timestampOrder = compareEntityTimestamps(right.timestamp, left.timestamp);\n if (timestampOrder !== 0) {\n return timestampOrder;\n }\n return right.text.localeCompare(left.text);\n}\n\nfunction jaccardSimilarity(a: string, b: string): number {\n const aTokens = new Set(tokenize(a));\n const bTokens = new Set(tokenize(b));\n if (aTokens.size === 0 || bTokens.size === 0) return 0;\n let intersection = 0;\n for (const token of aTokens) {\n if (bTokens.has(token)) intersection += 1;\n }\n const union = new Set([...aTokens, ...bTokens]).size;\n return union === 0 ? 0 : intersection / union;\n}\n\nfunction buildAliasIndex(entries: EntityMentionIndexEntry[]): Map<string, EntityMentionIndexEntry[]> {\n const index = new Map<string, EntityMentionIndexEntry[]>();\n for (const entry of entries) {\n const aliases = uniqueStrings([entry.name, ...entry.aliases]).map(normalizeEntityText).filter(Boolean);\n for (const alias of aliases) {\n const existing = index.get(alias) ?? [];\n existing.push(entry);\n index.set(alias, existing);\n }\n }\n return index;\n}\n\nasync function readNativeChunks(\n config: PluginConfig,\n recallNamespaces?: string[],\n): Promise<NativeKnowledgeChunk[]> {\n if (!config.nativeKnowledge?.enabled) return [];\n return collectNativeKnowledgeChunks({\n workspaceDir: config.workspaceDir,\n memoryDir: config.memoryDir,\n config: config.nativeKnowledge,\n recallNamespaces: config.namespacesEnabled ? recallNamespaces : undefined,\n defaultNamespace: config.defaultNamespace,\n }).catch(() => []);\n}\n\nasync function resolveEntityIndexStorages(\n storage: StorageManager,\n config: PluginConfig,\n recallNamespaces?: string[],\n namespaceStorage?: (namespace: string) => Promise<StorageManager>,\n): Promise<StorageManager[]> {\n if (\n !config.namespacesEnabled ||\n !namespaceStorage ||\n !recallNamespaces ||\n recallNamespaces.length === 0\n ) {\n return [storage];\n }\n\n const storages: StorageManager[] = [];\n const seenDirs = new Set<string>();\n for (const namespace of uniqueStrings(recallNamespaces)) {\n try {\n const scopedStorage = await namespaceStorage(namespace);\n const storageDir = path.resolve(scopedStorage.dir);\n if (seenDirs.has(storageDir)) continue;\n seenDirs.add(storageDir);\n storages.push(scopedStorage);\n } catch {\n continue;\n }\n }\n\n return storages.length > 0 ? storages : [storage];\n}\n\nfunction entityIndexStatePath(storage: StorageManager): string {\n return path.join(storage.dir, \"state\", \"entity-mention-index.json\");\n}\n\nasync function readEntityIndexState(storage: StorageManager): Promise<EntityMentionIndex | null> {\n const raw = await readFile(entityIndexStatePath(storage), \"utf-8\").catch(() => \"\");\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as Partial<EntityMentionIndex>;\n if (parsed.version !== ENTITY_INDEX_VERSION || !Array.isArray(parsed.entities)) return null;\n return parsed as EntityMentionIndex;\n } catch {\n return null;\n }\n}\n\nasync function writeEntityIndexState(storage: StorageManager, index: EntityMentionIndex): Promise<void> {\n const statePath = entityIndexStatePath(storage);\n await mkdir(path.dirname(statePath), { recursive: true });\n const nextContent = JSON.stringify(index, null, 2) + \"\\n\";\n const currentContent = await readFile(statePath, \"utf-8\").catch(() => \"\");\n if (currentContent === nextContent) return;\n await writeFile(statePath, nextContent, \"utf-8\");\n}\n\nfunction nativePseudoCanonicalId(chunk: NativeKnowledgeChunk): string {\n return `native:${createHash(\"sha256\").update(chunk.sourcePath).digest(\"hex\").slice(0, 12)}`;\n}\n\nfunction createPseudoNativeEntry(chunk: NativeKnowledgeChunk): EntityMentionIndexEntry {\n const canonicalId = nativePseudoCanonicalId(chunk);\n return {\n canonicalId,\n name: chunk.title,\n type: chunk.sourceKind,\n aliases: uniqueStrings(chunk.aliases ?? []),\n facts: [],\n structuredSections: [],\n timelineFacts: [],\n timeline: [],\n relationships: [],\n activity: [],\n factCount: 0,\n memorySnippets: [],\n nativeChunks: [\n {\n chunkId: chunk.chunkId,\n title: chunk.title,\n sourceKind: chunk.sourceKind,\n sourcePath: chunk.sourcePath,\n snippet: compactLine(chunk.content, 180),\n derivedDate: chunk.derivedDate,\n },\n ],\n };\n}\n\nfunction mergeNativeChunk(entry: EntityMentionIndexEntry, chunk: NativeKnowledgeChunk): void {\n const existing = entry.nativeChunks.find((item) => item.chunkId === chunk.chunkId);\n if (existing) return;\n entry.nativeChunks.push({\n chunkId: chunk.chunkId,\n title: chunk.title,\n sourceKind: chunk.sourceKind,\n sourcePath: chunk.sourcePath,\n snippet: compactLine(chunk.content, 180),\n derivedDate: chunk.derivedDate,\n });\n entry.aliases = uniqueStrings([...entry.aliases, ...(chunk.aliases ?? [])]);\n}\n\nasync function buildEntityMentionIndex(\n storage: StorageManager,\n config: PluginConfig,\n recallNamespaces?: string[],\n namespaceStorage?: (namespace: string) => Promise<StorageManager>,\n): Promise<EntityMentionIndex> {\n const storages = await resolveEntityIndexStorages(\n storage,\n config,\n recallNamespaces,\n namespaceStorage,\n );\n const shouldPersistIndex =\n storages.length === 1 && path.resolve(storages[0]!.dir) === path.resolve(storage.dir);\n const [previousIndex, entityFileSets, memorySets, nativeChunks] = await Promise.all([\n shouldPersistIndex ? readEntityIndexState(storage) : Promise.resolve(null),\n Promise.all(storages.map((scopedStorage) => scopedStorage.readAllEntityFiles())),\n Promise.all(storages.map((scopedStorage) => scopedStorage.readAllMemories())),\n readNativeChunks(config, recallNamespaces),\n ]);\n const entityFiles = entityFileSets.flat();\n const memories = memorySets.flat();\n\n const entities = new Map<string, EntityMentionIndexEntry>();\n for (const entity of entityFiles) {\n const canonicalId = normalizeEntityName(entity.name, entity.type);\n const rawStructuredSections = entity.structuredSections ?? [];\n const rawBeliefLedgerFactKeys = beliefLedgerFactKeys(rawStructuredSections);\n const sanitizedFacts = entity.facts\n .map((fact) => sanitizeEntityFact(fact))\n .filter(Boolean)\n .filter((fact) => !rawBeliefLedgerFactKeys.has(normalizeEntityText(fact)))\n .map((fact) => compactLine(fact, 180));\n const sanitizedTimelineFacts = entity.timeline\n .map((entry) => sanitizeEntityFact(entry.text))\n .filter(Boolean)\n .map((fact) => compactLine(fact, 180));\n entities.set(canonicalId, {\n canonicalId,\n name: entity.name,\n type: entity.type,\n aliases: uniqueStrings(entity.aliases),\n summary: entity.synthesis?.trim() || entity.summary?.trim() || undefined,\n facts: sanitizedFacts,\n timelineFacts: uniqueStrings(sanitizedTimelineFacts),\n structuredSections: rawStructuredSections.map((section) => ({\n key: section.key,\n title: section.title,\n facts: recallFactsForStructuredSection(section)\n .map((fact) => sanitizeEntityFact(fact))\n .filter(Boolean)\n .map((fact) => compactLine(fact, 180)),\n })).filter((section) => section.facts.length > 0),\n timeline: entity.timeline.map((entry) => ({ ...entry })),\n relationships: entity.relationships.map((relationship) => ({ ...relationship })),\n activity: entity.activity.map((activity) => ({ ...activity })),\n factCount: sanitizedFacts.length,\n memorySnippets: [],\n nativeChunks: [],\n });\n }\n\n for (const memory of memories) {\n const entityRef = typeof memory.frontmatter.entityRef === \"string\" ? memory.frontmatter.entityRef : \"\";\n if (!entityRef) continue;\n const entry = entities.get(entityRef);\n if (!entry) continue;\n const snippet = await readMemorySnippet(memory);\n if (!entry.memorySnippets.includes(snippet)) {\n entry.memorySnippets.push(snippet);\n }\n }\n\n const aliasIndex = buildAliasIndex([...entities.values()]);\n for (const chunk of nativeChunks) {\n const existingPseudo = entities.get(nativePseudoCanonicalId(chunk));\n if (existingPseudo) {\n mergeNativeChunk(existingPseudo, chunk);\n continue;\n }\n const candidateAliases = uniqueStrings([chunk.title, ...(chunk.aliases ?? [])]).map(normalizeEntityText).filter(Boolean);\n let matched = false;\n for (const alias of candidateAliases) {\n for (const entry of aliasIndex.get(alias) ?? []) {\n mergeNativeChunk(entry, chunk);\n matched = true;\n }\n }\n if (matched) continue;\n const pseudoEntry = createPseudoNativeEntry(chunk);\n entities.set(pseudoEntry.canonicalId, pseudoEntry);\n }\n\n const sortedEntities = [...entities.values()].sort((left, right) => left.name.localeCompare(right.name));\n const previousEntities = previousIndex ? JSON.stringify(previousIndex.entities) : \"\";\n const nextEntities = JSON.stringify(sortedEntities);\n const index: EntityMentionIndex = {\n version: ENTITY_INDEX_VERSION,\n updatedAt:\n previousIndex && previousEntities === nextEntities\n ? previousIndex.updatedAt\n : new Date().toISOString(),\n entities: sortedEntities,\n };\n if (shouldPersistIndex) {\n await writeEntityIndexState(storage, index);\n }\n return index;\n}\n\nfunction resolveExplicitCandidates(\n index: EntityMentionIndex,\n query: string,\n): EntityCandidate[] {\n const candidates: EntityCandidate[] = [];\n for (const entry of index.entities) {\n const aliases = uniqueStrings([entry.name, ...entry.aliases]);\n let bestAlias = \"\";\n let bestScore = 0;\n for (const alias of aliases) {\n const score = scoreAliasMatch(query, alias);\n if (score > bestScore) {\n bestAlias = alias;\n bestScore = score;\n }\n }\n if (bestScore <= 0) continue;\n candidates.push({ entry, alias: bestAlias, score: bestScore, source: \"query\" });\n }\n return candidates.sort((left, right) => right.score - left.score);\n}\n\nfunction resolveRecentTurnCandidates(\n index: EntityMentionIndex,\n transcriptEntries: TranscriptEntry[],\n recentTurns: number,\n): EntityCandidate[] {\n if (recentTurns <= 0 || transcriptEntries.length === 0) return [];\n const recentEntries = transcriptEntries.slice(-recentTurns);\n const candidates = new Map<string, EntityCandidate>();\n for (let indexOffset = recentEntries.length - 1; indexOffset >= 0; indexOffset -= 1) {\n const turn = recentEntries[indexOffset];\n const recencyBoost = recentEntries.length - indexOffset;\n const roleWeight = turn.role === \"user\" ? 2 : turn.role === \"assistant\" ? -1 : 0;\n for (const entry of index.entities) {\n const aliases = uniqueStrings([entry.name, ...entry.aliases]);\n for (const alias of aliases) {\n const score = scoreAliasMatch(turn.content, alias);\n if (score <= 0) continue;\n const current = candidates.get(entry.canonicalId);\n const weightedScore = score + Math.max(0, 6 - recencyBoost) + roleWeight;\n if (!current || weightedScore > current.score) {\n candidates.set(entry.canonicalId, {\n entry,\n alias,\n score: weightedScore,\n source: \"recent_turn\",\n });\n }\n }\n }\n }\n return [...candidates.values()].sort((left, right) => right.score - left.score);\n}\n\nasync function readMemorySnippet(memory: MemoryFile): Promise<string> {\n const content = memory.content.replace(/\\s+/g, \" \").trim();\n return compactLine(content, 180);\n}\n\nasync function buildHintSnippets(\n entry: EntityMentionIndexEntry,\n queryTokens: string[],\n mode: EntityQueryMode,\n maxSupportingFacts: number,\n requestedSectionKeys: Set<string>,\n): Promise<EntityHintSnippet[]> {\n const snippets: EntityHintSnippet[] = [];\n const aliasTokens = new Set(tokenize(uniqueStrings([entry.name, ...entry.aliases]).join(\" \")));\n if (entry.summary) {\n snippets.push({ text: compactLine(entry.summary, 180), score: 10, kind: \"summary\" });\n }\n\n if (requestedSectionKeys.size > 0) {\n for (const section of entry.structuredSections) {\n if (!requestedSectionKeys.has(normalizeEntityText(section.key).replace(/\\s+/g, \"_\"))) continue;\n for (const fact of section.facts) {\n snippets.push({ text: fact, score: mode === \"direct\" ? 8 : 9, kind: \"section\" });\n }\n }\n } else {\n for (const fact of entry.timelineFacts) {\n snippets.push({ text: fact, score: mode === \"direct\" ? 6 : 7, kind: \"fact\" });\n }\n for (const section of entry.structuredSections) {\n for (const fact of section.facts) {\n const normalizedFact = normalizeEntityText(fact);\n const hasNonAliasQueryOverlap = queryTokens.some((token) =>\n !aliasTokens.has(token) && normalizedFact.includes(token)\n );\n if (entry.timelineFacts.length > 0 && !hasNonAliasQueryOverlap) {\n continue;\n }\n snippets.push({ text: fact, score: mode === \"direct\" ? 6 : 7, kind: \"fact\" });\n }\n }\n if (entry.timelineFacts.length === 0 && entry.structuredSections.length === 0) {\n for (const fact of entry.facts) {\n if (!fact.trim()) continue;\n snippets.push({ text: fact, score: mode === \"direct\" ? 6 : 7, kind: \"fact\" });\n }\n }\n }\n\n if (requestedSectionKeys.size === 0) {\n for (const relationship of entry.relationships) {\n snippets.push({\n text: compactLine(relationLine(entry, relationship), 180),\n score: mode === \"direct\" && entry.type.toLowerCase() === \"person\" ? 6 : 4,\n kind: \"relationship\",\n });\n }\n\n for (const activity of entry.activity) {\n snippets.push({\n text: compactLine(`${activity.date}: ${activity.note}`, 180),\n score: 4,\n kind: \"activity\",\n });\n }\n\n for (const memorySnippet of entry.memorySnippets.slice(0, Math.min(maxSupportingFacts, 4))) {\n snippets.push({\n text: memorySnippet,\n score: 5,\n kind: \"memory\",\n });\n }\n\n for (const chunk of entry.nativeChunks) {\n snippets.push({\n text: compactLine(chunk.snippet, 180),\n score: 3,\n kind: \"native\",\n });\n }\n }\n\n const deduped = new Map<string, EntityHintSnippet>();\n for (const snippet of snippets) {\n const scored = scoreHintSnippet(snippet, queryTokens);\n if (!scored) continue;\n const normalized = normalizeEntityText(scored.text);\n const existing = deduped.get(normalized);\n if (!existing || scored.score > existing.score) deduped.set(normalized, scored);\n }\n\n return [...deduped.values()]\n .filter((snippet) => snippet.score > 0)\n .sort((left, right) => right.score - left.score)\n .slice(0, maxSupportingFacts);\n}\n\nfunction summarizeUncertainty(snippets: EntityHintSnippet[]): string | null {\n const direct = snippets.filter((snippet) =>\n snippet.kind === \"summary\"\n || snippet.kind === \"fact\"\n || snippet.kind === \"section\"\n || snippet.kind === \"memory\"\n );\n if (direct.length < 2) return null;\n for (let index = 0; index < direct.length; index += 1) {\n for (let compare = index + 1; compare < direct.length; compare += 1) {\n if (jaccardSimilarity(direct[index].text, direct[compare].text) < 0.2) {\n return \"Evidence is mixed across stored facts; treat the hints below as partial and verify before answering definitively.\";\n }\n }\n }\n return null;\n}\n\nfunction formatEntityHintSection(\n candidates: Array<{\n candidate: EntityCandidate;\n snippets: EntityHintSnippet[];\n uncertainty: string | null;\n }>,\n queryTokens: string[],\n mode: EntityQueryMode,\n maxRelatedEntities: number,\n maxChars: number,\n): string | null {\n if (candidates.length === 0) return null;\n const lines: string[] = [\"## entity_answer_hints\", \"\"];\n for (const { candidate, snippets, uncertainty } of candidates) {\n const hasSummary = Boolean(candidate.entry.summary?.trim());\n const preferredTopSnippets = hasSummary\n ? snippets.filter((snippet) => snippet.kind !== \"fact\")\n : snippets;\n let topSnippets = (\n preferredTopSnippets.length > 0 ? preferredTopSnippets : snippets\n ).slice(0, 3);\n const buildTimelineSnippets = (seedExcludedTexts: Set<string>): EntityHintSnippet[] => {\n const explicitTimelinePool = dedupeHintSnippetsByText(\n (candidate.entry.timeline ?? [])\n .slice()\n .sort(sortTimelineEntriesDesc)\n .map((entry) => sanitizeEntityFact(entry.text))\n .filter(Boolean)\n .map((text) => scoreHintSnippet({\n text: compactLine(text, 180),\n score: 7,\n kind: \"activity\" as const,\n }, queryTokens))\n .filter((snippet): snippet is EntityHintSnippet => snippet !== null)\n .filter((snippet) => !seedExcludedTexts.has(normalizeEntityText(snippet.text))),\n ).slice(0, 2);\n const activityTimelinePool = dedupeHintSnippetsByText(\n snippets\n .filter((snippet) => (\n snippet.kind === \"activity\" || snippet.kind === \"memory\"\n ) && !seedExcludedTexts.has(normalizeEntityText(snippet.text))),\n ).slice(0, 2);\n return explicitTimelinePool.length > 0\n ? explicitTimelinePool\n : activityTimelinePool.length > 0\n ? activityTimelinePool\n : dedupeHintSnippetsByText(\n snippets\n .filter((snippet) => (\n snippet.kind === \"fact\" || snippet.kind === \"summary\"\n ) && !seedExcludedTexts.has(normalizeEntityText(snippet.text))),\n ).slice(0, 2);\n };\n const baseTopSnippetTexts = new Set(topSnippets.map((snippet) => normalizeEntityText(snippet.text)));\n const timelinePool = mode !== \"direct\" ? buildTimelineSnippets(baseTopSnippetTexts) : [];\n if (mode !== \"direct\" && hasSummary && topSnippets.length < 2) {\n if (timelinePool.length > 0) {\n topSnippets = [...topSnippets, timelinePool[0]!].slice(0, 3);\n }\n }\n const topSnippetTexts = new Set(topSnippets.map((snippet) => normalizeEntityText(snippet.text)));\n lines.push(`- target: ${candidate.entry.name} (${candidate.entry.type})`);\n if (candidate.source === \"recent_turn\") {\n lines.push(`- resolution: carried forward from recent turns via alias \"${candidate.alias}\"`);\n } else {\n lines.push(`- resolution: matched alias \"${candidate.alias}\" in the query`);\n }\n if (uncertainty) lines.push(`- uncertainty: ${uncertainty}`);\n if (topSnippets.length > 0) {\n lines.push(\"- likely answer:\");\n for (const snippet of topSnippets) {\n lines.push(` - ${snippet.text}`);\n }\n }\n if (mode !== \"direct\") {\n const fallbackTimeline = timelinePool.filter(\n (snippet) => !topSnippetTexts.has(normalizeEntityText(snippet.text)),\n );\n if (fallbackTimeline.length > 0) {\n lines.push(\"- recent timeline:\");\n for (const snippet of fallbackTimeline) {\n lines.push(` - ${snippet.text}`);\n }\n }\n }\n const related = candidate.entry.relationships.slice(0, maxRelatedEntities).map((relationship) => relationship.target);\n if (related.length > 0) {\n lines.push(`- related entities: ${related.join(\", \")}`);\n }\n lines.push(`- support counts: facts=${candidate.entry.factCount}, memories=${candidate.entry.memorySnippets.length}, native=${candidate.entry.nativeChunks.length}`);\n lines.push(\"\");\n }\n\n let result = lines.join(\"\\n\");\n if (result.length > maxChars) {\n result = `${result.slice(0, Math.max(0, maxChars - 15)).trimEnd()}\\n\\n...(trimmed)\\n`;\n }\n return result.trim().length > 0 ? result.trimEnd() : null;\n}\n\nexport async function buildEntityRecallSection(options: BuildEntityRecallSectionOptions): Promise<string | null> {\n const mode = detectEntityQueryMode(options.query);\n if (!mode) return null;\n\n const index = await buildEntityMentionIndex(\n options.storage,\n options.config,\n options.recallNamespaces,\n options.namespaceStorage,\n );\n if (index.entities.length === 0) return null;\n\n const explicitCandidates = resolveExplicitCandidates(index, options.query);\n const candidates = explicitCandidates.length > 0\n ? explicitCandidates\n : resolveRecentTurnCandidates(index, options.transcriptEntries, options.recentTurns);\n\n if (candidates.length === 0) return null;\n\n const queryTokens = tokenize(options.query);\n const candidateLimit = explicitCandidates.length === 0 && mode === \"follow_up\"\n ? 1\n : options.maxHints;\n const rankedCandidates = candidates.slice(0, candidateLimit);\n const enriched = await Promise.all(\n rankedCandidates.map(async (candidate) => {\n const requestedSectionKeys = new Set(\n resolveRequestedEntitySectionKeys(\n options.query,\n candidate.entry.type,\n candidate.entry.structuredSections,\n options.config.entitySchemas,\n ),\n );\n const snippets = await buildHintSnippets(\n candidate.entry,\n queryTokens,\n mode,\n options.maxSupportingFacts,\n requestedSectionKeys,\n );\n return {\n candidate,\n snippets,\n uncertainty: summarizeUncertainty(snippets),\n };\n }),\n );\n\n const section = formatEntityHintSection(enriched, queryTokens, mode, options.maxRelatedEntities, options.maxChars);\n if (!section) return null;\n return section;\n}\n\nexport async function readRecentEntityTranscriptEntries(\n transcriptEntriesPromise: Promise<TranscriptEntry[]>,\n recentTurns: number,\n): Promise<TranscriptEntry[]> {\n if (recentTurns <= 0) return [];\n const transcriptEntries = await transcriptEntriesPromise.catch(() => []);\n if (transcriptEntries.length === 0) return [];\n return transcriptEntries.slice(-Math.max(1, recentTurns * 2));\n}\n\nexport const entityIndexVersion = ENTITY_INDEX_VERSION;\nexport const entityRecentTranscriptLookbackHours = RECENT_TRANSCRIPT_LOOKBACK_HOURS;\n"],"mappings":"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","names":[]}