@oh-my-pi/pi-mnemopi 17.2.4 → 17.2.6

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,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [17.2.6] - 2026-08-03
6
+
7
+ ### Added
8
+
9
+ - Added opt-in SQLite page-size configuration for file-backed databases, configurable via the `MNEMOPI_DB_PAGE_SIZE` environment variable or the `pageSize` option in `openDatabase`. Existing databases retain their original page size.
10
+
5
11
  ## [17.2.3] - 2026-08-01
6
12
 
7
13
  ### Fixed
package/README.md CHANGED
@@ -83,6 +83,7 @@ In `per-project-tagged`, the wrapper is responsible for combining project-local
83
83
  Common environment fallbacks:
84
84
 
85
85
  - `MNEMOPI_DATA_DIR` / `MNEMOPI_DB_PATH`: default storage location.
86
+ - `MNEMOPI_DB_PAGE_SIZE`: optional SQLite page size for new file-backed databases; use a valid power of two from 512 to 65536 or `os` to request the detected system page size. Unset preserves SQLite's default.
86
87
  - `MNEMOPI_NO_EMBEDDINGS=1`: force FTS-only recall.
87
88
  - `MNEMOPI_EMBEDDING_MODEL`: defaults to `BAAI/bge-small-en-v1.5`.
88
89
  - `MNEMOPI_EMBEDDING_API_URL` and `MNEMOPI_EMBEDDING_API_KEY`: OpenAI-compatible embedding endpoint.
@@ -1,4 +1,4 @@
1
- import { Database } from "bun:sqlite";
1
+ import type { Database } from "bun:sqlite";
2
2
  export declare const DEFAULT_LOG_DIR: string;
3
3
  export declare const DEFAULT_LOG_DB: string;
