@linxiraos/pi-mnemopi 1.1.23 → 1.1.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,7 +2,13 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
- ## [1.1.23] - 2026-10-01
5
+ ## [1.1.25] - 2026-10-03
6
+
7
+ - 版本线推进至 1.1.25;本版无独立用户可见变化。
8
+
9
+ ## [1.1.24] - 2026-10-03
10
+
11
+ - 上游 v18.4.11 集成;本轮无用户可见变更。
6
12
 
7
13
  ## [1.1.22] - 2026-09-30
8
14
 
@@ -1,7 +1,7 @@
1
- OMP THIRD-PARTY NOTICES
1
+ ZETA THIRD-PARTY NOTICES
2
2
  =======================
3
3
 
4
- This generated, self-contained aggregate accompanies OMP release artifacts.
4
+ This generated, self-contained aggregate accompanies Zeta release artifacts.
5
5
  It does not replace package-level license files. First-party licensing grants
6
6
  no rights in third-party code, fonts, data, or marks identified below.
7
7
 
@@ -1046,7 +1046,7 @@ These packages participate only in generation/web build tooling and are not ship
1046
1046
 
1047
1047
  SCOPED NON-PERMISSIVE PACKAGES
1048
1048
  ------------------------------
1049
- The exact @img/sharp-libvips-* 1.2.4 all-target family is LGPL-3.0-or-later. It is not distributed in OMP npm bundles or standalone assets: @huggingface/transformers is always external, installed into a user-side versioned runtime on demand, and sharp is stubbed for speech/text. The dynamically loaded native library remains replaceable. Corresponding source is available from https://github.com/lovell/sharp-libvips.git and upstream libvips sources.
1049
+ The exact @img/sharp-libvips-* 1.2.4 all-target family is LGPL-3.0-or-later. It is not distributed in Zeta npm bundles or standalone assets: @huggingface/transformers is always external, installed into a user-side versioned runtime on demand, and sharp is stubbed for speech/text. The dynamically loaded native library remains replaceable. Corresponding source is available from https://github.com/lovell/sharp-libvips.git and upstream libvips sources.
1050
1050
 
1051
1051
  lightningcss 1.32.0 and its exact target leaves are MPL-2.0 build-only dependencies. MPL is not globally allowed and these packages are absent from runtime artifacts.
1052
1052
 
@@ -80,16 +80,25 @@ export interface RecallFeatureFlags {
80
80
  proactiveLinking?: boolean;
81
81
  }
82
82
  /**
83
- * Sets process-wide defaults for the env-gated recall features. Host configuration
84
- * (e.g. the coding-agent `mnemopi.polyphonicRecall` / `mnemopi.enhancedRecall` /
85
- * `mnemopi.proactiveLinking` settings) lands here; the `MNEMOPI_POLYPHONIC_RECALL` /
86
- * `MNEMOPI_ENHANCED_RECALL` / `MNEMOPI_PROACTIVE_LINKING` environment variables still
87
- * win whenever they are set.
83
+ * Sets process-wide defaults for the env-gated recall features. Hosts that open
84
+ * several memories with different policies should pass the per-instance
85
+ * `polyphonicRecall` / `enhancedRecall` / `proactiveLinking` options instead
86
+ * (`Mnemopi` / `BeamMemory` constructors), which take precedence over these
87
+ * defaults. The `MNEMOPI_POLYPHONIC_RECALL` / `MNEMOPI_ENHANCED_RECALL` /
88
+ * `MNEMOPI_PROACTIVE_LINKING` environment variables still win whenever they are set.
88
89
  */
89
90
  export declare function configureRecallFeatures(flags: RecallFeatureFlags): void;
90
- export declare function polyphonicRecallEnabled(env?: Env): boolean;
91
+ /**
92
+ * Resolves the polyphonic recall gate: the env var wins when set, then the
93
+ * per-instance `configured` value, then the process-wide default.
94
+ */
95
+ export declare function polyphonicRecallEnabled(env?: Env, configured?: boolean): boolean;
91
96
  export declare function temporalHalflifeHours(env?: Env): number;
92
- export declare function enhancedRecallEnabled(env?: Env): boolean;
97
+ /**
98
+ * Resolves the enhanced recall (query result cache) gate: the env var wins when
99
+ * set, then the per-instance `configured` value, then the process-wide default.
100
+ */
101
+ export declare function enhancedRecallEnabled(env?: Env, configured?: boolean): boolean;
93
102
  export declare function proactiveLinkingEnabled(env?: Env): boolean;
94
103
  export declare function llmEnabled(env?: Env): boolean;
95
104
  export declare function llmMaxTokens(env?: Env): number;
@@ -1,4 +1,5 @@
1
1
  import { type ExtractedFactCategories } from "../extraction.js";
2
+ import { VeracityConsolidator } from "../veracity-consolidation.js";
2
3
  import type { BeamMemoryState, BeamStats, JsonValue, MemoriaRetrieveResult, Metadata, SleepResult } from "./types.js";
3
4
  type Row = Record<string, unknown>;
