@erichll/pi-auto-review 0.22.0 → 0.22.1

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
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.22.1 - 2026-10-01
4
+
5
+ - Fix permission-policy auditing being disabled on Bun with
6
+ `No such built-in module: node:sqlite`. Bun, including Bun-compiled Pi, now
7
+ uses its built-in `bun:sqlite`; Node continues to use `node:sqlite`.
8
+ - Preserve audit statistics and request deduplication when switching between
9
+ Node and Bun by sharing the existing database schema and HMAC key.
10
+ - Finalize Bun's cached SQLite statements when closing the database so the
11
+ last WAL connection can checkpoint and release its resources.
12
+ - Add regression coverage for database migration, redaction, retention,
13
+ failure handling, concurrent Node/Bun writers, and persistent audit reports
14
+ in a real Bun-compiled Pi process.
15
+ - Update the permission-system example for current MCP tool names, including
16
+ `codemode`, `tool_search`, `mcp__atlassian__*`, and `mcp__pencil__*`.
17
+
3
18
  ## 0.22.0 - 2026-09-30
4
19
 
5
20
  - Raise the `@earendil-works/pi-ai` and `@earendil-works/pi-coding-agent` peer
package/README.md CHANGED
@@ -34,9 +34,10 @@ that dependency is a hard prerequisite (see [Install and enable](#install-and-en
34
34
  > extension. This release line requires Pi 0.99.1 or later and supports
35
35
  > permission-system 36.0.0 and later:
36
36
 
37
- Node.js 22.13.0 or newer is required. Permission auditing uses Node's built-in
38
- `node:sqlite`; it does not require a SQLite CLI, system SQLite library, or npm
39
- SQLite package.
37
+ Node-based Pi requires Node.js 22.13.0 or newer. Permission auditing uses the
38
+ host runtime's built-in SQLite: `node:sqlite` on Node and `bun:sqlite` on Bun,
39
+ including Bun-compiled Pi (verified with Bun 1.3.14). It does not require a
40
+ SQLite CLI, system SQLite library, or npm SQLite package.
40
41
 
41
42
  ```bash
42
43
  pi install npm:@gotgenes/pi-permission-system
@@ -374,7 +375,9 @@ project-specific labels; inspect suggestions before copying them.
374
375
 
375
376
  Data lives at
376
377
  `~/.pi/agent/extensions/pi-auto-review/policy-audit.sqlite`, beside an
377
- owner-only HMAC key. The directory is mode `0700`; the key, database, WAL, and
378
+ owner-only HMAC key. Node and Bun use the same database schema and key, so
379
+ switching runtimes preserves statistics and request deduplication.
380
+ The directory is mode `0700`; the key, database, WAL, and
378
381
  SHM are mode `0600`. Initialization, lock, write, or corruption failures disable
379
382
  auditing and warn once without changing any permission result. A corrupt
380
383
  database is not deleted or rebuilt automatically. Disable new collection with:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@erichll/pi-auto-review",
3
- "version": "0.22.0",
3
+ "version": "0.22.1",
4
4
  "description": "Fail-closed, model-backed approval broker for Pi: deterministically hard-denies dangerous operations, reviews dangerous boundaries with a reviewer model, and issues one-shot expiring grants that OS sandbox adapters must consume before retrying.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -0,0 +1,55 @@
1
+ type Statement = {
2
+ run(...params: unknown[]): { changes: number | bigint };
3
+ // Node returns undefined for a missing row; Bun returns null.
4
+ get(...params: unknown[]): Record<string, unknown> | null | undefined;
5
+ all(...params: unknown[]): Record<string, unknown>[];
6
+ };
7
+
8
+ export type SqliteDatabase = {
9
+ exec(sql: string): void;
10
+ prepare(sql: string): Statement;
11
+ close(): void;
12
+ };
13
+
14
+ export type SqliteModule = {
15
+ DatabaseSync: new (path: string, options?: Record<string, unknown>) => SqliteDatabase;
16
+ };
17
+
18
+ type BunDatabase = Omit<SqliteDatabase, "prepare"> & {
19
+ query(sql: string): Statement;
20
+ };
21
+
22
+ /** Load the host's built-in SQLite lazily, including inside Bun-compiled Pi. */
23
+ export async function loadSqlite(): Promise<SqliteModule> {
24
+ if (process.versions.bun) {
25
+ // A runtime specifier avoids requiring Bun's ambient types in Node builds.
26
+ const specifier = "bun:sqlite";
27
+ const sqlite = await import(specifier) as {
28
+ Database: new (path: string, options?: Record<string, unknown>) => BunDatabase;
29
+ };
30
+ return {
31
+ DatabaseSync: class implements SqliteDatabase {
32
+ private readonly database: BunDatabase;
33
+
34
+ constructor(path: string, options?: Record<string, unknown>) {
35
+ this.database = new sqlite.Database(path, options);
36
+ }
37
+
38
+ exec(sql: string): void {
39
+ this.database.exec(sql);
40
+ }
41
+
42
+ prepare(sql: string): Statement {
43
+ // Bun finalizes cached queries on close. Uncached prepare() statements
44
+ // can outlive the connection, retaining locks and delaying checkpoints.
45
+ return this.database.query(sql);
46
+ }
47
+
48
+ close(): void {
49
+ this.database.close();
50
+ }
51
+ },
52
+ };
53
+ }
54
+ return import("node:sqlite") as Promise<SqliteModule>;
55
+ }
@@ -11,24 +11,12 @@ import {
11
11
  } from "node:fs";
12
12
  import { dirname, join, resolve } from "node:path";
13
13
  import type { ClassifiedPermission } from "./classifier.ts";
14
+ import { loadSqlite, type SqliteDatabase, type SqliteModule } from "./sqlite.ts";
14
15
 
15
16
  export const POLICY_AUDIT_SCHEMA_VERSION = 2;
16
17
  export const POLICY_AUDIT_DATABASE_NAME = "policy-audit.sqlite";
17
18
  export const POLICY_AUDIT_KEY_NAME = "policy-audit.key";
18
19
 
19
- type RunResult = { changes: number | bigint };
20
- type Statement = {
21
- run(...params: unknown[]): RunResult;
22
- get(...params: unknown[]): Record<string, unknown> | undefined;
23
- all(...params: unknown[]): Record<string, unknown>[];
24
- };
25
- type Database = {
26
- exec(sql: string): void;
27
- prepare(sql: string): Statement;
28
- close(): void;
29
- };
30
- type SqliteModule = { DatabaseSync: new (path: string, options?: Record<string, unknown>) => Database };
31
-
32
20
  export type SanitizedPermissionDecision = ClassifiedPermission & {
33
21
  requestId: string;
34
22
  result: "allow" | "deny";
@@ -124,7 +112,7 @@ export class PolicyAuditStore {
124
112
  mkdirSync(options.directory, { recursive: true, mode: 0o700 });
125
113
  chmodSync(options.directory, 0o700);
126
114
  const key = loadOrCreateKey(options.directory);
127
- const sqlite = await (options.sqliteLoader ?? (() => import("node:sqlite") as Promise<SqliteModule>))();
115
+ const sqlite = await (options.sqliteLoader ?? loadSqlite)();
128
116
  const databasePath = join(options.directory, POLICY_AUDIT_DATABASE_NAME);
129
117
  const db = new sqlite.DatabaseSync(databasePath);
130
118
  chmodSync(databasePath, 0o600);
@@ -139,7 +127,7 @@ export class PolicyAuditStore {
139
127
  }
140
128
 
141
129
  private constructor(
142
- private readonly db: Database,
130
+ private readonly db: SqliteDatabase,
143
131
  private readonly key: Buffer,
144
132
  private readonly databasePath: string,
145
133
  private readonly retentionDays: number,