4
4
  export interface CostStats {
@@ -1,4 +1,5 @@
1
1
  import { Database } from "bun:sqlite";
2
+ export type SqlitePageSize = number | "os";
2
3
  export type DatabasePath = string | ":memory:";
3
4
  export interface OpenDatabaseOptions {
4
5
  readonly create?: boolean;
@@ -6,9 +7,10 @@ export interface OpenDatabaseOptions {
6
7
  readonly strict?: boolean;
7
8
  readonly loadExtension?: string | readonly string[];
8
9
  readonly pragmas?: boolean;
10
+ readonly pageSize?: SqlitePageSize;
9
11
  }
10
12
  export declare function openDatabase(path?: DatabasePath, options?: OpenDatabaseOptions): Database;
11
- export declare function enablePragmas(db: Database, path?: DatabasePath): void;
13
+ export declare function enablePragmas(db: Database, path?: DatabasePath, pageSize?: SqlitePageSize): void;
12
14
  export declare function loadExtensions(db: Database, extensions: string | readonly string[]): void;
13
15
  export declare function transaction<T>(db: Database, fn: () => T): T;
14
16
  export declare const deferredTransaction: typeof transaction;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-mnemopi",
4
- "version": "17.2.4",
4
+ "version": "17.2.6",
5
5
  "description": "Local SQLite memory engine for Oh My Pi agents",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Can Boluk",
@@ -39,10 +39,10 @@
39
39
  "fmt": "biome format --write ."
40
40
  },
41
41
  "dependencies": {
42
- "@oh-my-pi/pi-ai": "17.2.4",
43
- "@oh-my-pi/pi-catalog": "17.2.4",
44
- "@oh-my-pi/pi-natives": "17.2.4",
45
- "@oh-my-pi/pi-utils": "17.2.4",
42
+ "@oh-my-pi/pi-ai": "17.2.6",
43
+ "@oh-my-pi/pi-catalog": "17.2.6",
44
+ "@oh-my-pi/pi-natives": "17.2.6",
45
+ "@oh-my-pi/pi-utils": "17.2.6",
46
46
  "lru-cache": "11.5.2"
47
47
  },
48
48
  "peerDependencies": {
@@ -1,10 +1,9 @@
1
- import { Database } from "bun:sqlite";
2
- import { mkdirSync } from "node:fs";
3
- import { homedir } from "node:os";
4
- import { dirname, join } from "node:path";
5
-
6
- export const DEFAULT_LOG_DIR = join(homedir(), ".mnemopi", "data");
7
- export const DEFAULT_LOG_DB = join(DEFAULT_LOG_DIR, "cost_log.db");
1
+ import type { Database } from "bun:sqlite";
2
+ import * as os from "node:os";
3
+ import * as path from "node:path";
4
+ import { openDatabase } from "../db";
5
+ export const DEFAULT_LOG_DIR = path.join(os.homedir(), ".mnemopi", "data");
6
+ export const DEFAULT_LOG_DB = path.join(DEFAULT_LOG_DIR, "cost_log.db");
8
7
 
9
8
  export interface CostStats {
10
9
  total_calls: number;
@@ -22,8 +21,7 @@ type AggregateRow = {
22
21
 
23
22
  export function getConn(dbPath?: string): Database {
24
23
  const path = dbPath ?? DEFAULT_LOG_DB;
25
- mkdirSync(dirname(path), { recursive: true });
26
- return new Database(path, { create: true, readwrite: true, strict: true });
24
+ return openDatabase(path, { pragmas: false });
27
25
  }
28
26
 
29
27
  export function initCostLog(dbPath?: string): void {
@@ -1,7 +1,6 @@
1
- import { Database } from "bun:sqlite";
2
- import { mkdirSync } from "node:fs";
3
- import { dirname } from "node:path";
1
+ import type { Database } from "bun:sqlite";
4
2
  import { type Env, enhancedRecallEnabled } from "../config";
3
+ import { openDatabase } from "../db";
5
4
  import { cosineSimilarity } from "./vector-math";
6
5
 
7
6
  export type QueryCacheResult = Record<string, unknown>;
@@ -80,8 +79,7 @@ export class QueryCache {
80
79
  }
81
80
 
82
81
  #initDb(dbPath: string): void {
83
- if (dbPath !== ":memory:") mkdirSync(dirname(dbPath), { recursive: true });
84
- const db = new Database(dbPath, { create: true, readwrite: true, strict: true });
82
+ const db = openDatabase(dbPath, { pragmas: false });
85
83
  this.#conn = db;
86
84
  if (dbPath !== ":memory:") db.exec("PRAGMA journal_mode=WAL");
87
85
  db.exec(`
package/src/db.ts CHANGED
@@ -3,6 +3,59 @@ import { mkdirSync } from "node:fs";
3
3
  import { dirname } from "node:path";
4
4
  import { dbPath } from "./config";
5
5
 
6
+ export type SqlitePageSize = number | "os";
7
+
8
+ const MIN_SQLITE_PAGE_SIZE = 512;
9
+ const MAX_SQLITE_PAGE_SIZE = 65536;
10
+ let detectedSystemPageSize: number | null | undefined;
11
+
12
+ function isValidPageSize(size: number): boolean {
13
+ return (
14
+ Number.isInteger(size) &&
15
+ size >= MIN_SQLITE_PAGE_SIZE &&
16
+ size <= MAX_SQLITE_PAGE_SIZE &&
17
+ (size & (size - 1)) === 0
18
+ );
19
+ }
20
+
21
+ function detectSystemPageSize(): number | undefined {
22
+ if (detectedSystemPageSize !== undefined) return detectedSystemPageSize ?? undefined;
23
+ try {
24
+ const proc = Bun.spawnSync(["getconf", "PAGE_SIZE"], { stdout: "pipe" });
25
+ if (proc.exitCode === 0) {
26
+ const size = Number(proc.stdout.toString().trim());
27
+ if (isValidPageSize(size)) {
28
+ detectedSystemPageSize = size;
29
+ return size;
30
+ }
31
+ }
32
+ } catch {
33
+ /* fall through */
34
+ }
35
+ detectedSystemPageSize = null;
36
+ return undefined;
37
+ }
38
+
39
+ function configuredPageSize(): SqlitePageSize | undefined {
40
+ const raw = process.env.MNEMOPI_DB_PAGE_SIZE?.trim();
41
+ if (!raw) return undefined;
42
+ if (raw.toLowerCase() === "os") return "os";
43
+ const size = Number(raw);
44
+ return isValidPageSize(size) ? size : undefined;
45
+ }
46
+
47
+ function resolvePageSize(requested?: SqlitePageSize): number | undefined {
48
+ const pageSize = requested ?? configuredPageSize();
49
+ if (pageSize === "os") return detectSystemPageSize();
50
+ return pageSize !== undefined && isValidPageSize(pageSize) ? pageSize : undefined;
51
+ }
52
+
53
+ function applyPageSize(db: Database, path: DatabasePath, requested?: SqlitePageSize): void {
54
+ if (path === ":memory:") return;
55
+ const pageSize = resolvePageSize(requested);
56
+ if (pageSize !== undefined) db.exec(`PRAGMA page_size=${pageSize}`);
57
+ }
58
+
6
59
  export type DatabasePath = string | ":memory:";
7
60
 
8
61
  export interface OpenDatabaseOptions {
@@ -11,6 +64,7 @@ export interface OpenDatabaseOptions {
11
64
  readonly strict?: boolean;
12
65
  readonly loadExtension?: string | readonly string[];
13
66
  readonly pragmas?: boolean;
67
+ readonly pageSize?: SqlitePageSize;
14
68
  }
15
69
 
16
70
  interface TxState {
@@ -29,15 +83,19 @@ export function openDatabase(path: DatabasePath = dbPath(), options: OpenDatabas
29
83
  readwrite: options.readwrite ?? true,
30
84
  strict: options.strict ?? true,
31
85
  });
32
- if (options.pragmas !== false) enablePragmas(db, path);
86
+ if (options.pragmas !== false) enablePragmas(db, path, options.pageSize);
87
+ else if (options.readwrite !== false) applyPageSize(db, path, options.pageSize);
33
88
  if (options.loadExtension !== undefined) loadExtensions(db, options.loadExtension);
34
89
  return db;
35
90
  }
36
91
 
37
- export function enablePragmas(db: Database, path?: DatabasePath): void {
92
+ export function enablePragmas(db: Database, path?: DatabasePath, pageSize?: SqlitePageSize): void {
38
93
  db.exec("PRAGMA foreign_keys=ON");
39
94
  db.exec("PRAGMA busy_timeout=5000");
40
- if (path !== ":memory:") db.exec("PRAGMA journal_mode=WAL");
95
+ if (path !== ":memory:") {
96
+ applyPageSize(db, path ?? dbPath(), pageSize);
97
+ db.exec("PRAGMA journal_mode=WAL");
98
+ }
41
99
  }
42
100
 
43
101
  export function loadExtensions(db: Database, extensions: string | readonly string[]): void {
@@ -1,4 +1,4 @@
1
- import { Database } from "bun:sqlite";
1
+ import type { Database } from "bun:sqlite";
2
2
  import { createHash } from "node:crypto";
3
3
  import {
4
4
  copyFileSync,
@@ -188,7 +188,7 @@ function isSqliteFile(bytes: Uint8Array): boolean {
188
188
  function writeGzippedSqlDump(sql: string, tempPath: string): void {
189
189
  let db: Database | null = null;
190
190
  try {
191
- db = new Database(tempPath, { create: true, readwrite: true, strict: true });
191
+ db = openDatabase(tempPath, { pragmas: false });
192
192
  db.exec(sql);
193
193
  } finally {
194
194
  closeQuietly(db);