@oh-my-pi/pi-mnemopi 18.11.0 → 18.11.2

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,6 +2,18 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.11.2] - 2026-10-16
6
+
7
+ ### Fixed
8
+
9
+ - Fixed `MNEMOPI_<LABEL>_WEIGHT` and `MNEMOPI_TIER<N>_WEIGHT` having no effect on recall ranking ([#13824](https://github.com/can1357/oh-my-pi/pull/13824) by [@sjawhar](https://github.com/sjawhar))
10
+
11
+ ## [18.11.1] - 2026-10-15
12
+
13
+ ### Fixed
14
+
15
+ - Fixed a failing `mnemopi` CLI command or MCP memory call dropping the embeddings of memories it had just stored ([#15804](https://github.com/can1357/oh-my-pi/pull/15804) by [@yuzu-octopus](https://github.com/yuzu-octopus))
16
+
5
17
  ## [18.11.0] - 2026-10-14
6
18
 
7
19
  ### Fixed
@@ -16,6 +16,7 @@ export declare class CliError extends Error {
16
16
  constructor(message: string, exitCode?: number);
17
17
  }
18
18
  type CommandHandler = (args: readonly string[], context?: CliContext) => number | Promise<number>;
19
+ export declare function withMemory<T>(context: CliContext | undefined, fn: (memory: BeamMemory) => T | Promise<T>): Promise<T>;
19
20
  export declare function memoryStats(memory: BeamMemory, dataDir?: string): Record<string, unknown>;
20
21
  export declare const cmdExport: CommandHandler;
21
22
  export declare const cmdImport: CommandHandler;
@@ -56,8 +56,11 @@ export declare function scratchpadMaxItems(env?: Env): number;
56
56
  export declare function recencyHalflifeHours(env?: Env): number;
57
57
  export declare function tier2Days(env?: Env): number;
58
58
  export declare function tier3Days(env?: Env): number;
59
+ /** Recall-time score multiplier for an episodic row still at degradation tier 1. */
59
60
  export declare function tier1Weight(env?: Env): number;
61
+ /** Recall-time score multiplier for an episodic row degraded to tier 2. */
60
62
  export declare function tier2Weight(env?: Env): number;
63
+ /** Recall-time score multiplier for an episodic row degraded to tier 3 or beyond. */
61
64
  export declare function tier3Weight(env?: Env): number;
62
65
  export declare function degradeBatchSize(env?: Env): number;
63
66
  export declare function smartCompressEnabled(env?: Env): boolean;
@@ -1,3 +1,4 @@
1
+ import { BeamMemory } from "./core/beam/index.js";
1
2
  export type JsonPrimitive = string | number | boolean | null;
2
3
  export type JsonValue = JsonPrimitive | JsonValue[] | {
3
4
  [key: string]: JsonValue;
@@ -480,5 +481,6 @@ export declare const GRAPH_LINK_SCHEMA: {
480
481
  readonly required: readonly ["source_id", "target_id", "relationship"];
481
482
  };
482
483
  export declare const TOOLS: readonly ToolDefinition[];
484
+ export declare function withBeam<T>(args: ToolArguments, fn: (beam: BeamMemory, bank: string) => T | Promise<T>): Promise<T>;
483
485
  export declare function handleToolCall(name: string, args?: ToolArguments): Promise<ToolResult>;
484
486
  export declare function getToolDefinitions(): readonly ToolDefinition[];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-mnemopi",
4
- "version": "18.11.0",
4
+ "version": "18.11.2",
5
5
  "description": "Local SQLite memory engine for omp agents",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": {
@@ -42,10 +42,10 @@
42
42
  "fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
43
43
  },
44
44
  "dependencies": {
45
- "@oh-my-pi/pi-ai": "18.11.0",
46
- "@oh-my-pi/pi-catalog": "18.11.0",
47
- "@oh-my-pi/pi-natives": "18.11.0",
48
- "@oh-my-pi/pi-utils": "18.11.0"
45
+ "@oh-my-pi/pi-ai": "18.11.2",
46
+ "@oh-my-pi/pi-catalog": "18.11.2",
47
+ "@oh-my-pi/pi-natives": "18.11.2",
48
+ "@oh-my-pi/pi-utils": "18.11.2"
49
49
  },
50
50
  "peerDependencies": {
51
51
  "fastembed": "3.0.0",
package/src/cli.ts CHANGED
@@ -77,17 +77,22 @@ function getMemory(context?: CliContext): { memory: BeamMemory; owned: boolean }
77
77
  return { memory: new BeamMemory({ dbPath: resolveDbPath(context) }), owned: true };
78
78
  }
79
79
 
80
- async function withMemory<T>(context: CliContext | undefined, fn: (memory: BeamMemory) => T | Promise<T>): Promise<T> {
80
+ export async function withMemory<T>(
81
+ context: CliContext | undefined,
82
+ fn: (memory: BeamMemory) => T | Promise<T>,
83
+ ): Promise<T> {
81
84
  const { memory, owned } = getMemory(context);
82
85
  try {
83
- const result = await fn(memory);
86
+ return await fn(memory);
87
+ } finally {
84
88
  // Drain background fact-extraction and embedding tasks before close so
85
89
  // short-lived owners (e.g. `mnemopi store …` / `mnemopi sleep …`) don't
86
90
  // race the SQLite handle shut from under in-flight `embed()` writes.
87
- if (owned) await memory.flushExtractions();
88
- return result;
89
- } finally {
90
- if (owned) memory.close();
91
+ // Runs on both success and throw paths.
92
+ if (owned) {
93
+ await memory.flushExtractions();
94
+ memory.close();
95
+ }
91
96
  }
92
97
  }
93
98
 
package/src/config.ts CHANGED
@@ -170,16 +170,19 @@ export function tier3Days(env: Env = process.env): number {
170
170
  return envInt("MNEMOPI_TIER3_DAYS", 180, env);
171
171
  }
172
172
 
173
+ /** Recall-time score multiplier for an episodic row still at degradation tier 1. */
173
174
  export function tier1Weight(env: Env = process.env): number {
174
175
  return envFloat("MNEMOPI_TIER1_WEIGHT", 1.0, env);
175
176
  }
176
177
 
178
+ /** Recall-time score multiplier for an episodic row degraded to tier 2. */
177
179
  export function tier2Weight(env: Env = process.env): number {
178
- return envFloat("MNEMOPI_TIER2_WEIGHT", 0.5, env);
180
+ return envFloat("MNEMOPI_TIER2_WEIGHT", 0.85, env);
179
181
  }
180
182
 
183
+ /** Recall-time score multiplier for an episodic row degraded to tier 3 or beyond. */
181
184
  export function tier3Weight(env: Env = process.env): number {
182
- return envFloat("MNEMOPI_TIER3_WEIGHT", 0.25, env);
185
+ return envFloat("MNEMOPI_TIER3_WEIGHT", 0.7, env);
183
186
  }
184
187
 
185
188
  export function degradeBatchSize(env: Env = process.env): number {
@@ -1,5 +1,17 @@
1
1
  import { popLoopPhase, pushLoopPhase, withLoopPhase } from "@oh-my-pi/pi-utils";
2
- import { normalizedRecallWeights, polyphonicRecallEnabled, temporalHalflifeHours } from "../../config";
2
+ import {
3
+ importedWeight,
4
+ inferredWeight,
5
+ normalizedRecallWeights,
6
+ polyphonicRecallEnabled,
7
+ statedWeight,
8
+ temporalHalflifeHours,
9
+ tier1Weight,
10
+ tier2Weight,
11
+ tier3Weight,
12
+ toolWeight,
13
+ unknownWeight,
14
+ } from "../../config";
3
15
  import { hasCjk, matchesWordForm } from "../../util/regex";
4
16
  import { embedQuery } from "../embeddings";
5
17
  import { mmrRerank } from "../mmr";
@@ -63,16 +75,33 @@ type RecallMmrItem = {
63
75
  readonly [key: string]: unknown;
64
76
  };
65
77
 
66
- const VERACITY_WEIGHTS: Record<string, number> = {
67
- stated: 1.0,
68
- true: 1.0,
69
- likely_true: 1.0,
70
- unknown: 0.8,
71
- inferred: 0.7,
72
- imported: 0.6,
73
- tool: 0.5,
74
- false: 0,
75
- };
78
+ /**
79
+ * Score multipliers for a candidate's stored `veracity` label and, for episodic
80
+ * rows, its degradation tier. Resolved once per {@link recall} from the
81
+ * `MNEMOPI_<LABEL>_WEIGHT` and `MNEMOPI_TIER<N>_WEIGHT` readers in `config.ts`,
82
+ * clamped at 0 like the hybrid weights so a negative value cannot rank a row
83
+ * below one labelled `false`; `true`, `likely_true` and `false` have no override.
84
+ */
85
+ interface TrustWeights {
86
+ readonly veracity: Readonly<Record<string, number>> & { readonly unknown: number };
87
+ readonly tiers: readonly [tier1: number, tier2: number, tier3: number];
88
+ }
89
+
90
+ function resolveTrustWeights(): TrustWeights {
91
+ return {
92
+ veracity: {
93
+ stated: Math.max(0, statedWeight()),
94
+ true: 1.0,
95
+ likely_true: 1.0,
96
+ unknown: Math.max(0, unknownWeight()),
97
+ inferred: Math.max(0, inferredWeight()),
98
+ imported: Math.max(0, importedWeight()),
99
+ tool: Math.max(0, toolWeight()),
100
+ false: 0,
101
+ },
102
+ tiers: [Math.max(0, tier1Weight()), Math.max(0, tier2Weight()), Math.max(0, tier3Weight())],
103
+ };
104
+ }
76
105
 
77
106
  /**
78
107
  * Default per-result content preview cap enforced by {@link recall}. Content
@@ -671,6 +700,7 @@ function scoreCandidate(
671
700
  queryTokens: readonly string[],
672
701
  queryGroups: readonly (readonly string[])[],
673
702
  weights: readonly [number, number, number],
703
+ trust: TrustWeights,
674
704
  options: RecallOptionsInternal,
675
705
  ): RecallResult | null {
676
706
  const content = asString(candidate.row.content);
@@ -714,11 +744,11 @@ function scoreCandidate(
714
744
  score *= 1 + temporalWeight * temporalScore;
715
745
  }
716
746
  const veracity = asString(candidate.row.veracity) || "unknown";
717
- const veracityWeight = VERACITY_WEIGHTS[veracity] ?? VERACITY_WEIGHTS.unknown ?? 0.8;
747
+ const veracityWeight = trust.veracity[veracity] ?? trust.veracity.unknown;
718
748
  const degradationTier = candidate.tierLabel === "episodic" ? asNumber(candidate.row.tier, 1) : undefined;
719
749
  if (candidate.tierLabel === "episodic") {
720
- const tierWeight = degradationTier === 1 ? 1 : degradationTier === 2 ? 0.85 : 0.7;
721
- score *= tierWeight;
750
+ const [tier1, tier2, tier3] = trust.tiers;
751
+ score *= degradationTier === 1 ? tier1 : degradationTier === 2 ? tier2 : tier3;
722
752
  }
723
753
  score *= veracityWeight * currentContentAdjustment(searchableContent, options.currentSensitive === true);
724
754
  const preview = clipRecallContent(content, options.contentPreviewChars ?? RECALL_CONTENT_PREVIEW_CHARS);
@@ -938,9 +968,10 @@ export async function recall(
938
968
  const tokens = expandedTokens(query, useSynonyms);
939
969
  const tokenGroups = expandedTokenGroups(query, useSynonyms);
940
970
  const candidates = collectMemoryCandidates(beam, query, topK, temporalOptions);
971
+ const trust = resolveTrustWeights();
941
972
  const scored: RecallResult[] = [];
942
973
  for (const candidate of candidates) {
943
- const result = scoreCandidate(candidate, tokens, tokenGroups, weights, temporalOptions);
974
+ const result = scoreCandidate(candidate, tokens, tokenGroups, weights, trust, temporalOptions);
944
975
  if (result !== null) scored.push(result);
945
976
  }
946
977
  scored.sort((left, right) => (right.score ?? 0) - (left.score ?? 0));
package/src/mcp-tools.ts CHANGED
@@ -447,20 +447,24 @@ function sharedBeam(): BeamMemory {
447
447
  return new BeamMemory({ sessionId: "mcp_shared_surface", dbPath });
448
448
  }
449
449
 
450
- async function withBeam<T>(args: ToolArguments, fn: (beam: BeamMemory, bank: string) => T | Promise<T>): Promise<T> {
450
+ export async function withBeam<T>(
451
+ args: ToolArguments,
452
+ fn: (beam: BeamMemory, bank: string) => T | Promise<T>,
453
+ ): Promise<T> {
451
454
  const bank = resolveBank(args);
452
455
  const beam = createBeam(args, bank);
456
+ let result: T;
453
457
  try {
454
- const result = await fn(beam, bank);
458
+ result = await fn(beam, bank);
459
+ } finally {
455
460
  // Drain background fact-extraction and embedding tasks before close so
456
461
  // the SQLite handle stays open until in-flight `embed()` writes commit;
457
462
  // otherwise the short-lived MCP `remember`/`update`/`sleep` paths race
458
463
  // the close and silently drop the new dense-recall rows.
459
464
  await beam.flushExtractions();
460
- return result;
461
- } finally {
462
465
  beam.close();
463
466
  }
467
+ return result;
464
468
  }
465
469
  function serialize(value: unknown): unknown {
466
470
  if (value instanceof Date) return value.toISOString();
@@ -739,13 +743,14 @@ function surfaceLabel(content: string, kind: string): string {
739
743
 
740
744
  async function withSharedBeam<T>(fn: (beam: BeamMemory) => T | Promise<T>): Promise<T> {
741
745
  const beam = sharedBeam();
746
+ let result: T;
742
747
  try {
743
- const result = await fn(beam);
744
- await beam.flushExtractions();
745
- return result;
748
+ result = await fn(beam);
746
749
  } finally {
750
+ await beam.flushExtractions();
747
751
  beam.close();
748
752
  }
753
+ return result;
749
754
  }
750
755
 
751
756
  async function handleSharedRemember(args: ToolArguments): Promise<ToolResult> {