diff --git a/CHANGELOG.md b/CHANGELOG.md index db8f282..4e0ce22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,16 @@ Format follows [Keep a Changelog](https://keepachangelog.com/). Versions follow --- +## [0.2.2] - 2026-03-21 + +### Fixed + +- `memory_scope_promote`, `memory_delete`, `memory_feedback_wrong`, `memory_feedback_useful` now accept 8-character short IDs (prefix of UUID) in addition to full 36-character UUIDs. Previously, passing a short ID always returned "not found" even though `memory_search` could find the same memory. +- `id` parameter minimum length raised from 6 to 8 characters on all memory tools. +- Fix: DELETE inside `updateMemoryScope` and `updateMemoryUsage` now uses resolved `match.id`, not the prefix argument. + +--- + ## [0.2.1] - 2026-03-21 ### Fixed diff --git a/package.json b/package.json index 28ffa07..a0b7e37 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lancedb-opencode-pro", - "version": "0.2.1", + "version": "0.2.2", "description": "LanceDB-backed long-term memory provider for OpenCode", "type": "module", "main": "dist/index.js", diff --git a/src/index.ts b/src/index.ts index 032da0c..f1ae3af 100644 --- a/src/index.ts +++ b/src/index.ts @@ -164,7 +164,7 @@ const plugin: Plugin = async (input) => { memory_delete: tool({ description: "Delete one memory entry by id", args: { - id: tool.schema.string().min(6), + id: tool.schema.string().min(8), scope: tool.schema.string().optional(), confirm: tool.schema.boolean().default(false), }, @@ -254,7 +254,7 @@ const plugin: Plugin = async (input) => { memory_feedback_wrong: tool({ description: "Record feedback for memory that should not be stored", args: { - id: tool.schema.string().min(6), + id: tool.schema.string().min(8), reason: tool.schema.string().optional(), scope: tool.schema.string().optional(), }, @@ -284,7 +284,7 @@ const plugin: Plugin = async (input) => { memory_feedback_useful: tool({ description: "Record whether a recalled memory was helpful", args: { - id: tool.schema.string().min(6), + id: tool.schema.string().min(8), helpful: tool.schema.boolean(), scope: tool.schema.string().optional(), }, @@ -327,7 +327,7 @@ const plugin: Plugin = async (input) => { memory_scope_promote: tool({ description: "Promote a memory from project scope to global scope for cross-project sharing", args: { - id: tool.schema.string().min(6), + id: tool.schema.string().min(8), confirm: tool.schema.boolean().default(false), }, execute: async (args, context) => { @@ -352,7 +352,7 @@ const plugin: Plugin = async (input) => { memory_scope_demote: tool({ description: "Demote a memory from global scope to project scope", args: { - id: tool.schema.string().min(6), + id: tool.schema.string().min(8), confirm: tool.schema.boolean().default(false), scope: tool.schema.string().optional(), }, diff --git a/src/store.ts b/src/store.ts index 481e4c0..deb8cd6 100644 --- a/src/store.ts +++ b/src/store.ts @@ -222,7 +222,7 @@ export class MemoryStore { async deleteById(id: string, scopes: string[]): Promise { const rows = await this.readByScopes(scopes); - const match = rows.find((row) => row.id === id); + const match = rows.find((row) => this.matchesId(row.id, id)); if (!match) return false; await this.requireTable().delete(`id = '${escapeSql(match.id)}'`); this.invalidateScope(match.scope); @@ -231,10 +231,10 @@ export class MemoryStore { async updateMemoryScope(id: string, newScope: string, scopes: string[]): Promise { const rows = await this.readByScopes(scopes); - const match = rows.find((row) => row.id === id); + const match = rows.find((row) => this.matchesId(row.id, id)); if (!match) return false; - await this.requireTable().delete(`id = '${escapeSql(id)}'`); + await this.requireTable().delete(`id = '${escapeSql(match.id)}'`); this.invalidateScope(match.scope); await this.requireTable().add([{ ...match, scope: newScope }]); @@ -282,10 +282,15 @@ export class MemoryStore { return rows.filter((row) => row.vectorDim !== expectedDim).length; } + private matchesId(candidateId: string, query: string): boolean { + if (query.length >= 36) return candidateId === query; + return candidateId.startsWith(query); + } + async hasMemory(id: string, scopes: string[]): Promise { for (let attempt = 0; attempt < 3; attempt++) { const rows = await this.readByScopes(scopes); - if (rows.some((row) => row.id === id)) { + if (rows.some((row) => this.matchesId(row.id, id))) { return true; } if (attempt < 2) { @@ -297,7 +302,7 @@ export class MemoryStore { async updateMemoryUsage(id: string, projectScope: string, scopes: string[]): Promise { const rows = await this.readByScopes(scopes); - const match = rows.find((row) => row.id === id); + const match = rows.find((row) => this.matchesId(row.id, id)); if (!match) return; const now = Date.now(); @@ -321,7 +326,7 @@ export class MemoryStore { } } - await this.requireTable().delete(`id = '${escapeSql(id)}'`); + await this.requireTable().delete(`id = '${escapeSql(match.id)}'`); this.invalidateScope(match.scope); await this.requireTable().add([{