@log0u7/opencode-memory 0.1.2 → 0.3.0

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
@@ -5,7 +5,66 @@ All notable changes to this project are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [Unreleased]
8
+ ## [0.3.0] - 2026-10-14
9
+
10
+ ### Added
11
+
12
+ - Deterministic injection: the first request of each session carries a bounded list of the
13
+ project's most recent entries (titles, kinds, dates, no bodies, ~1200 characters worst
14
+ case), so using memory no longer depends on the model remembering to call
15
+ `memory_search`. The tool stays for deeper lookups.
16
+ - Compaction inventory: the compaction context now lists the stored entries next to the
17
+ save instruction, so the model summarises from an inventory instead of recall and stops
18
+ re-saving what is already stored.
19
+
20
+ ### Changed
21
+
22
+ - Both plugin entry points (`server` for OpenCode 1, `setup` for OpenCode 2) now delegate
23
+ to one shared core (`src/lib/memory-ops.ts`): tool behaviour, error-to-text mapping,
24
+ caps and rendering live in a single place, and each host is thin SDK glue. No
25
+ behavioural change.
26
+ - The system guidance no longer tells the model to search at task start (the injection
27
+ covers it); the credential warning and both tool mentions stay.
28
+ - CONTRIBUTING.md states the merge-policy rationale instead of a solo-maintainer
29
+ preference: `--no-ff` keeps one merge commit per PR, which reads as the PR list via
30
+ `git log --merges --first-parent main` and is a single atomic revert unit, with a
31
+ five-commit PR ceiling bounding the cost. Documentation only, no package change.
32
+ - `memory_save` rewrites an entry that repeats a scope and title instead of adding a
33
+ duplicate, which stopped the store from growing with every compaction.
34
+ - Saving purges rows past their `expires_at` instead of only hiding them at query time.
35
+
36
+ ### Fixed
37
+
38
+ - `memory_forget` by query or id could delete entries of other projects: deletes are now
39
+ scoped to the caller's scope (plus `global`), like `memory_search` and `memory_list`.
40
+ - A model-supplied `limit` crashed `memory_search`/`memory_list` (fractional, `NaN`) or
41
+ returned the whole store (`-1`, which SQLite reads as unbounded). Limits are clamped to
42
+ 1..50, and tool results are bounded: bodies render as 500-character previews.
43
+ - A credential in an entry `title` or `kind` bypassed the secret guard; the scan now covers
44
+ title, kind, body and tags, and `kind` has a length cap.
45
+ - Search returned nothing for partial queries (`"migration strategy"` found no entry
46
+ containing "migration", and `"migrat"` found nothing): a strict AND match is now retried
47
+ as a prefix OR match, and an empty query returns no results instead of failing.
48
+ - The two plugin entry points defaulted to different scopes for the same project (v1 used
49
+ the session worktree, v2 the plugin directory) and disagreed about trailing slashes.
50
+ Both now derive one canonical scope key.
51
+ - The published package could not be loaded by a Node host: `src/lib/sqlite.ts` called a
52
+ bare `require("node:sqlite")`, which is undefined in an ES module. The Node branch now
53
+ uses `createRequire(import.meta.url)`.
54
+
55
+ ### Removed
56
+
57
+ - README advertised an FTS5 "recency boost" that the code never implemented; the actual
58
+ ranking (BM25, newest first on ties) is documented instead.
59
+
60
+ ## [0.2.0] - 2026-10-12
61
+
62
+ ### Added
63
+
64
+ - Dual-export plugin entry for OpenCode 2: one published package runs on both
65
+ OpenCode 1.18 and 2.x (official migration pattern, "Support V1 and V2 from
66
+ one package"). v1 hosts call `server()` (unchanged); v2 hosts call `setup()`
67
+ (`Plugin.define`). Both entries drive the same engine.
9
68
 
10
69
  ## [0.1.2] - 2026-09-23
11
70
 
package/README.md CHANGED
@@ -2,11 +2,20 @@
2
2
 
3
3
  [![CI](https://github.com/log0u7/opencode-memory/actions/workflows/ci.yml/badge.svg)](https://github.com/log0u7/opencode-memory/actions/workflows/ci.yml)
4
4
  [![npm](https://img.shields.io/npm/v/@log0u7/opencode-memory)](https://www.npmjs.com/package/@log0u7/opencode-memory)
5
+ [![Node.js](https://img.shields.io/node/v/@log0u7/opencode-memory?logo=node.js&logoColor=white)](https://www.npmjs.com/package/@log0u7/opencode-memory)
6
+ [![TypeScript](https://img.shields.io/github/package-json/dependency-version/log0u7/opencode-memory/dev/typescript?logo=typescript&logoColor=white)](https://github.com/log0u7/opencode-memory/blob/main/package.json)
5
7
  [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
6
8
 
7
9
  Working memory for [OpenCode](https://opencode.ai): a shared SQLite store that all sessions on a machine read and write, so decisions, facts, and compaction summaries survive session boundaries.
8
10
 
9
- Every session gets four tools (`memory_save`, `memory_search`, `memory_list`, `memory_forget`). Entries are scoped per project worktree (plus a `global` scope), searched with SQLite FTS5 (BM25 ranking with a recency boost). 100% local, no cloud, no API keys.
11
+ Every session gets four tools (`memory_save`, `memory_search`, `memory_list`, `memory_forget`). Entries are scoped per project worktree (plus a `global` scope), searched with SQLite FTS5 (BM25 ranking, strict match first with a prefix fallback). Recent entries are also injected into the prompt: once per session, and again during compaction. 100% local, no cloud, no API keys.
12
+
13
+ ## Safety limits
14
+
15
+ - Results are capped: at most 50 entries per call, and each body is rendered as a 500-character preview. A tool result has a bounded size whatever the model asks for.
16
+ - `memory_save` rewrites an entry that repeats a scope and title instead of stacking duplicates, and purges expired rows.
17
+ - `memory_forget` only deletes inside the caller's scope (plus the shared `global` scope).
18
+ - Entries that look like secrets (`sk-…`, `ghp_…`, `xoxb-…`) are refused, in the title, kind, body and tags alike.
10
19
 
11
20
  ## Install
12
21
 
@@ -46,7 +55,7 @@ memory_forget(id="...")
46
55
  | Tool | Args | Description |
47
56
  |---|---|---|
48
57
  | `memory_save` | `kind`, `title`, `body`, `scope?`, `tags?`, `expires_at?` | Save an entry. Default scope is the session worktree. |
49
- | `memory_search` | `query`, `scope?`, `limit?` | Hybrid search: FTS5 BM25 + recency boost. |
58
+ | `memory_search` | `query`, `scope?`, `limit?` | FTS5 BM25 search; strict match first, prefix OR fallback when nothing matches. |
50
59
  | `memory_list` | `scope?`, `limit?` | Recent entries, newest first. |
51
60
  | `memory_forget` | `id` or `query` | Delete one entry by id, or all matching a query. |
52
61
 
@@ -55,8 +64,12 @@ Entry `kind` is free-form; conventions that work well: `decision`, `fact`, `pref
55
64
  ### Notes
56
65
 
57
66
  - All sessions on the machine share one store (WAL mode, concurrent-safe).
58
- - Entries that look like secrets (`sk-…`, `ghp_…`, `xoxb-…`) are refused.
59
- - On session compaction, the plugin asks the compaction prompt to emit durable decisions so the model can persist them.
67
+ - Entries that look like secrets (`sk-…`, `ghp_…`, `xoxb-…`) are refused, in the title, kind, body and tags.
68
+ - Every repeated `memory_save` of the same title in the same scope rewrites the entry, so the store does not grow with duplicates, and expired rows are purged on save.
69
+ - `memory_forget` never leaves the scope it is called from, so a project cannot delete another project's entries.
70
+ - On the first request of a session, the most recent entries are injected into the system prompt as a bounded list (titles only), so the model does not have to remember to search. `memory_search` remains the tool for deeper lookups.
71
+ - On session compaction, the plugin lists the stored entries in the compaction context and asks the compaction prompt to emit durable decisions, so the model summarises from an inventory instead of recall.
72
+ - Injections stay bounded (~1200 characters worst case) and nothing is injected, nor any store file created, when memory is unused.
60
73
 
61
74
  ## Development
62
75
 
package/dist/index.d.ts CHANGED
@@ -5,8 +5,13 @@
5
5
  *
6
6
  * @packageDocumentation
7
7
  */
8
- declare const pluginModule: {
8
+ /**
9
+ * Dual entrypoint: v1 hosts call `server()`, v2 hosts call `setup()`
10
+ * (see https://opencode.ai/v2/docs/build/plugins/migrate-v1).
11
+ */
12
+ export declare const pluginModule: {
9
13
  id: string;
14
+ setup: (context: import("@opencode/plugin/promise/plugin").Context) => Promise<import("@opencode/plugin/promise/plugin").Cleanup | void> | import("@opencode/plugin/promise/plugin").Cleanup | void;
10
15
  server: import("@opencode-ai/plugin").Plugin;
11
16
  };
12
17
  export default pluginModule;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,QAAA,MAAM,YAAY;;;CAGqC,CAAC;eAEzC,YAAY;AAE3B,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAErE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH;;;GAGG;AACH,eAAO,MAAM,YAAY;;;;CAGiE,CAAC;eAE5E,YAAY;AAE3B,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAErE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js CHANGED
@@ -5,9 +5,14 @@
5
5
  *
6
6
  * @packageDocumentation
7
7
  */
8
+ import { MemoryV2 } from "./plugin-v2.js";
8
9
  import { MemoryPlugin } from "./plugin.js";
9
- const pluginModule = {
10
- id: "@log0u7/opencode-memory",
10
+ /**
11
+ * Dual entrypoint: v1 hosts call `server()`, v2 hosts call `setup()`
12
+ * (see https://opencode.ai/v2/docs/build/plugins/migrate-v1).
13
+ */
14
+ export const pluginModule = {
15
+ ...MemoryV2,
11
16
  server: MemoryPlugin,
12
17
  };
13
18
  export default pluginModule;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,YAAY,GAAG;IACnB,EAAE,EAAE,yBAAyB;IAC7B,MAAM,EAAE,YAAY;CACiC,CAAC;AAExD,eAAe,YAAY,CAAC;AAI5B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,GAAG,QAAQ;IACX,MAAM,EAAE,YAAY;CACoE,CAAC;AAE3F,eAAe,YAAY,CAAC;AAI5B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/lib/db.ts"],"names":[],"mappings":"AAGA,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAE1D,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,IAAI,IAAI,CAAC;CACf,CAAC;AA8BF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAWnD"}
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/lib/db.ts"],"names":[],"mappings":"AAGA,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAE1D,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,IAAI,IAAI,CAAC;CACf,CAAC;AA+BF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAWnD"}
package/dist/lib/db.js CHANGED
@@ -27,6 +27,7 @@ CREATE TRIGGER IF NOT EXISTS entries_au AFTER UPDATE ON entries BEGIN
27
27
  INSERT INTO entries_fts(entries_fts, rowid, title, body, tags) VALUES ('delete', old.rowid, old.title, old.body, old.tags);
28
28
  INSERT INTO entries_fts(rowid, title, body, tags) VALUES (new.rowid, new.title, new.body, new.tags);
29
29
  END;
30
+ CREATE INDEX IF NOT EXISTS entries_scope_title ON entries(scope, lower(trim(title)));
30
31
  `;
31
32
  export function openMemoryDb(path) {
32
33
  if (path !== ":memory:") {
@@ -1 +1 @@
1
- {"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/lib/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,UAAU,EAAmB,MAAM,aAAa,CAAC;AAO1D,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;CA0Bd,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;QACxB,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;IACtC,IAAI,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;IACvC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAElB,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;AAC7C,CAAC"}
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/lib/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,UAAU,EAAmB,MAAM,aAAa,CAAC;AAO1D,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2Bd,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;QACxB,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;IACtC,IAAI,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;IACvC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAElB,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,55 @@
1
+ import type { MemoryDb } from "./db.js";
2
+ export type SaveArgs = {
3
+ kind: string;
4
+ title: string;
5
+ body: string;
6
+ tags?: string[] | undefined;
7
+ expires_at?: number | null | undefined;
8
+ };
9
+ export type SearchArgs = {
10
+ query: string;
11
+ limit?: number | undefined;
12
+ };
13
+ export type ListArgs = {
14
+ limit?: number | undefined;
15
+ };
16
+ export type ForgetArgs = {
17
+ id?: string | undefined;
18
+ query?: string | undefined;
19
+ };
20
+ /** One shared behaviour behind both plugin entry points. Every operation returns the
21
+ * model-facing text directly, so a caller cannot bypass caps or error handling. */
22
+ export type MemoryOps = {
23
+ save: (args: SaveArgs, scope: string) => string;
24
+ search: (args: SearchArgs, scope: string) => string;
25
+ list: (args: ListArgs, scope: string) => string;
26
+ forget: (args: ForgetArgs, scope: string) => string;
27
+ };
28
+ export declare function createMemoryOps(getDb: () => MemoryDb): MemoryOps;
29
+ /** How much of the store is summarised into a prompt, worst case. */
30
+ export declare const INVENTORY_MAX_CHARS = 1200;
31
+ /** Tool descriptions shared by both entry points so the two hosts cannot drift. */
32
+ export declare const TOOL_DESCRIPTIONS: {
33
+ readonly save: "Save a durable entry to shared working memory (decisions, facts, preferences, gotchas). Never store credentials.";
34
+ readonly search: "Search shared working memory (all sessions on this machine). Results include this project's scope and global entries.";
35
+ readonly list: "List recent memory entries for a scope (default: this project), newest first.";
36
+ readonly forget: "Delete memory entries by id, or all entries matching a query. Deletes only inside this project's scope (plus global entries); an entry saved under another scope needs that scope to be removed.";
37
+ };
38
+ /**
39
+ * Produces at most one injection per session key, and only on the first request where
40
+ * the producer has something: a session that starts before any entry exists still gets
41
+ * its inventory as soon as one is available. Keys are capped so a long-lived host
42
+ * cannot grow the set forever; an absent sessionID (v1 marks it optional) degrades to
43
+ * one injection per plugin instance, never per request.
44
+ */
45
+ export declare function oncePerSession(): {
46
+ inject: (key: string | undefined, produce: () => string | null) => string | null;
47
+ reset: () => void;
48
+ };
49
+ /**
50
+ * Bounded summary of recent entries, injected into the first request of a session and
51
+ * into the compaction context. `null` when there is nothing worth injecting, so a
52
+ * caller pushes nothing at all.
53
+ */
54
+ export declare function memoryInventory(getDb: () => MemoryDb, dbPath: string, scope: string): string | null;
55
+ //# sourceMappingURL=memory-ops.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-ops.d.ts","sourceRoot":"","sources":["../../src/lib/memory-ops.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAKxC,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CACxC,CAAC;AACF,MAAM,MAAM,UAAU,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AACvE,MAAM,MAAM,QAAQ,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AACtD,MAAM,MAAM,UAAU,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAEjF;mFACmF;AACnF,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAChD,MAAM,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACpD,IAAI,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAChD,MAAM,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;CACrD,CAAC;AAEF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,QAAQ,GAAG,SAAS,CA4ChE;AAED,qEAAqE;AACrE,eAAO,MAAM,mBAAmB,OAAO,CAAC;AAIxC,mFAAmF;AACnF,eAAO,MAAM,iBAAiB;aAC5B,IAAI,EAAE,kHAAkH;aACxH,MAAM,EACJ,uHAAuH;aACzH,IAAI,EAAE,+EAA+E;aACrF,MAAM,EACJ,kMAAkM;CAC5L,CAAC;AAEX;;;;;;GAMG;AACH,wBAAgB,cAAc;kBAGP,MAAM,GAAG,SAAS,WAAW,MAAM,MAAM,GAAG,IAAI,KAAG,MAAM,GAAG,IAAI;;EAgBtF;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,QAAQ,EACrB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ,MAAM,GAAG,IAAI,CAqBf"}
@@ -0,0 +1,125 @@
1
+ import { existsSync } from "node:fs";
2
+ import { forget, list, renderEntries, save, search, SecretDetectedError } from "./store.js";
3
+ export function createMemoryOps(getDb) {
4
+ return {
5
+ save: (args, scope) => run(() => {
6
+ const entry = save(getDb(), {
7
+ scope,
8
+ kind: args.kind,
9
+ title: args.title,
10
+ body: args.body,
11
+ tags: args.tags ?? [],
12
+ expires_at: args.expires_at ?? null,
13
+ });
14
+ return `saved ${entry.id} (${entry.scope})`;
15
+ }),
16
+ search: (args, scope) => run(() => {
17
+ const hits = search(getDb(), {
18
+ query: args.query,
19
+ scope,
20
+ ...(args.limit !== undefined ? { limit: args.limit } : {}),
21
+ });
22
+ return hits.length === 0 ? "no matching memory entries" : renderEntries(hits);
23
+ }),
24
+ list: (args, scope) => run(() => {
25
+ const entries = list(getDb(), {
26
+ scope,
27
+ ...(args.limit !== undefined ? { limit: args.limit } : {}),
28
+ });
29
+ return entries.length === 0 ? "no memory entries" : renderEntries(entries);
30
+ }),
31
+ forget: (args, scope) => run(() => {
32
+ if (args.id !== undefined) {
33
+ const deleted = forget(getDb(), { id: args.id, scope });
34
+ return `deleted ${deleted} entr${deleted === 1 ? "y" : "ies"}`;
35
+ }
36
+ if (args.query !== undefined) {
37
+ const deleted = forget(getDb(), { query: args.query, scope });
38
+ return `deleted ${deleted} entr${deleted === 1 ? "y" : "ies"}`;
39
+ }
40
+ return "provide id or query";
41
+ }),
42
+ };
43
+ }
44
+ /** How much of the store is summarised into a prompt, worst case. */
45
+ export const INVENTORY_MAX_CHARS = 1200;
46
+ const INVENTORY_HEADER = "Memory already stored for this project (newest first):";
47
+ /** Tool descriptions shared by both entry points so the two hosts cannot drift. */
48
+ export const TOOL_DESCRIPTIONS = {
49
+ save: "Save a durable entry to shared working memory (decisions, facts, preferences, gotchas). Never store credentials.",
50
+ search: "Search shared working memory (all sessions on this machine). Results include this project's scope and global entries.",
51
+ list: "List recent memory entries for a scope (default: this project), newest first.",
52
+ forget: "Delete memory entries by id, or all entries matching a query. Deletes only inside this project's scope (plus global entries); an entry saved under another scope needs that scope to be removed.",
53
+ };
54
+ /**
55
+ * Produces at most one injection per session key, and only on the first request where
56
+ * the producer has something: a session that starts before any entry exists still gets
57
+ * its inventory as soon as one is available. Keys are capped so a long-lived host
58
+ * cannot grow the set forever; an absent sessionID (v1 marks it optional) degrades to
59
+ * one injection per plugin instance, never per request.
60
+ */
61
+ export function oncePerSession() {
62
+ const seen = new Set();
63
+ const MAX_KEYS = 512;
64
+ const inject = (key, produce) => {
65
+ const k = key ?? "__instance__";
66
+ if (seen.has(k)) {
67
+ return null;
68
+ }
69
+ const produced = produce();
70
+ if (produced === null) {
71
+ return null;
72
+ }
73
+ seen.add(k);
74
+ if (seen.size > MAX_KEYS) {
75
+ seen.clear();
76
+ }
77
+ return produced;
78
+ };
79
+ return { inject, reset: () => seen.clear() };
80
+ }
81
+ /**
82
+ * Bounded summary of recent entries, injected into the first request of a session and
83
+ * into the compaction context. `null` when there is nothing worth injecting, so a
84
+ * caller pushes nothing at all.
85
+ */
86
+ export function memoryInventory(getDb, dbPath, scope) {
87
+ if (!existsSync(dbPath)) {
88
+ return null;
89
+ }
90
+ const entries = list(getDb(), { scope, limit: 50 });
91
+ if (entries.length === 0) {
92
+ return null;
93
+ }
94
+ const lines = [INVENTORY_HEADER];
95
+ let size = INVENTORY_HEADER.length;
96
+ for (const entry of entries) {
97
+ const line = `- [${entry.kind}] ${entry.title} (updated ${dayOf(entry.updated_at)})`;
98
+ if (size + line.length + 1 > INVENTORY_MAX_CHARS) {
99
+ break;
100
+ }
101
+ lines.push(line);
102
+ size += line.length + 1;
103
+ }
104
+ return lines.join("\n");
105
+ }
106
+ function dayOf(epochMs) {
107
+ return new Date(epochMs).toISOString().slice(0, 10);
108
+ }
109
+ // Tool results are shown to the model: caught errors become readable text, so a
110
+ // rejected save is guidance instead of a crashed tool call.
111
+ function run(fn) {
112
+ try {
113
+ return fn();
114
+ }
115
+ catch (error) {
116
+ if (error instanceof SecretDetectedError) {
117
+ return "refused: the content looks like a credential; secrets must never be stored in memory";
118
+ }
119
+ if (error instanceof Error) {
120
+ return `error: ${error.message}`;
121
+ }
122
+ throw error;
123
+ }
124
+ }
125
+ //# sourceMappingURL=memory-ops.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-ops.js","sourceRoot":"","sources":["../../src/lib/memory-ops.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/lib/sqlite.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,CAAC;IACvD,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;IAC5D,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC3C,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,IAAI,IAAI,CAAC;CACf;AAqED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAanD"}
1
+ {"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/lib/sqlite.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,CAAC;IACvD,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;IAC5D,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC3C,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,IAAI,IAAI,CAAC;CACf;AAqED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAanD"}
@@ -1,3 +1,4 @@
1
+ import { createRequire } from "node:module";
1
2
  function createPreparedConn(db) {
2
3
  return {
3
4
  all(sql, params) {
@@ -46,9 +47,10 @@ export function openSqlite(path) {
46
47
  if (globalRuntime.Bun) {
47
48
  return createBunConn(new globalRuntime.Bun.sqlite.Database(path));
48
49
  }
49
- // node:sqlite ships with Node >= 22.5; require keeps non-node runtimes from
50
- // failing at module load and gives tests a clear error if the engine is absent.
51
- const { DatabaseSync } = require("node:sqlite");
50
+ // node:sqlite ships with Node >= 22.5. createRequire keeps the load lazy for
51
+ // runtimes without node:sqlite: a bare require is not defined in an ES module.
52
+ const { DatabaseSync } = nodeRequire("node:sqlite");
52
53
  return createPreparedConn(new DatabaseSync(path));
53
54
  }
55
+ const nodeRequire = createRequire(import.meta.url);
54
56
  //# sourceMappingURL=sqlite.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../../src/lib/sqlite.ts"],"names":[],"mappings":"AA8BA,SAAS,kBAAkB,CAAC,EAAsB;IAChD,OAAO;QACL,GAAG,CAAc,GAAW,EAAE,MAAkB;YAC9C,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC7B,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAQ,CAAC;QAC5C,CAAC;QACD,GAAG,CAAc,GAAW,EAAE,MAAkB;YAC9C,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC7B,OAAQ,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAmB,IAAI,IAAI,CAAC;QAChE,CAAC;QACD,GAAG,CAAC,GAAW,EAAE,MAAkB;YACjC,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC7B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC;QAC9B,CAAC;QACD,IAAI,CAAC,GAAW;YACd,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACf,CAAC;QACD,KAAK;YACH,EAAE,CAAC,KAAK,EAAE,CAAC;QACb,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,EAAqB;IAC1C,OAAO;QACL,GAAG,CAAc,GAAW,EAAE,MAAkB;YAC9C,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAQ,CAAC;QACrD,CAAC;QACD,GAAG,CAAc,GAAW,EAAE,MAAkB;YAC9C,OAAQ,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAmB,IAAI,IAAI,CAAC;QACzE,CAAC;QACD,GAAG,CAAC,GAAW,EAAE,MAAkB;YACjC,EAAE,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC;QAC5B,CAAC;QACD,IAAI,CAAC,GAAW;YACd,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,CAAC;QACD,KAAK;YACH,EAAE,CAAC,KAAK,EAAE,CAAC;QACb,CAAC;KACF,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,kFAAkF;AAClF,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,aAAa,GAAG,UAErB,CAAC;IAEF,IAAI,aAAa,CAAC,GAAG,EAAE,CAAC;QACtB,OAAO,aAAa,CAAC,IAAI,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;IACpE,CAAC;IAED,4EAA4E;IAC5E,gFAAgF;IAChF,MAAM,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,aAAa,CAAsB,CAAC;IACrE,OAAO,kBAAkB,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;AACpD,CAAC"}
1
+ {"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../../src/lib/sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAgC5C,SAAS,kBAAkB,CAAC,EAAsB;IAChD,OAAO;QACL,GAAG,CAAc,GAAW,EAAE,MAAkB;YAC9C,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC7B,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAQ,CAAC;QAC5C,CAAC;QACD,GAAG,CAAc,GAAW,EAAE,MAAkB;YAC9C,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC7B,OAAQ,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAmB,IAAI,IAAI,CAAC;QAChE,CAAC;QACD,GAAG,CAAC,GAAW,EAAE,MAAkB;YACjC,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC7B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC;QAC9B,CAAC;QACD,IAAI,CAAC,GAAW;YACd,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACf,CAAC;QACD,KAAK;YACH,EAAE,CAAC,KAAK,EAAE,CAAC;QACb,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,EAAqB;IAC1C,OAAO;QACL,GAAG,CAAc,GAAW,EAAE,MAAkB;YAC9C,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAQ,CAAC;QACrD,CAAC;QACD,GAAG,CAAc,GAAW,EAAE,MAAkB;YAC9C,OAAQ,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAmB,IAAI,IAAI,CAAC;QACzE,CAAC;QACD,GAAG,CAAC,GAAW,EAAE,MAAkB;YACjC,EAAE,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC;QAC5B,CAAC;QACD,IAAI,CAAC,GAAW;YACd,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,CAAC;QACD,KAAK;YACH,EAAE,CAAC,KAAK,EAAE,CAAC;QACb,CAAC;KACF,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,kFAAkF;AAClF,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,aAAa,GAAG,UAErB,CAAC;IAEF,IAAI,aAAa,CAAC,GAAG,EAAE,CAAC;QACtB,OAAO,aAAa,CAAC,IAAI,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;IACpE,CAAC;IAED,6EAA6E;IAC7E,+EAA+E;IAC/E,MAAM,EAAE,YAAY,EAAE,GAAG,WAAW,CAAC,aAAa,CAAsB,CAAC;IACzE,OAAO,kBAAkB,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,WAAW,GAAG,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC"}
@@ -1,8 +1,38 @@
1
1
  import type { MemoryDb } from "./db.js";
2
2
  import type { Entry, EntryInput } from "../types/entry.js";
3
+ export declare const TAG_MAX = 64;
4
+ export declare const KIND_MAX = 64;
5
+ /** Hard ceiling on how many entries one read can return, whatever the caller asks. */
6
+ export declare const MAX_LIMIT = 50;
7
+ /** A rendered body is a preview, never the full text: it bounds a tool result. */
8
+ export declare const BODY_PREVIEW_MAX = 500;
9
+ export declare const DEFAULT_LIMIT: {
10
+ search: number;
11
+ list: number;
12
+ };
13
+ /** Scope used when no worktree and no directory is known, so rows stay addressable. */
14
+ export declare const FALLBACK_SCOPE = "/";
3
15
  export declare class SecretDetectedError extends Error {
4
16
  constructor();
5
17
  }
18
+ /**
19
+ * One canonical scope string for one location. Callers hand in a worktree, a
20
+ * directory, or both, so an entry written by either plugin entry point is found
21
+ * by the other.
22
+ */
23
+ export declare function canonicalScope(...candidates: Array<string | undefined>): string;
24
+ /** A model-supplied limit is untrusted: SQLite reads a negative LIMIT as unbounded,
25
+ * so anything below one falls back to the default, and anything above the store
26
+ * ceiling is clamped. */
27
+ export declare function clampLimit(requested: number | undefined, fallback: number): number;
28
+ /**
29
+ * Model-facing rendering of a result set. Compact JSON and truncated bodies keep a
30
+ * tool result inside a known size regardless of how much is stored.
31
+ */
32
+ export declare function renderEntries(entries: Entry[]): string;
33
+ export declare function sweep(db: MemoryDb, options?: {
34
+ now?: number;
35
+ }): number;
6
36
  export declare function save(db: MemoryDb, input: EntryInput, options?: {
7
37
  now?: number;
8
38
  }): Entry;
@@ -17,9 +47,17 @@ export declare function list(db: MemoryDb, query: {
17
47
  limit?: number;
18
48
  now?: number;
19
49
  }): Entry[];
20
- export declare function forget(db: MemoryDb, target: {
50
+ export type ForgetTarget = {
51
+ scope: string;
52
+ } & ({
21
53
  id: string;
22
54
  } | {
23
55
  query: string;
24
- }): number;
56
+ });
57
+ /**
58
+ * Deletes inside one scope (plus the shared `global` scope), never across the whole
59
+ * machine: the store is shared, so an unscoped delete would silently hit other
60
+ * projects.
61
+ */
62
+ export declare function forget(db: MemoryDb, target: ForgetTarget): number;
25
63
  //# sourceMappingURL=store.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/lib/store.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAY3D,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,cAGC;CACF;AAqBD,wBAAgB,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,KAAK,CA+B3F;AAED,wBAAgB,MAAM,CACpB,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACpE,KAAK,EAAE,CAcT;AAED,wBAAgB,IAAI,CAClB,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACrD,KAAK,EAAE,CAaT;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAavF"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/lib/store.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAK3D,eAAO,MAAM,OAAO,KAAK,CAAC;AAC1B,eAAO,MAAM,QAAQ,KAAK,CAAC;AAC3B,sFAAsF;AACtF,eAAO,MAAM,SAAS,KAAK,CAAC;AAC5B,kFAAkF;AAClF,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAEpC,eAAO,MAAM,aAAa;IAAK,MAAM;IAAM,IAAI;CAAM,CAAC;AAEtD,uFAAuF;AACvF,eAAO,MAAM,cAAc,MAAM,CAAC;AAQlC,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,cAGC;CACF;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,UAAU,EAAE,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC,GAAG,MAAM,CAM/E;AA+BD;;yBAEyB;AACzB,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKlF;AASD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,CAEtD;AAED,wBAAgB,KAAK,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,MAAM,CAI1E;AAED,wBAAgB,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,KAAK,CAuE3F;AAED,wBAAgB,MAAM,CACpB,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACpE,KAAK,EAAE,CAqBT;AAED,wBAAgB,IAAI,CAClB,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACrD,KAAK,EAAE,CAcT;AAED,MAAM,MAAM,YAAY,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,CAAC;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEpF;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,GAAG,MAAM,CAwBjE"}
package/dist/lib/store.js CHANGED
@@ -2,6 +2,15 @@ import { randomUUID } from "node:crypto";
2
2
  const TITLE_MAX = 120;
3
3
  const BODY_MAX = 8000;
4
4
  const TAGS_MAX = 12;
5
+ export const TAG_MAX = 64;
6
+ export const KIND_MAX = 64;
7
+ /** Hard ceiling on how many entries one read can return, whatever the caller asks. */
8
+ export const MAX_LIMIT = 50;
9
+ /** A rendered body is a preview, never the full text: it bounds a tool result. */
10
+ export const BODY_PREVIEW_MAX = 500;
11
+ export const DEFAULT_LIMIT = { search: 10, list: 20 };
12
+ /** Scope used when no worktree and no directory is known, so rows stay addressable. */
13
+ export const FALLBACK_SCOPE = "/";
5
14
  const SECRET_PATTERNS = [
6
15
  /\bsk-[A-Za-z0-9_-]{20,}\b/,
7
16
  /\bghp_[A-Za-z0-9]{30,}\b/,
@@ -13,6 +22,18 @@ export class SecretDetectedError extends Error {
13
22
  this.name = "SecretDetectedError";
14
23
  }
15
24
  }
25
+ /**
26
+ * One canonical scope string for one location. Callers hand in a worktree, a
27
+ * directory, or both, so an entry written by either plugin entry point is found
28
+ * by the other.
29
+ */
30
+ export function canonicalScope(...candidates) {
31
+ const first = candidates.find((c) => typeof c === "string" && c.trim().length > 0);
32
+ if (first === undefined) {
33
+ return FALLBACK_SCOPE;
34
+ }
35
+ return first.trim().replace(/\/+$/, "") || FALLBACK_SCOPE;
36
+ }
16
37
  function validateEntry(input) {
17
38
  if (input.title.length === 0 || input.title.length > TITLE_MAX) {
18
39
  throw new TypeError(`title must be 1..${TITLE_MAX} characters`);
@@ -20,20 +41,91 @@ function validateEntry(input) {
20
41
  if (input.body.length > BODY_MAX) {
21
42
  throw new TypeError(`body must be at most ${BODY_MAX} characters`);
22
43
  }
44
+ if (input.kind.length === 0 || input.kind.length > KIND_MAX) {
45
+ throw new TypeError(`kind must be 1..${KIND_MAX} characters`);
46
+ }
23
47
  if (input.tags.length > TAGS_MAX) {
24
48
  throw new TypeError(`at most ${TAGS_MAX} tags allowed`);
25
49
  }
26
- if (SECRET_PATTERNS.some((pattern) => pattern.test(input.body) || input.tags.some((t) => pattern.test(t)))) {
50
+ // A tool result stays bounded, so a tag cannot smuggle an unbounded payload either.
51
+ if (input.tags.some((tag) => tag.length > TAG_MAX)) {
52
+ throw new TypeError(`each tag must be at most ${TAG_MAX} characters`);
53
+ }
54
+ if (SECRET_PATTERNS.some((pattern) => looksLikeSecret(input, pattern))) {
27
55
  throw new SecretDetectedError();
28
56
  }
29
57
  }
58
+ function looksLikeSecret(input, pattern) {
59
+ if (pattern.test(input.title) || pattern.test(input.kind) || pattern.test(input.body)) {
60
+ return true;
61
+ }
62
+ return input.tags.some((t) => pattern.test(t));
63
+ }
64
+ /** A model-supplied limit is untrusted: SQLite reads a negative LIMIT as unbounded,
65
+ * so anything below one falls back to the default, and anything above the store
66
+ * ceiling is clamped. */
67
+ export function clampLimit(requested, fallback) {
68
+ if (requested === undefined || !Number.isFinite(requested) || requested < 1) {
69
+ return fallback;
70
+ }
71
+ return Math.min(Math.trunc(requested), MAX_LIMIT);
72
+ }
73
+ function previewBody(body) {
74
+ if (body.length <= BODY_PREVIEW_MAX) {
75
+ return body;
76
+ }
77
+ return `${body.slice(0, BODY_PREVIEW_MAX)}… [truncated]`;
78
+ }
79
+ /**
80
+ * Model-facing rendering of a result set. Compact JSON and truncated bodies keep a
81
+ * tool result inside a known size regardless of how much is stored.
82
+ */
83
+ export function renderEntries(entries) {
84
+ return JSON.stringify(entries.map((entry) => ({ ...entry, body: previewBody(entry.body) })));
85
+ }
86
+ export function sweep(db, options = {}) {
87
+ const now = options.now ?? Date.now();
88
+ db.conn.run("DELETE FROM entries WHERE expires_at IS NOT NULL AND expires_at <= ?", [now]);
89
+ return db.conn.get("SELECT changes() AS changes")?.changes ?? 0;
90
+ }
30
91
  export function save(db, input, options = {}) {
31
92
  validateEntry(input);
93
+ const scope = canonicalScope(input.scope);
32
94
  const now = options.now ?? Date.now();
95
+ // Expired rows are dead weight in search, so drop them with the save.
96
+ sweep(db, { now });
97
+ // A repeated title in the same scope is the same knowledge: rewrite it instead of
98
+ // stacking duplicates.
99
+ // ponytail: SQLite lower() folds ASCII only, so a non-ASCII title re-saved in a
100
+ // different case is not deduped; and the read-then-write is not transactional, so two
101
+ // concurrent saves of the same title can both insert. Ceiling is a rare duplicate, not
102
+ // data loss. Upgrade path: a normalized key column plus BEGIN IMMEDIATE.
103
+ const existing = db.conn.get("SELECT id, created_at FROM entries WHERE scope = ? AND lower(trim(title)) = ?", [scope, input.title.trim().toLowerCase()]);
104
+ if (existing) {
105
+ db.conn.run("UPDATE entries SET kind = ?, body = ?, tags = ?, updated_at = ?, expires_at = ? WHERE id = ?", [
106
+ input.kind,
107
+ input.body,
108
+ JSON.stringify(input.tags),
109
+ now,
110
+ input.expires_at ?? null,
111
+ existing.id,
112
+ ]);
113
+ return {
114
+ id: existing.id,
115
+ scope,
116
+ kind: input.kind,
117
+ title: input.title,
118
+ body: input.body,
119
+ tags: input.tags,
120
+ created_at: existing.created_at,
121
+ updated_at: now,
122
+ expires_at: input.expires_at,
123
+ };
124
+ }
33
125
  const id = randomUUID();
34
126
  db.conn.run("INSERT INTO entries (id, scope, kind, title, body, tags, created_at, updated_at, expires_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", [
35
127
  id,
36
- input.scope,
128
+ scope,
37
129
  input.kind,
38
130
  input.title,
39
131
  input.body,
@@ -44,7 +136,7 @@ export function save(db, input, options = {}) {
44
136
  ]);
45
137
  return {
46
138
  id,
47
- scope: input.scope,
139
+ scope,
48
140
  kind: input.kind,
49
141
  title: input.title,
50
142
  body: input.body,
@@ -55,34 +147,60 @@ export function save(db, input, options = {}) {
55
147
  };
56
148
  }
57
149
  export function search(db, query) {
58
- const limit = query.limit ?? 10;
150
+ const scope = canonicalScope(query.scope);
151
+ const limit = clampLimit(query.limit, DEFAULT_LIMIT.search);
59
152
  const now = query.now ?? Date.now();
60
- return db.conn
61
- .all(`SELECT e.* FROM entries_fts JOIN entries e ON e.rowid = entries_fts.rowid
62
- WHERE entries_fts MATCH ? AND (e.scope = ? OR e.scope = 'global')
63
- AND (e.expires_at IS NULL OR e.expires_at > ?)
64
- ORDER BY bm25(entries_fts) ASC, e.updated_at DESC
65
- LIMIT ?`, [escapeFtsQuery(query.query), query.scope, now, limit])
66
- .map(rowToEntry);
153
+ for (const match of searchCandidates(query.query)) {
154
+ const hits = db.conn
155
+ .all(`SELECT e.* FROM entries_fts JOIN entries e ON e.rowid = entries_fts.rowid
156
+ WHERE entries_fts MATCH ? AND (e.scope = ? OR e.scope = 'global')
157
+ AND (e.expires_at IS NULL OR e.expires_at > ?)
158
+ ORDER BY bm25(entries_fts) ASC, e.updated_at DESC
159
+ LIMIT ?`, [match, scope, now, limit])
160
+ .map(rowToEntry);
161
+ if (hits.length > 0) {
162
+ return hits;
163
+ }
164
+ }
165
+ return [];
67
166
  }
68
167
  export function list(db, query) {
69
- const limit = query.limit ?? 20;
168
+ const scope = canonicalScope(query.scope);
169
+ const limit = clampLimit(query.limit, DEFAULT_LIMIT.list);
70
170
  const now = query.now ?? Date.now();
71
171
  return db.conn
72
172
  .all(`SELECT * FROM entries
73
173
  WHERE (scope = ? OR scope = 'global') AND (expires_at IS NULL OR expires_at > ?)
74
174
  ORDER BY updated_at DESC, id ASC
75
- LIMIT ?`, [query.scope, now, limit])
175
+ LIMIT ?`, [scope, now, limit])
76
176
  .map(rowToEntry);
77
177
  }
178
+ /**
179
+ * Deletes inside one scope (plus the shared `global` scope), never across the whole
180
+ * machine: the store is shared, so an unscoped delete would silently hit other
181
+ * projects.
182
+ */
78
183
  export function forget(db, target) {
184
+ const scope = canonicalScope(target.scope);
79
185
  if ("id" in target) {
80
- db.conn.run("DELETE FROM entries WHERE id = ?", [target.id]);
81
- return db.conn.get("SELECT changes() AS changes")?.changes ?? 0;
186
+ db.conn.run("DELETE FROM entries WHERE id = ? AND (scope = ? OR scope = 'global')", [
187
+ target.id,
188
+ scope,
189
+ ]);
190
+ return changesOf(db);
191
+ }
192
+ // Deleting is precision-first: only the strict all-token match, never the fuzzy
193
+ // fallback. A wrong read costs the model a retry, a wrong delete is unrecoverable.
194
+ const match = strictMatch(target.query);
195
+ if (match === null) {
196
+ return 0;
82
197
  }
83
198
  db.conn.run(`DELETE FROM entries WHERE id IN (
84
199
  SELECT e.id FROM entries_fts JOIN entries e ON e.rowid = entries_fts.rowid WHERE entries_fts MATCH ?
85
- )`, [escapeFtsQuery(target.query)]);
200
+ ) AND (scope = ? OR scope = 'global')`, [match, scope]);
201
+ return changesOf(db);
202
+ }
203
+ function changesOf(db) {
86
204
  return db.conn.get("SELECT changes() AS changes")?.changes ?? 0;
87
205
  }
88
206
  function rowToEntry(row) {
@@ -107,13 +225,31 @@ function parseTags(raw) {
107
225
  return [];
108
226
  }
109
227
  }
110
- // FTS5 queries treat punctuation as operators; wrap each whitespace-separated
111
- // token in double quotes so arbitrary user/model input stays a literal search.
112
- function escapeFtsQuery(raw) {
228
+ /**
229
+ * FTS5 candidates, most precise first, each quoted so arbitrary model input stays a
230
+ * literal search. The strict AND match is tried before the looser prefix OR match:
231
+ * recall matters more than precision once the strict match found nothing.
232
+ */
233
+ function searchCandidates(raw) {
234
+ const strict = strictMatch(raw);
235
+ const loose = prefixMatch(raw);
236
+ return [strict, loose].filter((candidate) => candidate !== null);
237
+ }
238
+ /** Every token must appear. Used alone by destructive paths, where a false positive
239
+ * cannot be undone. */
240
+ function strictMatch(raw) {
241
+ const tokens = tokenize(raw);
242
+ return tokens.length === 0 ? null : tokens.map((token) => `"${token}"`).join(" ");
243
+ }
244
+ /** Any token may match, each as a prefix. Read-only recall only. */
245
+ function prefixMatch(raw) {
246
+ const tokens = tokenize(raw);
247
+ return tokens.length === 0 ? null : tokens.map((token) => `"${token}" *`).join(" OR ");
248
+ }
249
+ function tokenize(raw) {
113
250
  return raw
114
251
  .split(/\s+/)
115
252
  .filter(Boolean)
116
- .map((token) => `"${token.replaceAll('"', '""')}"`)
117
- .join(" ");
253
+ .map((token) => token.replaceAll('"', ""));
118
254
  }
119
255
  //# sourceMappingURL=store.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/lib/store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/lib/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Plugin } from "@opencode/plugin";
2
+ export declare const MemoryV2: Plugin.Plugin;
3
+ //# sourceMappingURL=plugin-v2.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-v2.d.ts","sourceRoot":"","sources":["../src/plugin-v2.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAmB1C,eAAO,MAAM,QAAQ,eA4InB,CAAC"}
@@ -0,0 +1,124 @@
1
+ import { Plugin } from "@opencode/plugin";
2
+ import { openMemoryDb } from "./lib/db.js";
3
+ import { TOOL_DESCRIPTIONS, createMemoryOps, memoryInventory, oncePerSession, } from "./lib/memory-ops.js";
4
+ import { SYSTEM_GUIDANCE, COMPACTION_CONTEXT, defaultScope, defaultDbPath, } from "./plugin.js";
5
+ const textPart = (text) => ({ type: "text", text });
6
+ export const MemoryV2 = Plugin.define({
7
+ id: "@log0u7/opencode-memory",
8
+ async setup(ctx) {
9
+ const options = ctx.options;
10
+ const dbPath = defaultDbPath(options);
11
+ let db = null;
12
+ const getDb = () => (db ??= openMemoryDb(dbPath));
13
+ const ops = createMemoryOps(getDb);
14
+ // Request and compaction hooks carry no directory: the instance scope is the project.
15
+ const projectScope = defaultScope(undefined, ctx.location.directory);
16
+ const injected = oncePerSession();
17
+ await ctx.tool.transform((editor) => {
18
+ editor.add({
19
+ name: "memory_save",
20
+ description: TOOL_DESCRIPTIONS.save,
21
+ input: {
22
+ type: "object",
23
+ properties: {
24
+ kind: {
25
+ type: "string",
26
+ description: "Entry type, e.g. decision, fact, preference, gotcha",
27
+ },
28
+ title: { type: "string", description: "Short title (max 120 chars)" },
29
+ body: { type: "string", description: "Entry content (max 8000 chars)" },
30
+ scope: { type: "string", description: "Scope key; defaults to the project directory" },
31
+ tags: { type: "array", items: { type: "string" }, description: "Optional tags" },
32
+ expires_at: { type: "number", description: "Optional epoch ms expiry" },
33
+ },
34
+ required: ["kind", "title", "body"],
35
+ additionalProperties: false,
36
+ },
37
+ async execute(args) {
38
+ const a = args;
39
+ return {
40
+ content: ops.save(a, defaultScope(undefined, a.scope ?? ctx.location.directory)),
41
+ };
42
+ },
43
+ });
44
+ editor.add({
45
+ name: "memory_search",
46
+ description: TOOL_DESCRIPTIONS.search,
47
+ input: {
48
+ type: "object",
49
+ properties: {
50
+ query: { type: "string", description: "Free-text query" },
51
+ scope: { type: "string", description: "Scope key; defaults to the project directory" },
52
+ limit: { type: "number", description: "Max results (default 10)" },
53
+ },
54
+ required: ["query"],
55
+ additionalProperties: false,
56
+ },
57
+ async execute(args) {
58
+ const a = args;
59
+ return {
60
+ content: ops.search({ query: a.query, ...(a.limit !== undefined ? { limit: a.limit } : {}) }, defaultScope(undefined, a.scope ?? ctx.location.directory)),
61
+ };
62
+ },
63
+ });
64
+ editor.add({
65
+ name: "memory_list",
66
+ description: TOOL_DESCRIPTIONS.list,
67
+ input: {
68
+ type: "object",
69
+ properties: {
70
+ scope: { type: "string", description: "Scope key; defaults to the project directory" },
71
+ limit: { type: "number", description: "Max results (default 20)" },
72
+ },
73
+ additionalProperties: false,
74
+ },
75
+ async execute(args) {
76
+ const a = args;
77
+ return {
78
+ content: ops.list(a.limit !== undefined ? { limit: a.limit } : {}, defaultScope(undefined, a.scope ?? ctx.location.directory)),
79
+ };
80
+ },
81
+ });
82
+ editor.add({
83
+ name: "memory_forget",
84
+ description: TOOL_DESCRIPTIONS.forget,
85
+ input: {
86
+ type: "object",
87
+ properties: {
88
+ id: { type: "string", description: "Entry id to delete" },
89
+ query: { type: "string", description: "Delete every entry matching this query" },
90
+ },
91
+ additionalProperties: false,
92
+ },
93
+ async execute(args) {
94
+ const a = args;
95
+ return { content: ops.forget(a, defaultScope(undefined, ctx.location.directory)) };
96
+ },
97
+ });
98
+ });
99
+ // The v1 system transform maps to the v2 context hook: guidance first, then the
100
+ // once-per-session inventory.
101
+ await ctx.session.hook("context", (event) => {
102
+ event.system.push(textPart(SYSTEM_GUIDANCE));
103
+ const inventory = injected.inject(event.sessionID, () => memoryInventory(getDb, dbPath, projectScope));
104
+ if (inventory !== null) {
105
+ event.system.push(textPart(inventory));
106
+ }
107
+ });
108
+ // experimental.session.compacting -> compaction hook
109
+ await ctx.session.hook("compaction", (event) => {
110
+ event.system.push(textPart(COMPACTION_CONTEXT));
111
+ const inventory = memoryInventory(getDb, dbPath, projectScope);
112
+ if (inventory !== null) {
113
+ event.system.push(textPart(inventory));
114
+ }
115
+ });
116
+ const cleanup = async () => {
117
+ injected.reset();
118
+ db?.close();
119
+ db = null;
120
+ };
121
+ return cleanup;
122
+ },
123
+ });
124
+ //# sourceMappingURL=plugin-v2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-v2.js","sourceRoot":"","sources":["../src/plugin-v2.ts"],"names":[],"mappings":"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"}
package/dist/plugin.d.ts CHANGED
@@ -1,3 +1,14 @@
1
1
  import type { Plugin } from "@opencode-ai/plugin";
2
+ export type PluginOptions = {
3
+ dbPath?: string;
4
+ };
5
+ export declare const SYSTEM_GUIDANCE = "You have persistent working memory shared with all OpenCode sessions on this machine.\nRecent entries are listed above when present; run memory_search for anything older or more specific.\nWhen you make a durable decision or learn a lasting fact, call memory_save (kinds: decision, fact, preference, gotcha).\nNever store credentials, tokens, or API keys in memory; the store refuses them.";
6
+ export declare const COMPACTION_CONTEXT: string;
7
+ export declare function defaultDbPath(options?: PluginOptions): string;
8
+ /**
9
+ * Scope of a session: the v1 tool context carries a worktree, so that is the canonical
10
+ * key. `directory` is the fallback so the same project does not get two scopes.
11
+ */
12
+ export declare function defaultScope(worktree?: string, directory?: string): string;
2
13
  export declare const MemoryPlugin: Plugin;
3
14
  //# sourceMappingURL=plugin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,MAAM,EAAE,MAAM,qBAAqB,CAAC;AA2BzD,eAAO,MAAM,YAAY,EAAE,MA6H1B,CAAC"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAYzD,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,eAAO,MAAM,eAAe,0YAGoD,CAAC;AAEjF,eAAO,MAAM,kBAAkB,QAEoE,CAAC;AAEpG,wBAAgB,aAAa,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,CAM7D;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAE1E;AAED,eAAO,MAAM,YAAY,EAAE,MAmG1B,CAAC"}
package/dist/plugin.js CHANGED
@@ -1,31 +1,39 @@
1
- import { openMemoryDb } from "./lib/db.js";
2
- import { forget, list, save, search, SecretDetectedError } from "./lib/store.js";
3
1
  import { tool } from "@opencode-ai/plugin";
4
- const SYSTEM_GUIDANCE = `You have persistent working memory shared with all OpenCode sessions on this machine.
5
- At the start of a non-trivial task, run memory_search for relevant decisions and facts about this project.
2
+ import { openMemoryDb } from "./lib/db.js";
3
+ import { TOOL_DESCRIPTIONS, createMemoryOps, memoryInventory, oncePerSession, } from "./lib/memory-ops.js";
4
+ import { canonicalScope } from "./lib/store.js";
5
+ export const SYSTEM_GUIDANCE = `You have persistent working memory shared with all OpenCode sessions on this machine.
6
+ Recent entries are listed above when present; run memory_search for anything older or more specific.
6
7
  When you make a durable decision or learn a lasting fact, call memory_save (kinds: decision, fact, preference, gotcha).
7
8
  Never store credentials, tokens, or API keys in memory; the store refuses them.`;
8
- const COMPACTION_CONTEXT = "The session is being compacted. After compaction, call memory_save for every durable decision still relevant " +
9
+ export const COMPACTION_CONTEXT = "The session is being compacted. After compaction, call memory_save for every durable decision still relevant " +
9
10
  "(kind=decision) and any newly learned facts (kind=fact), so other sessions keep this knowledge.";
10
- function defaultDbPath(options) {
11
+ export function defaultDbPath(options) {
11
12
  if (typeof options?.dbPath === "string" && options.dbPath.length > 0) {
12
13
  return options.dbPath;
13
14
  }
14
15
  const dataHome = process.env.XDG_DATA_HOME ?? `${process.env.HOME ?? ""}/.local/share`;
15
16
  return `${dataHome}/opencode-memory/memory.db`;
16
17
  }
17
- export const MemoryPlugin = async (_input, options) => {
18
+ /**
19
+ * Scope of a session: the v1 tool context carries a worktree, so that is the canonical
20
+ * key. `directory` is the fallback so the same project does not get two scopes.
21
+ */
22
+ export function defaultScope(worktree, directory) {
23
+ return canonicalScope(worktree, directory);
24
+ }
25
+ export const MemoryPlugin = async (input, options) => {
26
+ const dbPath = defaultDbPath(options);
18
27
  let db = null;
19
- const getDb = () => {
20
- if (!db) {
21
- db = openMemoryDb(defaultDbPath(options));
22
- }
23
- return db;
24
- };
28
+ const getDb = () => (db ??= openMemoryDb(dbPath));
29
+ const ops = createMemoryOps(getDb);
30
+ // Request and compaction hooks carry no directory: the instance scope is the project.
31
+ const projectScope = defaultScope(input.worktree, input.directory);
32
+ const injected = oncePerSession();
25
33
  const hooks = {
26
34
  tool: {
27
35
  memory_save: tool({
28
- description: "Save a durable entry to shared working memory (decisions, facts, preferences, gotchas). Never store credentials.",
36
+ description: TOOL_DESCRIPTIONS.save,
29
37
  args: {
30
38
  kind: tool.schema
31
39
  .string()
@@ -39,20 +47,10 @@ export const MemoryPlugin = async (_input, options) => {
39
47
  tags: tool.schema.array(tool.schema.string()).optional().describe("Optional tags"),
40
48
  expires_at: tool.schema.number().optional().describe("Optional epoch ms expiry"),
41
49
  },
42
- execute: (args, context) => run(() => {
43
- const entry = save(getDb(), {
44
- scope: args.scope ?? context.worktree,
45
- kind: args.kind,
46
- title: args.title,
47
- body: args.body,
48
- tags: args.tags ?? [],
49
- expires_at: args.expires_at ?? null,
50
- });
51
- return `saved ${entry.id} (${entry.scope})`;
52
- }),
50
+ execute: async (args, context) => ops.save(args, defaultScope(args.scope ?? context.worktree ?? context.directory)),
53
51
  }),
54
52
  memory_search: tool({
55
- description: "Search shared working memory (all sessions on this machine). Results include this project's scope and global entries.",
53
+ description: TOOL_DESCRIPTIONS.search,
56
54
  args: {
57
55
  query: tool.schema.string().describe("Free-text query"),
58
56
  scope: tool.schema
@@ -61,20 +59,10 @@ export const MemoryPlugin = async (_input, options) => {
61
59
  .describe("Scope key; defaults to this session's worktree"),
62
60
  limit: tool.schema.number().optional().describe("Max results (default 10)"),
63
61
  },
64
- execute: (args, context) => run(() => {
65
- const hits = search(getDb(), {
66
- query: args.query,
67
- scope: args.scope ?? context.worktree,
68
- ...(args.limit !== undefined ? { limit: args.limit } : {}),
69
- });
70
- if (hits.length === 0) {
71
- return "no matching memory entries";
72
- }
73
- return JSON.stringify(hits, null, 2);
74
- }),
62
+ execute: async (args, context) => ops.search({ query: args.query, ...(args.limit !== undefined ? { limit: args.limit } : {}) }, defaultScope(args.scope ?? context.worktree ?? context.directory)),
75
63
  }),
76
64
  memory_list: tool({
77
- description: "List recent memory entries for a scope (default: this session's worktree), newest first.",
65
+ description: TOOL_DESCRIPTIONS.list,
78
66
  args: {
79
67
  scope: tool.schema
80
68
  .string()
@@ -82,65 +70,39 @@ export const MemoryPlugin = async (_input, options) => {
82
70
  .describe("Scope key; defaults to this session's worktree"),
83
71
  limit: tool.schema.number().optional().describe("Max results (default 20)"),
84
72
  },
85
- execute: (args, context) => run(() => {
86
- const entries = list(getDb(), {
87
- scope: args.scope ?? context.worktree,
88
- ...(args.limit !== undefined ? { limit: args.limit } : {}),
89
- });
90
- if (entries.length === 0) {
91
- return "no memory entries";
92
- }
93
- return JSON.stringify(entries, null, 2);
94
- }),
73
+ execute: async (args, context) => ops.list(args.limit !== undefined ? { limit: args.limit } : {}, defaultScope(args.scope ?? context.worktree ?? context.directory)),
95
74
  }),
96
75
  memory_forget: tool({
97
- description: "Delete memory entries by id, or all entries matching a query.",
76
+ description: TOOL_DESCRIPTIONS.forget,
98
77
  args: {
99
78
  id: tool.schema.string().optional().describe("Entry id to delete"),
100
- query: tool.schema.string().optional().describe("Delete every entry matching this query"),
79
+ query: tool.schema.string().optional().describe("Delete every entry matching a query"),
101
80
  },
102
- execute: (args, _context) => run(() => {
103
- if (args.id !== undefined) {
104
- const deleted = forget(getDb(), { id: args.id });
105
- return `deleted ${deleted} entr${deleted === 1 ? "y" : "ies"}`;
106
- }
107
- if (args.query !== undefined) {
108
- const deleted = forget(getDb(), { query: args.query });
109
- return `deleted ${deleted} entr${deleted === 1 ? "y" : "ies"}`;
110
- }
111
- return "provide id or query";
112
- }),
81
+ execute: async (args, context) => ops.forget(args, defaultScope(context.worktree, context.directory)),
113
82
  }),
114
83
  },
115
84
  };
116
85
  return {
117
86
  ...hooks,
118
- "experimental.chat.system.transform": async (_input, output) => {
87
+ "experimental.chat.system.transform": async (input, output) => {
119
88
  output.system.push(SYSTEM_GUIDANCE);
89
+ const inventory = injected.inject(input.sessionID, () => memoryInventory(getDb, dbPath, projectScope));
90
+ if (inventory !== null) {
91
+ output.system.push(inventory);
92
+ }
120
93
  },
121
94
  "experimental.session.compacting": async (_input, output) => {
122
95
  output.context.push(COMPACTION_CONTEXT);
96
+ const inventory = memoryInventory(getDb, dbPath, projectScope);
97
+ if (inventory !== null) {
98
+ output.context.push(inventory);
99
+ }
123
100
  },
124
101
  dispose: async () => {
102
+ injected.reset();
125
103
  db?.close();
126
104
  db = null;
127
105
  },
128
106
  };
129
107
  };
130
- // Tool results are shown to the model: caught errors become readable text, so
131
- // a rejected save is guidance instead of a crashed tool call.
132
- async function run(fn) {
133
- try {
134
- return fn();
135
- }
136
- catch (error) {
137
- if (error instanceof SecretDetectedError) {
138
- return "refused: the content looks like a credential; secrets must never be stored in memory";
139
- }
140
- if (error instanceof Error) {
141
- return `error: ${error.message}`;
142
- }
143
- throw error;
144
- }
145
- }
146
108
  //# sourceMappingURL=plugin.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@log0u7/opencode-memory",
3
- "version": "0.1.2",
3
+ "version": "0.3.0",
4
4
  "description": "Working memory for OpenCode: shared SQLite store with hybrid search, shared across all sessions on a machine.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -33,11 +33,12 @@
33
33
  "node": ">=22.0.0"
34
34
  },
35
35
  "devDependencies": {
36
- "@biomejs/biome": "2.5.13",
37
- "@opencode-ai/plugin": "1.18.30",
38
- "@types/node": "^22.0.0",
36
+ "@biomejs/biome": "2.5.14",
37
+ "@opencode-ai/plugin": "1.18.31",
38
+ "@opencode/plugin": "2.0.26",
39
+ "@types/node": "^26.6.2",
39
40
  "@vitest/coverage-v8": "^5.0.1",
40
- "lefthook": "2.1.12",
41
+ "lefthook": "2.1.14",
41
42
  "typescript": "7.0.2",
42
43
  "vitest": "^5.0.0"
43
44
  },