opencode-memory-pro 1.3.7 → 1.3.8

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/README.md CHANGED
@@ -40,7 +40,7 @@ Published on npm — install directly (requires OpenCode ≥ 1.x and Node.js ≥
40
40
  opencode plugin opencode-memory-pro
41
41
  ```
42
42
 
43
- The latest release is **v1.3.7** on [npm](https://www.npmjs.com/package/opencode-memory-pro); source and releases are on [GitHub](https://github.com/tman204-50/opencode-memory-pro).
43
+ The latest release is **v1.3.8** on [npm](https://www.npmjs.com/package/opencode-memory-pro); source and releases are on [GitHub](https://github.com/tman204-50/opencode-memory-pro).
44
44
 
45
45
  Remove the old plugin pin at the same time:
46
46
 
@@ -408,6 +408,22 @@ so your memories and graph carry over untouched.
408
408
 
409
409
  ## Changelog
410
410
 
411
+ ### v1.3.8 (2026-09-06)
412
+
413
+ Fixes `memory_forget(force=true)` being unable to permanently delete a memory
414
+ that was soft-deleted first:
415
+
416
+ - **Force delete now sees hidden rows**: `softDeleteMemory` marks a row
417
+ `status='disabled'`, and the force path previously used `deleteById`, whose
418
+ `readByScopes` query filters out `status='disabled'` (and `merged`/`digested`)
419
+ rows — so "Use force=true for permanent deletion" silently failed and left
420
+ the hidden row on disk forever. The force path now uses the new
421
+ `deleteByIdForce` in `dist/store.js`, which tries the exact-id raw delete
422
+ first and otherwise scans unfiltered rows (so id prefixes still match).
423
+ - **Tests**: integration test covers the exact scenario — soft-delete, confirm
424
+ the old path returns `false`, then `deleteByIdForce` removes the row and
425
+ reports `false` on a second attempt.
426
+
411
427
  ### v1.3.7 (2026-09-06)
412
428
 
413
429
  Scope normalization — fixes lost memories when a `scope` argument is
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ import { requestLLMCapture, isOwnSession } from "./llm.js";
11
11
  import { createMemoryTools, createFeedbackTools, createEpisodicTools } from "./tools/index.js";
12
12
  import { sweepExpiredMemories } from "./tools/memory.js";
13
13
  import { createGraphStore } from "./graph.js";
14
- const PLUGIN_VERSION = "1.3.7";
14
+ const PLUGIN_VERSION = "1.3.8";
15
15
  const SCHEMA_VERSION = 1;
16
16
  // Event-driven dedup: run consolidateDuplicates on session.idle (throttled to
17
17
  // this interval so chatty sessions aren't re-scanning the store every turn)
package/dist/store.d.ts CHANGED
@@ -49,6 +49,8 @@ export declare class MemoryStore {
49
49
  globalDiscountFactor?: number;
50
50
  }): Promise<SearchResult[]>;
51
51
  deleteById(id: string, scopes: string[]): Promise<boolean>;
52
+ deleteByIdRaw(id: string): Promise<boolean>;
53
+ deleteByIdForce(id: string): Promise<boolean>;
52
54
  softDeleteMemory(id: string, scopes: string[]): Promise<boolean>;
53
55
  updateMemoryScope(id: string, newScope: string, scopes: string[]): Promise<boolean>;
54
56
  readGlobalMemories(limit?: number): Promise<MemoryRecord[]>;
package/dist/store.js CHANGED
@@ -628,6 +628,28 @@ export class MemoryStore {
628
628
  this.notifyGraphRemoved(id);
629
629
  return true;
630
630
  }
631
+ // DELETE_BY_FORCE (1.3.8): like deleteById but sees rows the status-
632
+ // filtered reads hide (disabled/merged/digested). Fixes memory_forget
633
+ // force=true: the soft-delete path marks rows disabled, and the force
634
+ // path previously used deleteById, whose readByScopes filter excludes
635
+ // status='disabled' — so "Use force=true for permanent deletion" silently
636
+ // failed and left the hidden row on disk forever. Tries the exact-id raw
637
+ // delete first (fast path), then falls back to an unfiltered scan so
638
+ // prefix ids and hidden rows both work.
639
+ async deleteByIdForce(id) {
640
+ if (await this.deleteByIdRaw(id)) {
641
+ return true;
642
+ }
643
+ const table = this.requireTable();
644
+ const rows = await table.query().limit(100000).toArray();
645
+ const match = rows.find((row) => this.matchesId(row.id, id));
646
+ if (!match)
647
+ return false;
648
+ await table.delete(`id = '${escapeSql(match.id)}'`);
649
+ this.invalidateScope(match.scope);
650
+ this.notifyGraphRemoved(match.id);
651
+ return true;
652
+ }
631
653
  async softDeleteMemory(id, scopes) {
632
654
  const rows = await this.readByScopes(scopes);
633
655
  const match = rows.find((row) => this.matchesId(row.id, id));
@@ -447,7 +447,11 @@ export function createMemoryTools(state) {
447
447
  const activeScope = resolveScope(args.scope, context.directory || context.worktree);
448
448
  const scopes = buildScopeFilter(activeScope, state.config.includeGlobalScope);
449
449
  if (args.force) {
450
- const deleted = await state.store.deleteById(args.id, scopes);
450
+ // FORCE_DELETE_HIDDEN (1.3.8): was deleteById, whose
451
+ // readByScopes filter excludes disabled/merged rows — so
452
+ // force=true could never permanently delete a memory that
453
+ // was soft-deleted first.
454
+ const deleted = await state.store.deleteByIdForce(args.id);
451
455
  if (!deleted) {
452
456
  return `Memory ${args.id} not found in current scope.`;
453
457
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-memory-pro",
3
- "version": "1.3.7",
3
+ "version": "1.3.8",
4
4
  "description": "LanceDB-backed long-term memory provider for OpenCode — standalone fork of lancedb-opencode-pro with entity graph, lifecycle, and retention",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",