4
5
  type FactCounts = {
@@ -17,6 +18,18 @@ type ConsolidateOptions = {
17
18
  scope?: string;
18
19
  veracity?: string | null;
19
20
  };
21
+ /**
22
+ * This beam's veracity consolidator, created on first use so `consolidated_facts` /
23
+ * `conflicts` DDL only runs once polyphonic recall needs them.
24
+ */
25
+ export declare function ensureVeracityConsolidator(beam: BeamMemoryState): VeracityConsolidator;
26
+ /**
27
+ * Consolidate every extracted KG fact (`memoria_kg`) whose source memory is not yet
28
+ * among that fact's `consolidated_facts` sources, e.g. facts written while polyphonic
29
+ * recall was off. Idempotent: a (fact, source) pair is only ever counted once.
30
+ * Facts without a source memory are skipped; the fact voice could not return them.
31
+ */
32
+ export declare function backfillConsolidatedFacts(beam: BeamMemoryState, consolidator: VeracityConsolidator): number;
20
33
  export declare function consolidateToEpisodic(beam: BeamMemoryState, summary: string, sourceWmIds: readonly string[], source?: string, importance?: number, options?: ConsolidateOptions): string;
21
34
  export declare function detectLanguage(_beam: BeamMemoryState, text: string): string;
22
35
  type StoreFactStringOptions = {
@@ -1,4 +1,5 @@
1
1
  import type { Database } from "bun:sqlite";
2
+ import type { VeracityConsolidator } from "../veracity-consolidation.js";
2
3
  import type { BeamCaches, BeamConfig, BeamEvent, BeamMemoryOptions, BeamMemoryState, BeamStats, ImportStats, MemoriaRetrieveResult, Metadata, RecallEnhancedOptions, RecallOptions, RecallResult, RememberBatchItem, RememberBatchOptions, RememberOptions, SleepResult } from "./types.js";
3
4
  export { initBeam } from "./schema.js";
4
5
  export type * from "./types.js";
@@ -16,7 +17,8 @@ export declare class BeamMemory implements BeamMemoryState {
16
17
  readonly annotations: BeamMemoryState["annotations"];
17
18
  readonly triples: BeamMemoryState["triples"];
18
19
  readonly episodicGraph: unknown | null;
19
- readonly veracityConsolidator: unknown | null;
20
+ /** Created on first polyphonic use (see `ensureVeracityConsolidator`); `null` until then. */
21
+ veracityConsolidator: VeracityConsolidator | null;
20
22
  readonly caches: BeamCaches;
21
23
  readonly config: BeamConfig;
22
24
  readonly pendingExtractions: Set<Promise<void>>;
@@ -46,6 +46,18 @@ export declare function clipRecallContent(content: string, limit?: number): {
46
46
  export declare function parseQueryTime(value: RecallOptionsInternal["queryTime"]): Date;
47
47
  export declare function temporalBoost(timestamp: unknown, queryTime: Date, halfLifeHours: number): number;
48
48
  export declare function recall(beam: BeamMemoryState, query: string, topK?: number, options?: RecallOptionsInternal): Promise<RecallResult[]>;
49
+ /**
50
+ * Enhanced recall: linear hybrid recall with synonyms, intent weighting and MMR,
51
+ * optionally merged with extracted facts.
52
+ *
53
+ * Two opt-in layers sit on top, each gated per instance (`BeamConfig`) with the
54
+ * `MNEMOPI_POLYPHONIC_RECALL` / `MNEMOPI_ENHANCED_RECALL` env vars winning:
55
+ * - polyphonic recall fuses this ranking with the vector, graph, fact and temporal
56
+ * voices (see {@link orchestrateRecall});
57
+ * - the enhanced recall cache serves repeated or similar queries whose options all
58
+ * match, and is dropped by any database write.
59
+ * With both off the linear path runs unchanged.
60
+ */
49
61
  export declare function recallEnhanced(beam: BeamMemoryState, query: string, topK?: number, options?: RecallEnhancedOptions & RecallOptionsInternal): Promise<RecallResult[]>;
50
62
  export declare function formatContext(beam: BeamMemoryState, results: readonly RecallResult[], format?: string): string;
51
63
  export declare function factRecall(beam: BeamMemoryState, query: string, topK?: number): FactRecallResult[];
@@ -1,4 +1,5 @@
1
1
  import type { Database } from "bun:sqlite";
2
+ import type { QueryCache } from "../query-cache.js";
2
3
  export type JsonPrimitive = string | number | boolean | null;
3
4
  export type JsonValue = JsonPrimitive | JsonValue[] | {
4
5
  [key: string]: JsonValue;
@@ -27,6 +28,13 @@ export interface BeamCaches {
27
28
  polyphonicEngine?: unknown;
28
29
  extractionClient?: unknown;
29
30
  extractionBuffer: unknown[];
31
+ /** Enhanced-recall result cache; installed lazily while `enhancedRecall` is enabled. */
32
+ queryCache?: QueryCache<RecallResult>;
33
+ /**
34
+ * SQLite write token (`data_version` + `total_changes()`) the cached entries were
35
+ * computed against; any other write to the database drops the cache.
36
+ */
37
+ queryCacheToken?: string;
30
38
  [key: string]: unknown;
31
39
  }
32
40
  export interface BeamConfig {
@@ -40,6 +48,10 @@ export interface BeamConfig {
40
48
  localLlmEnabled: boolean;
41
49
  maxEpisodeChars: number;
42
50
  proactiveLinking?: boolean;
51
+ /** Per-instance polyphonic recall gate; `undefined` defers to the process-wide default. */
52
+ polyphonicRecall?: boolean;
53
+ /** Per-instance enhanced recall (query cache) gate; `undefined` defers to the process-wide default. */
54
+ enhancedRecall?: boolean;
43
55
  }
44
56
  export interface BeamMemoryOptions {
45
57
  sessionId?: string;
@@ -49,6 +61,10 @@ export interface BeamMemoryOptions {
49
61
  channelId?: string | null;
50
62
  useCloud?: boolean;
51
63
  proactiveLinking?: boolean;
64
+ /** Route `recallEnhanced` through polyphonic recall. `MNEMOPI_POLYPHONIC_RECALL` wins when set. */
65
+ polyphonicRecall?: boolean;
66
+ /** Cache `recallEnhanced` results per query and options. `MNEMOPI_ENHANCED_RECALL` wins when set. */
67
+ enhancedRecall?: boolean;
52
68
  eventEmitter?: (event: BeamEvent) => void;
53
69
  pluginManager?: BeamPluginManager | null;
54
70
  annotations?: AnnotationStoreLike | null;
@@ -159,6 +175,7 @@ export interface RecallOptions {
159
175
  contentPreviewChars?: number;
160
176
  }
161
177
  export interface RecallEnhancedOptions extends RecallOptions {
178
+ /** Set `false` to bypass the enhanced-recall result cache for this call. */
162
179
  useCache?: boolean;
163
180
  includeFacts?: boolean;
164
181
  }
@@ -17,6 +17,8 @@ export interface Fact {
17
17
  readonly timestamp: string;
18
18
  readonly confidence: number;
19
19
  readonly temporalQualifier?: string | null;
20
+ /** Memory the fact was extracted from (`facts.source_msg_id`), when stored. */
21
+ readonly memoryId?: string | null;
20
22
  }
21
23
  export interface GraphEdge {
22
24
  readonly source: string;
@@ -68,6 +70,12 @@ export declare class EpisodicGraph {
68
70
  addEdge(edge: GraphEdge): void;
69
71
  getEdges(source?: string | null): GraphEdge[];
70
72
  findRelatedMemories(memoryId: string, depth?: number, edgeType?: string, minWeight?: number): RelatedMemory[];
73
+ /**
74
+ * Edges touching any of `nodeIds` in one query, strongest first, at most `limit` rows.
75
+ * Batched frontier expansion for bounded traversals; see {@link findRelatedMemories}
76
+ * for the per-node walk.
77
+ */
78
+ findEdgesTouching(nodeIds: readonly string[], edgeType: string, minWeight: number, limit: number): GraphEdge[];
71
79
  findFactsBySubject(subject: string): Fact[];
72
80
  findGistsByParticipant(participant: string): Gist[];
73
81
  scoreMemoryLink(sourceMemoryId: string, targetMemoryId: string): number;
@@ -28,6 +28,16 @@ export interface MnemopiOptions {
28
28
  readonly llmModel?: string | Model<Api>;
29
29
  readonly llm?: false | MnemopiLlmRuntimeOptions | Model<Api> | MnemopiLlmCompletion;
30
30
  readonly proactiveLinking?: boolean;
31
+ /**
32
+ * Route `recallEnhanced` through polyphonic recall for this instance. Unset
33
+ * defers to `configureRecallFeatures`; `MNEMOPI_POLYPHONIC_RECALL` wins when set.
34
+ */
35
+ readonly polyphonicRecall?: boolean;
36
+ /**
37
+ * Cache `recallEnhanced` results for this instance. Unset defers to
38
+ * `configureRecallFeatures`; `MNEMOPI_ENHANCED_RECALL` wins when set.
39
+ */
40
+ readonly enhancedRecall?: boolean;
31
41
  /** Escalate best-effort failure logs (embedding pipeline) from debug to warn. */
32
42
  readonly debug?: boolean;
33
43
  /**
@@ -4,17 +4,20 @@ export interface OrchestratorBeam extends BeamMemoryState {
4
4
  recall?: (query: string, topK?: number, options?: RecallOptions) => Promise<RecallResult[]>;
5
5
  recallEnhanced?: (query: string, topK?: number, options?: RecallOptions) => Promise<RecallResult[]>;
6
6
  }
7
- export interface OrchestrateRecallOptions extends Omit<RecallOptions, "queryEmbedding">, Omit<PolyphonicRecallOptions, "queryEmbedding"> {
7
+ export interface OrchestrateRecallOptions extends Omit<RecallOptions, "queryEmbedding">, Omit<PolyphonicRecallOptions, "queryEmbedding" | "channelId" | "queryTime"> {
8
8
  readonly queryEmbedding?: readonly number[] | Float32Array | null;
9
9
  readonly enhanced?: boolean;
10
10
  readonly forcePolyphonic?: boolean;
11
11
  readonly forceLinear?: boolean;
12
12
  }
13
- export interface OrchestratedRecallResult extends Omit<RecallResult, "metadata" | "score" | "tier"> {
14
- score?: number;
15
- metadata?: RecallResult["metadata"];
16
- tier?: RecallResult["tier"] | PolyphonicMemoryResult["tier"];
13
+ /** Linear recall rows, or polyphonic rows that also carry `combined_score`. */
14
+ export interface OrchestratedRecallResult extends RecallResult {
17
15
  combined_score?: PolyphonicMemoryResult["combined_score"];
18
- voice_scores?: PolyphonicMemoryResult["voice_scores"];
19
16
  }
17
+ /**
18
+ * Pick the recall path for one query. Polyphonic recall runs when forced or when
19
+ * the gate is on for this beam (`MNEMOPI_POLYPHONIC_RECALL`, then the beam's
20
+ * `polyphonicRecall` config, then the process-wide default); otherwise the beam's
21
+ * linear `recall` / `recallEnhanced` surface answers.
22
+ */
20
23
  export declare function orchestrateRecall(beam: OrchestratorBeam, query: string, topK?: number, options?: OrchestrateRecallOptions): Promise<OrchestratedRecallResult[]>;
@@ -1,10 +1,10 @@
1
1
  import type { Database } from "bun:sqlite";
2
2
  import { type Env } from "../config.js";
3
3
  import { type DatabasePath } from "../db.js";
4
- import type { BeamMemoryState, JsonValue, Metadata, RecallResult } from "./beam/types.js";
4
+ import type { BeamMemoryState, JsonValue, Metadata, RecallResult, RecallTierLabel } from "./beam/types.js";
5
5
  import { EpisodicGraph } from "./episodic-graph.js";
6
6
  import { VeracityConsolidator } from "./veracity-consolidation.js";
7
- export type PolyphonicVoice = "vector" | "graph" | "fact" | "temporal";
7
+ export type PolyphonicVoice = "hybrid" | "vector" | "graph" | "fact" | "temporal";
8
8
  export interface VoiceRecallResult {
9
9
  readonly memoryId: string;
10
10
  readonly score: number;
@@ -17,15 +17,37 @@ export interface PolyphonicResult {
17
17
  readonly voiceScores: Partial<Record<PolyphonicVoice, number>>;
18
18
  readonly metadata: Metadata;
19
19
  }
20
- export interface PolyphonicMemoryResult extends Omit<RecallResult, "metadata" | "score" | "tier"> {
20
+ /** A hydrated recall row with its fused score and per-voice RRF contributions. */
21
+ export interface PolyphonicMemoryResult extends RecallResult {
21
22
  score: number;
22
23
  combined_score: number;
23
24
  voice_scores: Partial<Record<PolyphonicVoice, number>>;
24
25
  metadata: Metadata;
25
- tier: "working" | "episodic";
26
+ tier: RecallTierLabel;
26
27
  }
27
- export interface PolyphonicRecallOptions {
28
+ /** Per-call options that shape which memories the voices may surface. */
29
+ export interface PolyphonicCallOptions {
30
+ /**
31
+ * Extra visibility channel, mirroring linear recall: rows are visible when they
32
+ * belong to the engine session, are `global`, or carry this `channel_id`.
33
+ */
34
+ readonly channelId?: string | null;
35
+ /** Anchor for the temporal voice ("recent" is measured back from this instant). */
36
+ readonly queryTime?: string | Date | null;
37
+ /** Keep non-memory rows (extracted facts) contributed by the `hybrid` voice. */
38
+ readonly includeFacts?: boolean;
39
+ /**
40
+ * Ranked results of the standard (linear) recall for the same query. They form
41
+ * the `hybrid` voice and are reused verbatim when a fused hit is one of them.
42
+ */
43
+ readonly baseline?: readonly RecallResult[];
44
+ }
45
+ export interface PolyphonicRecallOptions extends PolyphonicCallOptions {
28
46
  readonly queryEmbedding?: readonly number[] | Float32Array | null;
47
+ /**
48
+ * Approximate token budget for the returned rows (default 4000); rows past it are
49
+ * dropped even below `topK`. `Infinity` disables the budget.
50
+ */
29
51
  readonly contextBudget?: number;
30
52
  }
31
53
  interface PolyphonicEngineOptions {
@@ -36,8 +58,31 @@ interface PolyphonicEngineOptions {
36
58
  readonly sessionId?: string | null;
37
59
  readonly channelId?: string | null;
38
60
  }
39
- export declare function polyphonicRecallIsEnabled(env?: Env): boolean;
61
+ /** Reciprocal rank fusion constant: a voice's rank-`r` hit contributes `1 / (RRF_K + r)`. */
62
+ export declare const POLYPHONIC_RRF_K = 60;
63
+ export declare const POLYPHONIC_VOICES: readonly PolyphonicVoice[];
64
+ /** Highest possible fused score: rank 1 in every voice. */
65
+ export declare const POLYPHONIC_MAX_COMBINED_SCORE: number;
66
+ /**
67
+ * Graph voice bounds. Proactive linking makes banks dense (every memory sharing an
68
+ * entity links to every other), so an unbounded walk grows with the cube of the bank.
69
+ * The voice reports at most `MAX_DIRECT` entity matches, expands the `MAX_SEEDS`
70
+ * strongest of them and at most `MAX_FRONTIER` nodes per later hop, reads at most
71
+ * `MAX_EDGE_ROWS` edges per hop, and reports at most `MAX_RESULTS` memories in total.
72
+ */
73
+ export declare const GRAPH_VOICE_MAX_DEPTH = 2;
74
+ export declare const GRAPH_VOICE_MAX_DIRECT = 32;
75
+ export declare const GRAPH_VOICE_MAX_SEEDS = 16;
76
+ export declare const GRAPH_VOICE_MAX_FRONTIER = 32;
77
+ export declare const GRAPH_VOICE_MAX_EDGE_ROWS = 512;
78
+ export declare const GRAPH_VOICE_MAX_RESULTS = 64;
79
+ /**
80
+ * Polyphonic recall gate: `MNEMOPI_POLYPHONIC_RECALL` wins when set, then the
81
+ * per-instance `configured` flag, then the process-wide default.
82
+ */
83
+ export declare function polyphonicRecallIsEnabled(env?: Env, configured?: boolean): boolean;
40
84
  export declare class PolyphonicRecallEngine {
85
+ #private;
41
86
  readonly dbPath: DatabasePath;
42
87
  readonly db: Database;
43
88
  readonly ownsConnection: boolean;
@@ -47,20 +92,43 @@ export declare class PolyphonicRecallEngine {
47
92
  readonly channelId: string | null;
48
93
  readonly voiceWeights: Readonly<Record<PolyphonicVoice, number>>;
49
94
  constructor(options?: PolyphonicEngineOptions);
50
- recall(query: string, queryEmbedding?: readonly number[] | Float32Array | null, topK?: number, contextBudget?: number): PolyphonicMemoryResult[];
51
- vectorVoice(queryEmbedding: readonly number[] | Float32Array | null): VoiceRecallResult[];
95
+ recall(query: string, queryEmbedding?: readonly number[] | Float32Array | null, topK?: number, contextBudget?: number, options?: PolyphonicCallOptions): PolyphonicMemoryResult[];
96
+ /** The standard recall ranking for the same query, fused as one more voice. */
97
+ hybridVoice(baseline: readonly RecallResult[], options?: PolyphonicCallOptions): VoiceRecallResult[];
98
+ vectorVoice(queryEmbedding: readonly number[] | Float32Array | null, options?: PolyphonicCallOptions): VoiceRecallResult[];
99
+ /**
100
+ * Memories whose gist or KG facts name a query entity (seeds), plus memories reached
101
+ * from the strongest seeds over `ctx` edges. The walk is one multi-source BFS: each
102
+ * node expands at most once, every hop is one batched edge query, and seeds, frontier,
103
+ * edge rows and results are all capped, so dense proactively linked banks cost the
104
+ * same bounded work as sparse ones.
105
+ */
52
106
  graphVoice(query: string): VoiceRecallResult[];
53
107
  factVoice(query: string): VoiceRecallResult[];
54
- temporalVoice(query: string): VoiceRecallResult[];
108
+ temporalVoice(query: string, options?: PolyphonicCallOptions): VoiceRecallResult[];
109
+ /**
110
+ * Reciprocal rank fusion. A voice may report the same memory several times (e.g. the
111
+ * graph voice once per matching gist or fact); only its best rank counts, so each
112
+ * voice contributes at most `1 / (RRF_K + 1)` and the fused score stays bounded by
113
+ * {@link POLYPHONIC_MAX_COMBINED_SCORE}.
114
+ */
55
115
  combineVoices(...voiceResults: readonly VoiceRecallResult[][]): Map<string, PolyphonicResult>;
56
- diversityRerank(results: ReadonlyMap<string, PolyphonicResult>, topK: number): PolyphonicResult[];
57
- estimateSimilarity(a: PolyphonicResult, b: PolyphonicResult): number;
58
- assembleContext(results: readonly PolyphonicResult[], budget: number): PolyphonicResult[];
116
+ /**
117
+ * Walk fused candidates best-first, hydrating each and skipping rows that are not
118
+ * visible or whose content near-duplicates an already selected row, until `topK`
119
+ * rows are selected.
120
+ */
121
+ diversityRerank(results: ReadonlyMap<string, PolyphonicResult>, topK: number, hydrate: (candidate: PolyphonicResult) => PolyphonicMemoryResult | null): PolyphonicMemoryResult[];
122
+ assembleContext(results: readonly PolyphonicMemoryResult[], budget: number): PolyphonicMemoryResult[];
59
123
  getStats(): Record<string, JsonValue>;
60
124
  close(): void;
61
- private hydrateResults;
62
- private lookupMemory;
63
125
  }
126
+ /**
127
+ * The beam's polyphonic engine, built on first use. Building it creates the fact voice
128
+ * tables; until it succeeds once, each call also backfills `consolidated_facts` from KG
129
+ * facts extracted while polyphonic recall was off, so enabling the flag on an existing
130
+ * bank starts with a fact voice and a failed backfill (e.g. a busy database) is retried.
131
+ */
64
132
  export declare function getPolyphonicEngine(beam: BeamMemoryState): PolyphonicRecallEngine;
65
133
  export declare function polyphonicRecall(beam: BeamMemoryState, query: string, topK?: number, options?: PolyphonicRecallOptions): PolyphonicMemoryResult[];
66
134
  export {};
@@ -21,9 +21,19 @@ export interface QueryCacheStats {
21
21
  readonly max_size: number;
22
22
  readonly version: number;
23
23
  }
24
- export declare function isEnhancedRecallEnabled(env?: Env): boolean;
25
- export declare function isQueryCacheEnabled(useCache?: boolean, env?: Env): boolean;
26
- export declare class QueryCache {
24
+ export declare function isEnhancedRecallEnabled(env?: Env, configured?: boolean): boolean;
25
+ export declare function isQueryCacheEnabled(useCache?: boolean, env?: Env, configured?: boolean): boolean;
26
+ /**
27
+ * Tiered query result cache. Every entry lives in a `scope`: an opaque string the
28
+ * caller derives from all non-query inputs (result limit, filters, visibility,
29
+ * ranking mode, ...). Lookups only ever consider entries of the same scope, so the
30
+ * fuzzy tiers can match a *similar query* but never a different set of options.
31
+ *
32
+ * - Tier 1: exact normalized query match.
33
+ * - Tiers 2/3: query embedding cosine >= 0.88, or >= 0.78 plus word overlap.
34
+ * - Tier 4: normalized word overlap.
35
+ */
36
+ export declare class QueryCache<T = QueryCacheResult> {
27
37
  #private;
28
38
  readonly maxSize: number;
29
39
  readonly ttlSeconds: number;
@@ -35,8 +45,8 @@ export declare class QueryCache {
35
45
  tier4Hits: number;
36
46
  constructor(options?: QueryCacheOptions | string | null, maxSize?: number, ttlSeconds?: number);
37
47
  invalidate(): void;
38
- get(query: string, embedding?: QueryEmbedding | null): readonly QueryCacheResult[] | null;
39
- put(query: string, results: readonly QueryCacheResult[], embedding?: QueryEmbedding | null): void;
48
+ get(query: string, embedding?: QueryEmbedding | null, scope?: string): readonly T[] | null;
49
+ put(query: string, results: readonly T[], embedding?: QueryEmbedding | null, scope?: string): void;
40
50
  close(): void;
41
51
  get hitRate(): number;
42
52
  stats(): QueryCacheStats;
@@ -9,6 +9,12 @@ export declare const VERACITY_WEIGHTS: Readonly<{
9
9
  }>;
10
10
  export type Veracity = keyof typeof VERACITY_WEIGHTS;
11
11
  export declare const VERACITY_ALLOWED: Record<Veracity, true>;
12
+ /**
13
+ * Relations that legitimately hold for many objects at once ("Alice uses Rust" and
14
+ * "Alice uses Go" are both true). A second object for them is not a contradiction, so
15
+ * no conflict is recorded; otherwise every pair of objects would be one (quadratic).
16
+ */
17
+ export declare const MULTI_VALUED_PREDICATES: Readonly<Record<string, true>>;
12
18
  export interface ConsolidatedFact {
13
19
  readonly subject: string;
14
20
  readonly predicate: string;
@@ -52,6 +58,11 @@ export declare class VeracityConsolidator {
52
58
  resolveConflict(conflictId: number, winningFactId: string): void;
53
59
  getConflicts(): Conflict[];
54
60
  getConsolidatedFacts(subject?: string | null, minConfidence?: number): ConsolidatedFact[];
61
+ /**
62
+ * Active facts whose subject equals one of `subjects`, ignoring ASCII case, so a
63
+ * lower-case query word finds `PostgreSQL` or `Alice`.
64
+ */
65
+ getConsolidatedFactsBySubjects(subjects: readonly string[], minConfidence?: number): ConsolidatedFact[];
55
66
  getHighConfidenceSummary(subject: string, threshold?: number): string;
56
67
  runConsolidationPass(): void;
57
68
  resolveConflictByFacts(winningId: string, losingId: string): void;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@linxiraos/pi-mnemopi",
4
- "version": "1.1.23",
4
+ "version": "1.1.25",
5
5
  "description": "Local SQLite memory engine for omp agents",
6
6
  "homepage": "https://linxira-os.github.io/zeta/",
7
7
  "author": "Linxira-OS",
@@ -39,10 +39,10 @@
39
39
  "fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
40
40
  },
41
41
  "dependencies": {
42
- "@linxiraos/pi-ai": "1.1.23",
43
- "@linxiraos/pi-catalog": "1.1.23",
44
- "@linxiraos/pi-natives": "1.1.23",
45
- "@linxiraos/pi-utils": "1.1.23"
42
+ "@linxiraos/pi-ai": "1.1.25",
43
+ "@linxiraos/pi-catalog": "1.1.25",
44
+ "@linxiraos/pi-natives": "1.1.25",
45
+ "@linxiraos/pi-utils": "1.1.25"
46
46
  },
47
47
  "peerDependencies": {
48
48
  "fastembed": "2.1.0",
package/src/config.ts CHANGED
@@ -279,11 +279,12 @@ let enhancedRecallDefault = false;
279
279
  let proactiveLinkingDefault = false;
280
280
 
281
281
  /**
282
- * Sets process-wide defaults for the env-gated recall features. Host configuration
283
- * (e.g. the coding-agent `mnemopi.polyphonicRecall` / `mnemopi.enhancedRecall` /
284
- * `mnemopi.proactiveLinking` settings) lands here; the `MNEMOPI_POLYPHONIC_RECALL` /
285
- * `MNEMOPI_ENHANCED_RECALL` / `MNEMOPI_PROACTIVE_LINKING` environment variables still
286
- * win whenever they are set.
282
+ * Sets process-wide defaults for the env-gated recall features. Hosts that open
283
+ * several memories with different policies should pass the per-instance
284
+ * `polyphonicRecall` / `enhancedRecall` / `proactiveLinking` options instead
285
+ * (`Mnemopi` / `BeamMemory` constructors), which take precedence over these
286
+ * defaults. The `MNEMOPI_POLYPHONIC_RECALL` / `MNEMOPI_ENHANCED_RECALL` /
287
+ * `MNEMOPI_PROACTIVE_LINKING` environment variables still win whenever they are set.
287
288
  */
288
289
  export function configureRecallFeatures(flags: RecallFeatureFlags): void {
289
290
  if (flags.polyphonicRecall !== undefined) polyphonicRecallDefault = flags.polyphonicRecall;
@@ -291,18 +292,26 @@ export function configureRecallFeatures(flags: RecallFeatureFlags): void {
291
292
  if (flags.proactiveLinking !== undefined) proactiveLinkingDefault = flags.proactiveLinking;
292
293
  }
293
294
 
294
- export function polyphonicRecallEnabled(env: Env = process.env): boolean {
295
+ /**
296
+ * Resolves the polyphonic recall gate: the env var wins when set, then the
297
+ * per-instance `configured` value, then the process-wide default.
298
+ */
299
+ export function polyphonicRecallEnabled(env: Env = process.env, configured?: boolean): boolean {
295
300
  const value = envOptionalString("MNEMOPI_POLYPHONIC_RECALL", env);
296
- return value === undefined ? polyphonicRecallDefault : value === "1";
301
+ return value === undefined ? (configured ?? polyphonicRecallDefault) : value === "1";
297
302
  }
298
303
 
299
304
  export function temporalHalflifeHours(env: Env = process.env): number {
300
305
  return envFloat("MNEMOPI_TEMPORAL_HALFLIFE_HOURS", 24, env);
301
306
  }
302
307
 
303
- export function enhancedRecallEnabled(env: Env = process.env): boolean {
308
+ /**
309
+ * Resolves the enhanced recall (query result cache) gate: the env var wins when
310
+ * set, then the per-instance `configured` value, then the process-wide default.
311
+ */
312
+ export function enhancedRecallEnabled(env: Env = process.env, configured?: boolean): boolean {
304
313
  const value = envOptionalString("MNEMOPI_ENHANCED_RECALL", env);
305
- return value === undefined ? enhancedRecallDefault : value === "1";
314
+ return value === undefined ? (configured ?? enhancedRecallDefault) : value === "1";
306
315
  }
307
316
 
308
317
  export function proactiveLinkingEnabled(env: Env = process.env): boolean {
@@ -1,10 +1,12 @@
1
1
  import type { SQLQueryBindings } from "bun:sqlite";
2
+ import { logger } from "@linxiraos/pi-utils";
3
+ import { polyphonicRecallEnabled } from "../../config";
2
4
  import { generateId, stableMemoryId } from "../../util/ids";
3
5
  import { aaakEncode } from "../aaak";
4
6
  import { REGEX_EXTRACTION_MAX_INPUT_CHARS } from "../entities";
5
7
  import { EpisodicGraph } from "../episodic-graph";
6
8
  import { type ExtractedFactCategories, heuristicExtractFacts } from "../extraction";
7
- import { clampVeracity } from "../veracity-consolidation";
9
+ import { clampVeracity, VERACITY_WEIGHTS, type Veracity, VeracityConsolidator } from "../veracity-consolidation";
8
10
  import { scheduleEmbedding } from "./helpers";
9
11
  import type { BeamMemoryState, BeamStats, JsonValue, MemoriaRetrieveResult, Metadata, SleepResult } from "./types";
10
12
 
@@ -325,6 +327,102 @@ function insertKg(
325
327
  source: sourceMemoryId ?? "extraction",
326
328
  confidence: 0.65,
327
329
  });
330
+ consolidateKgFact(beam, subject, predicate, object, sourceMemoryId);
331
+ }
332
+
333
+ /** Veracity of the memory a fact was extracted from, restricted to the consolidator's vocabulary. */
334
+ function sourceMemoryVeracity(beam: BeamMemoryState, sourceMemoryId: string | null): Veracity {
335
+ if (sourceMemoryId === null) return "unknown";
336
+ const row = (beam.db.query("SELECT veracity FROM working_memory WHERE id = ?").get(sourceMemoryId) ??
337
+ beam.db.query("SELECT veracity FROM episodic_memory WHERE id = ?").get(sourceMemoryId)) as {
338
+ veracity: string | null;
339
+ } | null;
340
+ const veracity = row?.veracity ?? "unknown";
341
+ return Object.hasOwn(VERACITY_WEIGHTS, veracity) ? (veracity as Veracity) : "unknown";
342
+ }
343
+
344
+ /**
345
+ * This beam's veracity consolidator, created on first use so `consolidated_facts` /
346
+ * `conflicts` DDL only runs once polyphonic recall needs them.
347
+ */
348
+ export function ensureVeracityConsolidator(beam: BeamMemoryState): VeracityConsolidator {
349
+ const existing = beam.veracityConsolidator;
350
+ if (existing instanceof VeracityConsolidator && existing.conn === beam.db) return existing;
351
+ const consolidator = new VeracityConsolidator(beam.dbPath ?? ":memory:", beam.db);
352
+ beam.veracityConsolidator = consolidator;
353
+ return consolidator;
354
+ }
355
+
356
+ /**
357
+ * Feed an extracted subject/predicate/object fact into the veracity consolidator so
358
+ * repeated mentions gain confidence in `consolidated_facts`, which the polyphonic
359
+ * recall fact voice reads. Only runs while polyphonic recall is enabled for this beam;
360
+ * facts written with it off are picked up by {@link backfillConsolidatedFacts}.
361
+ * Best-effort: a failure never blocks the extraction write.
362
+ */
363
+ function consolidateKgFact(
364
+ beam: BeamMemoryState,
365
+ subject: string,
366
+ predicate: string,
367
+ object: string,
368
+ sourceMemoryId: string | null,
369
+ ): void {
370
+ if (!polyphonicRecallEnabled(process.env, beam.config?.polyphonicRecall)) return;
371
+ const cleanSubject = subject.trim();
372
+ const cleanPredicate = predicate.trim();
373
+ const cleanObject = object.trim();
374
+ if (cleanSubject === "" || cleanPredicate === "" || cleanObject === "") return;
375
+ try {
376
+ ensureVeracityConsolidator(beam).consolidateFact(
377
+ cleanSubject,
378
+ cleanPredicate,
379
+ cleanObject,
380
+ sourceMemoryVeracity(beam, sourceMemoryId),
381
+ sourceMemoryId,
382
+ );
383
+ } catch (error) {
384
+ // Fact consolidation is an enrichment; the MEMORIA/KG rows above are already written,
385
+ // and the next polyphonic engine built on this bank backfills the fact.
386
+ logger.warn("mnemopi: fact consolidation failed", { subject: cleanSubject, error: String(error) });
387
+ }
388
+ }
389
+
390
+ /**
391
+ * Consolidate every extracted KG fact (`memoria_kg`) whose source memory is not yet
392
+ * among that fact's `consolidated_facts` sources, e.g. facts written while polyphonic
393
+ * recall was off. Idempotent: a (fact, source) pair is only ever counted once.
394
+ * Facts without a source memory are skipped; the fact voice could not return them.
395
+ */
396
+ export function backfillConsolidatedFacts(beam: BeamMemoryState, consolidator: VeracityConsolidator): number {
397
+ const rows = beam.db
398
+ .query(`
399
+ SELECT trim(k.subject) AS subject, trim(k.predicate) AS predicate, trim(k.object) AS object,
400
+ k.source_memory_id AS source
401
+ FROM memoria_kg k
402
+ WHERE k.source_memory_id IS NOT NULL AND k.source_memory_id != ''
403
+ AND trim(k.subject) != '' AND trim(k.predicate) != '' AND trim(k.object) != ''
404
+ AND NOT EXISTS (
405
+ SELECT 1 FROM consolidated_facts cf, json_each(cf.sources_json) s
406
+ WHERE cf.subject = trim(k.subject) AND cf.predicate = trim(k.predicate)
407
+ AND cf.object = trim(k.object) AND s.value = k.source_memory_id
408
+ )
409
+ GROUP BY 1, 2, 3, 4
410
+ ORDER BY MIN(k.id)
411
+ `)
412
+ .all() as { subject: string; predicate: string; object: string; source: string }[];
413
+ if (rows.length === 0) return 0;
414
+ consolidator.serializedWrite(() => {
415
+ for (const row of rows) {
416
+ consolidator.consolidateFact(
417
+ row.subject,
418
+ row.predicate,
419
+ row.object,
420
+ sourceMemoryVeracity(beam, row.source),
421
+ row.source,
422
+ );
423
+ }
424
+ });
425
+ return rows.length;
328
426
  }
329
427
 
330
428
  function insertPreference(