@log0u7/opencode-memory 0.3.0 → 0.4.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 +53 -0
- package/README.md +24 -4
- package/dist/lib/db.d.ts.map +1 -1
- package/dist/lib/db.js +24 -2
- package/dist/lib/db.js.map +1 -1
- package/dist/lib/memory-ops.d.ts +6 -3
- package/dist/lib/memory-ops.d.ts.map +1 -1
- package/dist/lib/memory-ops.js +21 -8
- package/dist/lib/memory-ops.js.map +1 -1
- package/dist/lib/reflection.d.ts +27 -0
- package/dist/lib/reflection.d.ts.map +1 -0
- package/dist/lib/reflection.js +130 -0
- package/dist/lib/reflection.js.map +1 -0
- package/dist/lib/store.d.ts +12 -3
- package/dist/lib/store.d.ts.map +1 -1
- package/dist/lib/store.js +28 -13
- package/dist/lib/store.js.map +1 -1
- package/dist/plugin-v2.d.ts.map +1 -1
- package/dist/plugin-v2.js +113 -3
- package/dist/plugin-v2.js.map +1 -1
- package/dist/plugin.d.ts +3 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +111 -3
- package/dist/plugin.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,59 @@ 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
|
+
## [0.4.0] - 2026-10-14
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Lesson feedback loop: the system guidance and the compaction prompt now nudge the agent
|
|
13
|
+
to save reusable lessons (`kind=lesson`), and lessons are injected first in later
|
|
14
|
+
sessions' inventory, ahead of recency noise (`store.list` gained an optional `kind`
|
|
15
|
+
filter).
|
|
16
|
+
- Optional LLM reflection at `session.idle`, off by default: with
|
|
17
|
+
`reflection.enabled: true` in the plugin options, the plugin distills at most
|
|
18
|
+
`maxLessons` durable lessons from the bounded session tail with one LLM call per
|
|
19
|
+
session and saves them as lessons. Options: `model` (`"provider/model"`), `maxLessons`
|
|
20
|
+
(3), `transcriptChars` (8000), `timeoutMs` (60000). Subagent sessions are skipped and the
|
|
21
|
+
run is best-effort: failures are silent.
|
|
22
|
+
|
|
23
|
+
- New `memory_stats` tool (both hosts): counts entries by kind for the project scope
|
|
24
|
+
(plus global) with a total, so the model can see how much memory exists, including
|
|
25
|
+
how many lessons reflection has written, without paging through `memory_list`.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- `pnpm verify` (and therefore CI) now runs the suite with coverage, so the coverage gate
|
|
30
|
+
the docs promise (90% lines/functions/statements, 80% branches) is actually enforced
|
|
31
|
+
instead of only configured.
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- The store database is created with restrictive permissions: the file is `0600`
|
|
36
|
+
(enforced on every open, regardless of the process umask, so databases created earlier
|
|
37
|
+
with a permissive umask are tightened too) and the directory is `0700` when the plugin
|
|
38
|
+
creates it, as `SECURITY.md` already promised.
|
|
39
|
+
- Secret detection now also refuses JWTs, PEM private key blocks, AWS access key ids,
|
|
40
|
+
GitLab personal access tokens, Google API keys, and Slack user/app tokens, in addition
|
|
41
|
+
to the existing OpenAI, GitHub, and Slack bot formats.
|
|
42
|
+
- `memory_forget` by id now deletes across scopes: ids are globally unique, so an exact
|
|
43
|
+
id match is precise anywhere, and closed worktrees stay cleanable from any project.
|
|
44
|
+
Query deletes keep the scope lock (a fuzzy match must never silently hit other
|
|
45
|
+
projects).
|
|
46
|
+
- Reflection no longer permanently skips a session whose first `session.idle` arrived
|
|
47
|
+
before the transcript reached the distillation floor: the session is marked reflected
|
|
48
|
+
only once a run is actually attempted, so a later, longer turn still gets exactly one
|
|
49
|
+
reflection.
|
|
50
|
+
- The README safety-limits note on `memory_forget` now matches the code: query deletes
|
|
51
|
+
stay in scope, id deletes are global.
|
|
52
|
+
- Reflection's LLM call is now bounded by a timeout (`reflection.timeoutMs`, default 60s):
|
|
53
|
+
a hung provider no longer wedges a session forever, and the abort reaches the SDK so the
|
|
54
|
+
v1 host still deletes its ephemeral child session.
|
|
55
|
+
- The v2 event stream now reconnects with a capped backoff after an error instead of
|
|
56
|
+
silently ending the reflection loop for good.
|
|
57
|
+
- After `dispose` (v1) or `cleanup` (v2), memory operations no longer reopen the SQLite
|
|
58
|
+
connection: a late tool call or in-flight reflection returns a readable error instead of
|
|
59
|
+
leaking a connection that nobody closes again.
|
|
60
|
+
|
|
8
61
|
## [0.3.0] - 2026-10-14
|
|
9
62
|
|
|
10
63
|
### Added
|
package/README.md
CHANGED
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
|
|
9
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.
|
|
10
10
|
|
|
11
|
-
Every session gets
|
|
11
|
+
Every session gets five tools (`memory_save`, `memory_search`, `memory_list`, `memory_forget`, `memory_stats`). 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
12
|
|
|
13
13
|
## Safety limits
|
|
14
14
|
|
|
15
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
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).
|
|
17
|
+
- `memory_forget` by query only deletes inside the caller's scope (plus the shared `global` scope); by id it deletes the entry wherever it lives, since ids are globally unique.
|
|
18
18
|
- Entries that look like secrets (`sk-…`, `ghp_…`, `xoxb-…`) are refused, in the title, kind, body and tags alike.
|
|
19
19
|
|
|
20
20
|
## Install
|
|
@@ -48,6 +48,7 @@ memory_save(kind="decision", title="Use WAL mode", body="...", tags=["db"])
|
|
|
48
48
|
memory_search("postgres migration strategy")
|
|
49
49
|
memory_list()
|
|
50
50
|
memory_forget(id="...")
|
|
51
|
+
memory_stats()
|
|
51
52
|
```
|
|
52
53
|
|
|
53
54
|
### Tools
|
|
@@ -58,15 +59,34 @@ memory_forget(id="...")
|
|
|
58
59
|
| `memory_search` | `query`, `scope?`, `limit?` | FTS5 BM25 search; strict match first, prefix OR fallback when nothing matches. |
|
|
59
60
|
| `memory_list` | `scope?`, `limit?` | Recent entries, newest first. |
|
|
60
61
|
| `memory_forget` | `id` or `query` | Delete one entry by id, or all matching a query. |
|
|
62
|
+
| `memory_stats` | `scope?` | Count entries by kind (plus global) with a total; checks how much memory exists. |
|
|
61
63
|
|
|
62
|
-
Entry `kind` is free-form; conventions that work well: `decision`, `fact`, `preference`, `gotcha`, `compaction`.
|
|
64
|
+
Entry `kind` is free-form; conventions that work well: `decision`, `fact`, `preference`, `gotcha`, `lesson`, `compaction`.
|
|
65
|
+
|
|
66
|
+
### Feedback loop
|
|
67
|
+
|
|
68
|
+
The agent is nudged to write memory itself: the system prompt asks for durable decisions, facts, preferences and gotchas as they emerge, and the compaction prompt repeats the instruction next to the store inventory. Reusable lessons (`kind=lesson`: a non-obvious approach that clearly failed or succeeded, and why) are injected first in later sessions, so known mistakes are not retried.
|
|
69
|
+
|
|
70
|
+
Optional LLM reflection, off by default: on `session.idle` the plugin sends the last `transcriptChars` of the session to one bounded LLM call and saves at most `maxLessons` distilled lessons through the same store (the secret guard applies). Subagent sessions are skipped and at most one reflection runs per session.
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"plugin": [
|
|
75
|
+
["@log0u7/opencode-memory", { "reflection": { "enabled": true } }]
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Options: `enabled` (default `false`), `model` (`"provider/model"`, default: the host's model), `maxLessons` (default 3, cap 10), `transcriptChars` (default 8000).
|
|
81
|
+
|
|
82
|
+
Cost per reflection: ~2k input tokens (the bounded transcript) and at most ~300 output tokens, at most once per session: roughly $0.001 with a small model, $0.01 with a frontier model (indicative). The nudges and injections are plain text: they cost nothing.
|
|
63
83
|
|
|
64
84
|
### Notes
|
|
65
85
|
|
|
66
86
|
- All sessions on the machine share one store (WAL mode, concurrent-safe).
|
|
67
87
|
- Entries that look like secrets (`sk-…`, `ghp_…`, `xoxb-…`) are refused, in the title, kind, body and tags.
|
|
68
88
|
- 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.
|
|
89
|
+
- `memory_forget` by query never leaves the scope it is called from, so a project cannot fuzzily delete another project's entries; by id it deletes anywhere (ids are globally unique), which keeps closed worktrees cleanable.
|
|
70
90
|
- 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
91
|
- 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
92
|
- Injections stay bounded (~1200 characters worst case) and nothing is injected, nor any store file created, when memory is unused.
|
package/dist/lib/db.d.ts.map
CHANGED
|
@@ -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;
|
|
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;AAsCF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CA2BnD"}
|
package/dist/lib/db.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdirSync } from "node:fs";
|
|
1
|
+
import { chmodSync, mkdirSync } from "node:fs";
|
|
2
2
|
import { dirname } from "node:path";
|
|
3
3
|
import { openSqlite } from "./sqlite.js";
|
|
4
4
|
const SCHEMA = `
|
|
@@ -29,14 +29,36 @@ CREATE TRIGGER IF NOT EXISTS entries_au AFTER UPDATE ON entries BEGIN
|
|
|
29
29
|
END;
|
|
30
30
|
CREATE INDEX IF NOT EXISTS entries_scope_title ON entries(scope, lower(trim(title)));
|
|
31
31
|
`;
|
|
32
|
+
// SECURITY.md promises restrictive permissions whatever the user's umask is, so the
|
|
33
|
+
// directory and files are mode-enforced explicitly instead of left to the umask.
|
|
34
|
+
// POSIX only: chmodSync on Windows flips a read-only bit, which would corrupt usage.
|
|
35
|
+
// Directories the plugin did not create are left to their owner: the db file is
|
|
36
|
+
// 0600 anyway, so a loose parent only leaks file names.
|
|
37
|
+
const POSIX = process.platform !== "win32";
|
|
32
38
|
export function openMemoryDb(path) {
|
|
33
39
|
if (path !== ":memory:") {
|
|
34
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
40
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
35
41
|
}
|
|
36
42
|
const conn = openSqlite(path);
|
|
37
43
|
conn.run("PRAGMA journal_mode = WAL");
|
|
38
44
|
conn.run("PRAGMA busy_timeout = 5000");
|
|
39
45
|
conn.exec(SCHEMA);
|
|
46
|
+
if (POSIX && path !== ":memory:") {
|
|
47
|
+
// Also fixes databases created earlier with a permissive umask.
|
|
48
|
+
chmodSync(path, 0o600);
|
|
49
|
+
for (const suffix of ["-wal", "-shm"]) {
|
|
50
|
+
try {
|
|
51
|
+
chmodSync(`${path}${suffix}`, 0o600);
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
// Only tolerate a sidecar that is not written yet (or already
|
|
55
|
+
// checkpointed away); anything else is a real failure.
|
|
56
|
+
if (error.code !== "ENOENT") {
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
40
62
|
return { conn, close: () => conn.close() };
|
|
41
63
|
}
|
|
42
64
|
//# sourceMappingURL=db.js.map
|
package/dist/lib/db.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/lib/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/lib/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,UAAU,EAAmB,MAAM,aAAa,CAAC;AAO1D,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2Bd,CAAC;AAEF,oFAAoF;AACpF,iFAAiF;AACjF,qFAAqF;AACrF,gFAAgF;AAChF,wDAAwD;AACxD,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC;AAE3C,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,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7D,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,IAAI,KAAK,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;QACjC,gEAAgE;QAChE,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACvB,KAAK,MAAM,MAAM,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;YACtC,IAAI,CAAC;gBACH,SAAS,CAAC,GAAG,IAAI,GAAG,MAAM,EAAE,EAAE,KAAK,CAAC,CAAC;YACvC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,8DAA8D;gBAC9D,uDAAuD;gBACvD,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;oBACvD,MAAM,KAAK,CAAC;gBACd,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;AAC7C,CAAC"}
|
package/dist/lib/memory-ops.d.ts
CHANGED
|
@@ -24,16 +24,18 @@ export type MemoryOps = {
|
|
|
24
24
|
search: (args: SearchArgs, scope: string) => string;
|
|
25
25
|
list: (args: ListArgs, scope: string) => string;
|
|
26
26
|
forget: (args: ForgetArgs, scope: string) => string;
|
|
27
|
+
stats: (scope: string) => string;
|
|
27
28
|
};
|
|
28
29
|
export declare function createMemoryOps(getDb: () => MemoryDb): MemoryOps;
|
|
29
30
|
/** How much of the store is summarised into a prompt, worst case. */
|
|
30
31
|
export declare const INVENTORY_MAX_CHARS = 1200;
|
|
31
32
|
/** Tool descriptions shared by both entry points so the two hosts cannot drift. */
|
|
32
33
|
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 save: "Save a durable entry to shared working memory (decisions, facts, preferences, gotchas, lessons). Never store credentials.";
|
|
34
35
|
readonly search: "Search shared working memory (all sessions on this machine). Results include this project's scope and global entries.";
|
|
35
36
|
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.
|
|
37
|
+
readonly forget: "Delete memory entries by id (any scope: ids are globally unique), or all entries matching a query. Query deletes stay inside this project's scope (plus global entries); an entry saved under another scope needs its id, or that scope, to be removed.";
|
|
38
|
+
readonly stats: "Count memory entries by kind for this project (plus global entries), with a total. Use it to see how much memory already exists, for example how many lessons reflection has written.";
|
|
37
39
|
};
|
|
38
40
|
/**
|
|
39
41
|
* Produces at most one injection per session key, and only on the first request where
|
|
@@ -48,7 +50,8 @@ export declare function oncePerSession(): {
|
|
|
48
50
|
};
|
|
49
51
|
/**
|
|
50
52
|
* Bounded summary of recent entries, injected into the first request of a session and
|
|
51
|
-
* into the compaction context.
|
|
53
|
+
* into the compaction context. Lessons are promoted above recency noise so a stored
|
|
54
|
+
* lesson survives a chatty store. `null` when there is nothing worth injecting, so a
|
|
52
55
|
* caller pushes nothing at all.
|
|
53
56
|
*/
|
|
54
57
|
export declare function memoryInventory(getDb: () => MemoryDb, dbPath: string, scope: string): string | null;
|
|
@@ -1 +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;
|
|
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;IACpD,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;CAClC,CAAC;AAEF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,QAAQ,GAAG,SAAS,CAqDhE;AAED,qEAAqE;AACrE,eAAO,MAAM,mBAAmB,OAAO,CAAC;AAIxC,mFAAmF;AACnF,eAAO,MAAM,iBAAiB;aAC5B,IAAI,EAAE,2HAA2H;aACjI,MAAM,EACJ,uHAAuH;aACzH,IAAI,EAAE,+EAA+E;aACrF,MAAM,EACJ,yPAAyP;aAC3P,KAAK,EACH,uLAAuL;CACjL,CAAC;AAEX;;;;;;GAMG;AACH,wBAAgB,cAAc;kBAGP,MAAM,GAAG,SAAS,WAAW,MAAM,MAAM,GAAG,IAAI,KAAG,MAAM,GAAG,IAAI;;EAgBtF;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,QAAQ,EACrB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ,MAAM,GAAG,IAAI,CAyBf"}
|
package/dist/lib/memory-ops.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import { forget, list, renderEntries, save, search, SecretDetectedError } from "./store.js";
|
|
2
|
+
import { forget, list, renderEntries, save, search, SecretDetectedError, stats } from "./store.js";
|
|
3
3
|
export function createMemoryOps(getDb) {
|
|
4
4
|
return {
|
|
5
5
|
save: (args, scope) => run(() => {
|
|
@@ -39,17 +39,26 @@ export function createMemoryOps(getDb) {
|
|
|
39
39
|
}
|
|
40
40
|
return "provide id or query";
|
|
41
41
|
}),
|
|
42
|
+
stats: (scope) => run(() => {
|
|
43
|
+
const counts = stats(getDb(), { scope });
|
|
44
|
+
if (counts.length === 0) {
|
|
45
|
+
return "no memory entries";
|
|
46
|
+
}
|
|
47
|
+
const total = counts.reduce((sum, row) => sum + row.count, 0);
|
|
48
|
+
return `${counts.map((row) => `${row.kind}: ${row.count}`).join(", ")}, total: ${total}`;
|
|
49
|
+
}),
|
|
42
50
|
};
|
|
43
51
|
}
|
|
44
52
|
/** How much of the store is summarised into a prompt, worst case. */
|
|
45
53
|
export const INVENTORY_MAX_CHARS = 1200;
|
|
46
|
-
const INVENTORY_HEADER = "Memory already stored for this project (
|
|
54
|
+
const INVENTORY_HEADER = "Memory already stored for this project (lessons first, then newest):";
|
|
47
55
|
/** Tool descriptions shared by both entry points so the two hosts cannot drift. */
|
|
48
56
|
export const TOOL_DESCRIPTIONS = {
|
|
49
|
-
save: "Save a durable entry to shared working memory (decisions, facts, preferences, gotchas). Never store credentials.",
|
|
57
|
+
save: "Save a durable entry to shared working memory (decisions, facts, preferences, gotchas, lessons). Never store credentials.",
|
|
50
58
|
search: "Search shared working memory (all sessions on this machine). Results include this project's scope and global entries.",
|
|
51
59
|
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.
|
|
60
|
+
forget: "Delete memory entries by id (any scope: ids are globally unique), or all entries matching a query. Query deletes stay inside this project's scope (plus global entries); an entry saved under another scope needs its id, or that scope, to be removed.",
|
|
61
|
+
stats: "Count memory entries by kind for this project (plus global entries), with a total. Use it to see how much memory already exists, for example how many lessons reflection has written.",
|
|
53
62
|
};
|
|
54
63
|
/**
|
|
55
64
|
* Produces at most one injection per session key, and only on the first request where
|
|
@@ -80,20 +89,24 @@ export function oncePerSession() {
|
|
|
80
89
|
}
|
|
81
90
|
/**
|
|
82
91
|
* Bounded summary of recent entries, injected into the first request of a session and
|
|
83
|
-
* into the compaction context.
|
|
92
|
+
* into the compaction context. Lessons are promoted above recency noise so a stored
|
|
93
|
+
* lesson survives a chatty store. `null` when there is nothing worth injecting, so a
|
|
84
94
|
* caller pushes nothing at all.
|
|
85
95
|
*/
|
|
86
96
|
export function memoryInventory(getDb, dbPath, scope) {
|
|
87
97
|
if (!existsSync(dbPath)) {
|
|
88
98
|
return null;
|
|
89
99
|
}
|
|
90
|
-
const
|
|
91
|
-
|
|
100
|
+
const lessons = list(getDb(), { scope, limit: 50, kind: "lesson" });
|
|
101
|
+
const others = list(getDb(), { scope, limit: 50 });
|
|
102
|
+
if (lessons.length === 0 && others.length === 0) {
|
|
92
103
|
return null;
|
|
93
104
|
}
|
|
105
|
+
const seen = new Set(lessons.map((entry) => entry.id));
|
|
106
|
+
const ordered = [...lessons, ...others.filter((entry) => !seen.has(entry.id))];
|
|
94
107
|
const lines = [INVENTORY_HEADER];
|
|
95
108
|
let size = INVENTORY_HEADER.length;
|
|
96
|
-
for (const entry of
|
|
109
|
+
for (const entry of ordered) {
|
|
97
110
|
const line = `- [${entry.kind}] ${entry.title} (updated ${dayOf(entry.updated_at)})`;
|
|
98
111
|
if (size + line.length + 1 > INVENTORY_MAX_CHARS) {
|
|
99
112
|
break;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-ops.js","sourceRoot":"","sources":["../../src/lib/memory-ops.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAGrC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"memory-ops.js","sourceRoot":"","sources":["../../src/lib/memory-ops.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional end-of-turn reflection: distills durable lessons from a session tail with
|
|
3
|
+
* one bounded LLM call and saves them as `lesson` entries. Best-effort by design:
|
|
4
|
+
* a disabled config wires nothing, a failing run is swallowed, and each session is
|
|
5
|
+
* reflected at most once.
|
|
6
|
+
*/
|
|
7
|
+
export type ReflectionConfig = {
|
|
8
|
+
enabled: boolean;
|
|
9
|
+
model?: string | undefined;
|
|
10
|
+
maxLessons: number;
|
|
11
|
+
transcriptChars: number;
|
|
12
|
+
timeoutMs: number;
|
|
13
|
+
};
|
|
14
|
+
export type ReflectionDeps = {
|
|
15
|
+
isSubagent: (sessionID: string) => Promise<boolean>;
|
|
16
|
+
transcript: (sessionID: string) => Promise<string>;
|
|
17
|
+
generate: (prompt: string, sessionID: string, signal: AbortSignal) => Promise<string>;
|
|
18
|
+
saveLesson: (title: string, body: string) => void;
|
|
19
|
+
};
|
|
20
|
+
export declare function parseReflectionConfig(raw: unknown): ReflectionConfig | null;
|
|
21
|
+
export declare function distillPrompt(transcript: string, maxLessons: number): string;
|
|
22
|
+
export declare function parseLessons(text: string, max: number): Array<{
|
|
23
|
+
title: string;
|
|
24
|
+
body: string;
|
|
25
|
+
}>;
|
|
26
|
+
export declare function createReflection(config: ReflectionConfig, deps: ReflectionDeps): (sessionID: string | undefined) => void;
|
|
27
|
+
//# sourceMappingURL=reflection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reflection.d.ts","sourceRoot":"","sources":["../../src/lib/reflection.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACpD,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACnD,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACtF,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACnD,CAAC;AAcF,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,gBAAgB,GAAG,IAAI,CAyB3E;AASD,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAU5E;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CA8B9F;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,cAAc,GACnB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAoDzC"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional end-of-turn reflection: distills durable lessons from a session tail with
|
|
3
|
+
* one bounded LLM call and saves them as `lesson` entries. Best-effort by design:
|
|
4
|
+
* a disabled config wires nothing, a failing run is swallowed, and each session is
|
|
5
|
+
* reflected at most once.
|
|
6
|
+
*/
|
|
7
|
+
const MAX_LESSONS_DEFAULT = 3;
|
|
8
|
+
const MAX_LESSONS_CAP = 10;
|
|
9
|
+
const TRANSCRIPT_CHARS_DEFAULT = 8000;
|
|
10
|
+
const TRANSCRIPT_CHARS_MIN = 500;
|
|
11
|
+
const TRANSCRIPT_CHARS_MAX = 50000;
|
|
12
|
+
const TIMEOUT_MS_DEFAULT = 60000;
|
|
13
|
+
const TIMEOUT_MS_MIN = 1000;
|
|
14
|
+
const TIMEOUT_MS_MAX = 600000;
|
|
15
|
+
/** Below this much transcript there is nothing to distill; saves the LLM call. */
|
|
16
|
+
const TRANSCRIPT_MIN_CHARS = 256;
|
|
17
|
+
const MAX_TRACKED = 512;
|
|
18
|
+
export function parseReflectionConfig(raw) {
|
|
19
|
+
if (raw === null || typeof raw !== "object" || raw.enabled !== true) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
const rawConfig = raw;
|
|
23
|
+
return {
|
|
24
|
+
enabled: true,
|
|
25
|
+
model: typeof rawConfig.model === "string" && rawConfig.model.includes("/")
|
|
26
|
+
? rawConfig.model
|
|
27
|
+
: undefined,
|
|
28
|
+
maxLessons: clamp(rawConfig.maxLessons, 1, MAX_LESSONS_CAP, MAX_LESSONS_DEFAULT),
|
|
29
|
+
transcriptChars: clamp(rawConfig.transcriptChars, TRANSCRIPT_CHARS_MIN, TRANSCRIPT_CHARS_MAX, TRANSCRIPT_CHARS_DEFAULT),
|
|
30
|
+
timeoutMs: clamp(rawConfig.timeoutMs, TIMEOUT_MS_MIN, TIMEOUT_MS_MAX, TIMEOUT_MS_DEFAULT),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function clamp(value, min, max, fallback) {
|
|
34
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
35
|
+
return fallback;
|
|
36
|
+
}
|
|
37
|
+
return Math.min(max, Math.max(min, Math.round(value)));
|
|
38
|
+
}
|
|
39
|
+
export function distillPrompt(transcript, maxLessons) {
|
|
40
|
+
return (`You are the persistent-memory reflection for this project's OpenCode sessions. Below is the tail of one ` +
|
|
41
|
+
`coding session transcript. Extract at most ${maxLessons} durable, reusable lessons: non-obvious approaches ` +
|
|
42
|
+
`that clearly failed and why, non-obvious approaches that clearly worked and why, or constraints discovered ` +
|
|
43
|
+
`the hard way. Exclude anything trivially re-derivable from the code, anything session-specific, and any ` +
|
|
44
|
+
`credential. Reply with ONLY a JSON array, no markdown fences and no prose: ` +
|
|
45
|
+
`[{"title": "max 120 chars", "body": "the lesson, max 8000 chars"}]. Reply [] when nothing qualifies.\n\n` +
|
|
46
|
+
`SESSION TRANSCRIPT:\n${transcript}`);
|
|
47
|
+
}
|
|
48
|
+
export function parseLessons(text, max) {
|
|
49
|
+
let parsed;
|
|
50
|
+
try {
|
|
51
|
+
parsed = JSON.parse(text);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
const fenced = text
|
|
55
|
+
.replace(/^```(?:json)?/m, "")
|
|
56
|
+
.replace(/```$/m, "")
|
|
57
|
+
.trim();
|
|
58
|
+
try {
|
|
59
|
+
parsed = JSON.parse(fenced);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (!Array.isArray(parsed)) {
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
const lessons = [];
|
|
69
|
+
for (const item of parsed.slice(0, max)) {
|
|
70
|
+
if (item === null || typeof item !== "object") {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const { title, body } = item;
|
|
74
|
+
if (typeof title !== "string" || typeof body !== "string" || !title.trim() || !body.trim()) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
lessons.push({ title: title.slice(0, 120), body: body.slice(0, 8000) });
|
|
78
|
+
}
|
|
79
|
+
return lessons;
|
|
80
|
+
}
|
|
81
|
+
export function createReflection(config, deps) {
|
|
82
|
+
const done = new Set();
|
|
83
|
+
const inFlight = new Set();
|
|
84
|
+
const run = async (sessionID) => {
|
|
85
|
+
// Only a session we actually tried to reflect is marked done. A too-short
|
|
86
|
+
// transcript leaves it unsettled, so a later, longer idle can still reflect it
|
|
87
|
+
// once: idle fires at the end of every turn, not once per session.
|
|
88
|
+
let settled = false;
|
|
89
|
+
try {
|
|
90
|
+
if (await deps.isSubagent(sessionID)) {
|
|
91
|
+
settled = true;
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const tail = (await deps.transcript(sessionID)).slice(-config.transcriptChars);
|
|
95
|
+
if (tail.length < Math.min(TRANSCRIPT_MIN_CHARS, config.transcriptChars)) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
settled = true;
|
|
99
|
+
// Bound the LLM call: a hung provider must not wedge a session forever. The
|
|
100
|
+
// hosts forward this signal to the SDK, so the abort also unblocks their own
|
|
101
|
+
// cleanup (the v1 host deletes the ephemeral child session in its `finally`).
|
|
102
|
+
const reply = await deps.generate(distillPrompt(tail, config.maxLessons), sessionID, AbortSignal.timeout(config.timeoutMs));
|
|
103
|
+
for (const lesson of parseLessons(reply, config.maxLessons)) {
|
|
104
|
+
deps.saveLesson(lesson.title, lesson.body);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// Reflection is best-effort: never surface a failure to the host. A failed
|
|
109
|
+
// attempt still settles the session so a broken pipeline is not retried forever.
|
|
110
|
+
settled = true;
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
inFlight.delete(sessionID);
|
|
114
|
+
if (settled) {
|
|
115
|
+
done.add(sessionID);
|
|
116
|
+
if (done.size > MAX_TRACKED) {
|
|
117
|
+
done.clear();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
return (sessionID) => {
|
|
123
|
+
if (!sessionID || !config.enabled || done.has(sessionID) || inFlight.has(sessionID)) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
inFlight.add(sessionID);
|
|
127
|
+
void run(sessionID);
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=reflection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reflection.js","sourceRoot":"","sources":["../../src/lib/reflection.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/store.d.ts
CHANGED
|
@@ -46,6 +46,7 @@ export declare function list(db: MemoryDb, query: {
|
|
|
46
46
|
scope: string;
|
|
47
47
|
limit?: number;
|
|
48
48
|
now?: number;
|
|
49
|
+
kind?: string;
|
|
49
50
|
}): Entry[];
|
|
50
51
|
export type ForgetTarget = {
|
|
51
52
|
scope: string;
|
|
@@ -54,10 +55,18 @@ export type ForgetTarget = {
|
|
|
54
55
|
} | {
|
|
55
56
|
query: string;
|
|
56
57
|
});
|
|
58
|
+
/** Counts by kind for one scope plus `global`, expired entries excluded. */
|
|
59
|
+
export declare function stats(db: MemoryDb, query: {
|
|
60
|
+
scope: string;
|
|
61
|
+
now?: number;
|
|
62
|
+
}): Array<{
|
|
63
|
+
kind: string;
|
|
64
|
+
count: number;
|
|
65
|
+
}>;
|
|
57
66
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
67
|
+
* Query deletes stay inside one scope (plus the shared `global` scope): a fuzzy match
|
|
68
|
+
* must never silently hit other projects' entries. Id deletes are machine-wide because
|
|
69
|
+
* the id is globally unique: closed worktrees stay cleanable from any project.
|
|
61
70
|
*/
|
|
62
71
|
export declare function forget(db: MemoryDb, target: ForgetTarget): number;
|
|
63
72
|
//# sourceMappingURL=store.d.ts.map
|
package/dist/lib/store.d.ts.map
CHANGED
|
@@ -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;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;
|
|
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;AAgBlC,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,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GACpE,KAAK,EAAE,CAgBT;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,4EAA4E;AAC5E,wBAAgB,KAAK,CACnB,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACrC,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CASxC;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,GAAG,MAAM,CAqBjE"}
|
package/dist/lib/store.js
CHANGED
|
@@ -11,10 +11,18 @@ export const BODY_PREVIEW_MAX = 500;
|
|
|
11
11
|
export const DEFAULT_LIMIT = { search: 10, list: 20 };
|
|
12
12
|
/** Scope used when no worktree and no directory is known, so rows stay addressable. */
|
|
13
13
|
export const FALLBACK_SCOPE = "/";
|
|
14
|
+
// High-confidence credential formats only: a generic "password =" rule would block
|
|
15
|
+
// ordinary prose. Extend with care, one false positive costs a legitimate save.
|
|
14
16
|
const SECRET_PATTERNS = [
|
|
15
|
-
/\bsk-[A-Za-z0-9_-]{20,}\b/,
|
|
16
|
-
/\bghp_[A-Za-z0-9]{30,}\b/,
|
|
17
|
-
/\bxoxb-\d{10,}-\d{10,}\b/,
|
|
17
|
+
/\bsk-[A-Za-z0-9_-]{20,}\b/, // OpenAI-style
|
|
18
|
+
/\bghp_[A-Za-z0-9]{30,}\b/, // GitHub PAT
|
|
19
|
+
/\bxoxb-\d{10,}-\d{10,}\b/, // Slack bot token
|
|
20
|
+
/\bxox[ap]-[0-9A-Za-z-]{10,}/, // Slack user / app token
|
|
21
|
+
/\bAKIA[0-9A-Z]{16}\b/, // AWS access key id
|
|
22
|
+
/\bglpat-[0-9A-Za-z_-]{20,}\b/, // GitLab PAT
|
|
23
|
+
/\bAIza[0-9A-Za-z_-]{35}/, // Google API key
|
|
24
|
+
/\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/, // JWT
|
|
25
|
+
/-----BEGIN [A-Z ]*PRIVATE KEY-----/, // PEM private key block header
|
|
18
26
|
];
|
|
19
27
|
export class SecretDetectedError extends Error {
|
|
20
28
|
constructor() {
|
|
@@ -168,27 +176,34 @@ export function list(db, query) {
|
|
|
168
176
|
const scope = canonicalScope(query.scope);
|
|
169
177
|
const limit = clampLimit(query.limit, DEFAULT_LIMIT.list);
|
|
170
178
|
const now = query.now ?? Date.now();
|
|
179
|
+
const kindFilter = query.kind === undefined ? "" : "AND kind = ? ";
|
|
180
|
+
const kindArgs = query.kind === undefined ? [] : [query.kind];
|
|
171
181
|
return db.conn
|
|
172
182
|
.all(`SELECT * FROM entries
|
|
173
|
-
WHERE (scope = ? OR scope = 'global') AND (expires_at IS NULL OR expires_at > ?)
|
|
183
|
+
WHERE (scope = ? OR scope = 'global') AND (expires_at IS NULL OR expires_at > ?) ${kindFilter}
|
|
174
184
|
ORDER BY updated_at DESC, id ASC
|
|
175
|
-
LIMIT ?`, [scope, now, limit])
|
|
185
|
+
LIMIT ?`, [scope, now, ...kindArgs, limit])
|
|
176
186
|
.map(rowToEntry);
|
|
177
187
|
}
|
|
188
|
+
/** Counts by kind for one scope plus `global`, expired entries excluded. */
|
|
189
|
+
export function stats(db, query) {
|
|
190
|
+
const scope = canonicalScope(query.scope);
|
|
191
|
+
const now = query.now ?? Date.now();
|
|
192
|
+
return db.conn.all(`SELECT kind, COUNT(*) AS count FROM entries
|
|
193
|
+
WHERE (scope = ? OR scope = 'global') AND (expires_at IS NULL OR expires_at > ?)
|
|
194
|
+
GROUP BY kind ORDER BY count DESC, kind ASC`, [scope, now]);
|
|
195
|
+
}
|
|
178
196
|
/**
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
197
|
+
* Query deletes stay inside one scope (plus the shared `global` scope): a fuzzy match
|
|
198
|
+
* must never silently hit other projects' entries. Id deletes are machine-wide because
|
|
199
|
+
* the id is globally unique: closed worktrees stay cleanable from any project.
|
|
182
200
|
*/
|
|
183
201
|
export function forget(db, target) {
|
|
184
|
-
const scope = canonicalScope(target.scope);
|
|
185
202
|
if ("id" in target) {
|
|
186
|
-
db.conn.run("DELETE FROM entries WHERE id = ?
|
|
187
|
-
target.id,
|
|
188
|
-
scope,
|
|
189
|
-
]);
|
|
203
|
+
db.conn.run("DELETE FROM entries WHERE id = ?", [target.id]);
|
|
190
204
|
return changesOf(db);
|
|
191
205
|
}
|
|
206
|
+
const scope = canonicalScope(target.scope);
|
|
192
207
|
// Deleting is precision-first: only the strict all-token match, never the fuzzy
|
|
193
208
|
// fallback. A wrong read costs the model a retry, a wrong delete is unrecoverable.
|
|
194
209
|
const match = strictMatch(target.query);
|
package/dist/lib/store.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/lib/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAKzC,MAAM,SAAS,GAAG,GAAG,CAAC;AACtB,MAAM,QAAQ,GAAG,IAAI,CAAC;AACtB,MAAM,QAAQ,GAAG,EAAE,CAAC;AACpB,MAAM,CAAC,MAAM,OAAO,GAAG,EAAE,CAAC;AAC1B,MAAM,CAAC,MAAM,QAAQ,GAAG,EAAE,CAAC;AAC3B,sFAAsF;AACtF,MAAM,CAAC,MAAM,SAAS,GAAG,EAAE,CAAC;AAC5B,kFAAkF;AAClF,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAEpC,MAAM,CAAC,MAAM,aAAa,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;AAEtD,uFAAuF;AACvF,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,CAAC;AAElC,MAAM,eAAe,GAAG;IACtB,2BAA2B;
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/lib/store.ts"],"names":[],"mappings":"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"}
|
package/dist/plugin-v2.d.ts.map
CHANGED
|
@@ -1 +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;
|
|
1
|
+
{"version":3,"file":"plugin-v2.d.ts","sourceRoot":"","sources":["../src/plugin-v2.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AA6C1C,eAAO,MAAM,QAAQ,eA2NnB,CAAC"}
|
package/dist/plugin-v2.js
CHANGED
|
@@ -1,15 +1,49 @@
|
|
|
1
1
|
import { Plugin } from "@opencode/plugin";
|
|
2
2
|
import { openMemoryDb } from "./lib/db.js";
|
|
3
3
|
import { TOOL_DESCRIPTIONS, createMemoryOps, memoryInventory, oncePerSession, } from "./lib/memory-ops.js";
|
|
4
|
+
import { createReflection, parseReflectionConfig } from "./lib/reflection.js";
|
|
4
5
|
import { SYSTEM_GUIDANCE, COMPACTION_CONTEXT, defaultScope, defaultDbPath, } from "./plugin.js";
|
|
5
6
|
const textPart = (text) => ({ type: "text", text });
|
|
7
|
+
/** Reconnect backoff for the v2 event stream: short first retry, capped growth. */
|
|
8
|
+
const RECONNECT_MIN_MS = 250;
|
|
9
|
+
const RECONNECT_MAX_MS = 5000;
|
|
10
|
+
/** Session tail as plain text: user messages verbatim, assistant text parts joined. */
|
|
11
|
+
async function transcriptV2(ctx, sessionID) {
|
|
12
|
+
const messages = await ctx.session.context({ sessionID });
|
|
13
|
+
const lines = [];
|
|
14
|
+
for (const message of messages) {
|
|
15
|
+
if (message.type === "user") {
|
|
16
|
+
lines.push(`user: ${message.text}`);
|
|
17
|
+
}
|
|
18
|
+
else if (message.type === "assistant") {
|
|
19
|
+
const text = message.content
|
|
20
|
+
.filter((part) => part.type === "text")
|
|
21
|
+
.map((part) => part.text)
|
|
22
|
+
.join(" ")
|
|
23
|
+
.trim();
|
|
24
|
+
if (text) {
|
|
25
|
+
lines.push(`assistant: ${text}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return lines.join("\n");
|
|
30
|
+
}
|
|
6
31
|
export const MemoryV2 = Plugin.define({
|
|
7
32
|
id: "@log0u7/opencode-memory",
|
|
8
33
|
async setup(ctx) {
|
|
9
34
|
const options = ctx.options;
|
|
10
35
|
const dbPath = defaultDbPath(options);
|
|
11
36
|
let db = null;
|
|
12
|
-
|
|
37
|
+
let disposed = false;
|
|
38
|
+
// After cleanup the store is closed for good: a late path (tool call, in-flight
|
|
39
|
+
// reflection) must not silently reopen a connection that nobody closes again.
|
|
40
|
+
const getDb = () => {
|
|
41
|
+
if (disposed) {
|
|
42
|
+
throw new Error("memory store is closed");
|
|
43
|
+
}
|
|
44
|
+
db ??= openMemoryDb(dbPath);
|
|
45
|
+
return db;
|
|
46
|
+
};
|
|
13
47
|
const ops = createMemoryOps(getDb);
|
|
14
48
|
// Request and compaction hooks carry no directory: the instance scope is the project.
|
|
15
49
|
const projectScope = defaultScope(undefined, ctx.location.directory);
|
|
@@ -23,7 +57,7 @@ export const MemoryV2 = Plugin.define({
|
|
|
23
57
|
properties: {
|
|
24
58
|
kind: {
|
|
25
59
|
type: "string",
|
|
26
|
-
description: "Entry type, e.g. decision, fact, preference, gotcha",
|
|
60
|
+
description: "Entry type, e.g. decision, fact, preference, gotcha, lesson",
|
|
27
61
|
},
|
|
28
62
|
title: { type: "string", description: "Short title (max 120 chars)" },
|
|
29
63
|
body: { type: "string", description: "Entry content (max 8000 chars)" },
|
|
@@ -95,11 +129,29 @@ export const MemoryV2 = Plugin.define({
|
|
|
95
129
|
return { content: ops.forget(a, defaultScope(undefined, ctx.location.directory)) };
|
|
96
130
|
},
|
|
97
131
|
});
|
|
132
|
+
editor.add({
|
|
133
|
+
name: "memory_stats",
|
|
134
|
+
description: TOOL_DESCRIPTIONS.stats,
|
|
135
|
+
input: {
|
|
136
|
+
type: "object",
|
|
137
|
+
properties: {
|
|
138
|
+
scope: { type: "string", description: "Scope key; defaults to the project directory" },
|
|
139
|
+
},
|
|
140
|
+
additionalProperties: false,
|
|
141
|
+
},
|
|
142
|
+
async execute(args) {
|
|
143
|
+
const a = args;
|
|
144
|
+
return { content: ops.stats(defaultScope(undefined, a.scope ?? ctx.location.directory)) };
|
|
145
|
+
},
|
|
146
|
+
});
|
|
98
147
|
});
|
|
99
148
|
// The v1 system transform maps to the v2 context hook: guidance first, then the
|
|
100
149
|
// once-per-session inventory.
|
|
101
150
|
await ctx.session.hook("context", (event) => {
|
|
102
151
|
event.system.push(textPart(SYSTEM_GUIDANCE));
|
|
152
|
+
if (disposed) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
103
155
|
const inventory = injected.inject(event.sessionID, () => memoryInventory(getDb, dbPath, projectScope));
|
|
104
156
|
if (inventory !== null) {
|
|
105
157
|
event.system.push(textPart(inventory));
|
|
@@ -108,17 +160,75 @@ export const MemoryV2 = Plugin.define({
|
|
|
108
160
|
// experimental.session.compacting -> compaction hook
|
|
109
161
|
await ctx.session.hook("compaction", (event) => {
|
|
110
162
|
event.system.push(textPart(COMPACTION_CONTEXT));
|
|
163
|
+
if (disposed) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
111
166
|
const inventory = memoryInventory(getDb, dbPath, projectScope);
|
|
112
167
|
if (inventory !== null) {
|
|
113
168
|
event.system.push(textPart(inventory));
|
|
114
169
|
}
|
|
115
170
|
});
|
|
116
171
|
const cleanup = async () => {
|
|
172
|
+
disposed = true;
|
|
117
173
|
injected.reset();
|
|
118
174
|
db?.close();
|
|
119
175
|
db = null;
|
|
120
176
|
};
|
|
121
|
-
|
|
177
|
+
const reflection = parseReflectionConfig(options.reflection);
|
|
178
|
+
if (!reflection) {
|
|
179
|
+
return cleanup;
|
|
180
|
+
}
|
|
181
|
+
const onIdle = createReflection(reflection, {
|
|
182
|
+
isSubagent: async (sessionID) => {
|
|
183
|
+
const session = await ctx.session.get({ sessionID });
|
|
184
|
+
return !!session.parentID;
|
|
185
|
+
},
|
|
186
|
+
transcript: (sessionID) => transcriptV2(ctx, sessionID),
|
|
187
|
+
generate: async (prompt, _sessionID, signal) => {
|
|
188
|
+
const model = modelRefV2(reflection.model);
|
|
189
|
+
const result = await ctx.generate.text({ prompt, ...(model ? { model } : {}) }, { signal });
|
|
190
|
+
return result.text;
|
|
191
|
+
},
|
|
192
|
+
saveLesson: (title, body) => {
|
|
193
|
+
ops.save({ kind: "lesson", title, body }, projectScope);
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
// The host keeps the event stream alive through this controller; aborting it stops
|
|
197
|
+
// the idle loop without touching the tool or session hooks. A stream that errors is
|
|
198
|
+
// retried with a capped backoff instead of ending the loop for good.
|
|
199
|
+
const controller = new AbortController();
|
|
200
|
+
void (async () => {
|
|
201
|
+
let delay = RECONNECT_MIN_MS;
|
|
202
|
+
while (!controller.signal.aborted) {
|
|
203
|
+
let failed = false;
|
|
204
|
+
try {
|
|
205
|
+
for await (const event of ctx.event.subscribe({ signal: controller.signal })) {
|
|
206
|
+
if (event.type === "session.idle") {
|
|
207
|
+
onIdle(event.data.sessionID);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
failed = true;
|
|
213
|
+
}
|
|
214
|
+
if (!failed || controller.signal.aborted) {
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
218
|
+
delay = Math.min(delay * 2, RECONNECT_MAX_MS);
|
|
219
|
+
}
|
|
220
|
+
})();
|
|
221
|
+
return async () => {
|
|
222
|
+
controller.abort();
|
|
223
|
+
await cleanup();
|
|
224
|
+
};
|
|
122
225
|
},
|
|
123
226
|
});
|
|
227
|
+
function modelRefV2(model) {
|
|
228
|
+
if (!model) {
|
|
229
|
+
return undefined;
|
|
230
|
+
}
|
|
231
|
+
const [providerID, id] = model.split("/", 2);
|
|
232
|
+
return providerID && id ? { providerID, id } : undefined;
|
|
233
|
+
}
|
|
124
234
|
//# sourceMappingURL=plugin-v2.js.map
|
package/dist/plugin-v2.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-v2.js","sourceRoot":"","sources":["../src/plugin-v2.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,OAAO,EAAE,YAAY,EAAiB,MAAM,aAAa,CAAC;AAC1D,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,cAAc,GACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,YAAY,EACZ,aAAa,GAEd,MAAM,aAAa,CAAC;AAErB,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC,CAAC;AAErE,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC;IACpC,EAAE,EAAE,yBAAyB;IAC7B,KAAK,CAAC,KAAK,CAAC,GAAG;QACb,MAAM,OAAO,GAAG,GAAG,CAAC,OAAwB,CAAC;QAC7C,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;QACtC,IAAI,EAAE,GAAoB,IAAI,CAAC;QAC/B,MAAM,KAAK,GAAG,GAAa,EAAE,CAAC,CAAC,EAAE,KAAK,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;
|
|
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,8 +1,10 @@
|
|
|
1
1
|
import type { Plugin } from "@opencode-ai/plugin";
|
|
2
2
|
export type PluginOptions = {
|
|
3
3
|
dbPath?: string;
|
|
4
|
+
reflection?: unknown;
|
|
4
5
|
};
|
|
5
|
-
export declare const
|
|
6
|
+
export declare const REFLECTION_SESSION_TITLE = "[memory-reflect] lessons";
|
|
7
|
+
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).\nWhen a non-obvious approach clearly fails or succeeds, save the lesson (kind=lesson) as soon as it is confirmed, and check stored lessons before retrying a failed approach.\nNever store credentials, tokens, or API keys in memory; the store refuses them.";
|
|
6
8
|
export declare const COMPACTION_CONTEXT: string;
|
|
7
9
|
export declare function defaultDbPath(options?: PluginOptions): string;
|
|
8
10
|
/**
|
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,MAAM,
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,MAAM,EAAe,MAAM,qBAAqB,CAAC;AAiBtE,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,eAAO,MAAM,wBAAwB,6BAA6B,CAAC;AAqBnE,eAAO,MAAM,eAAe,wjBAIoD,CAAC;AAEjF,eAAO,MAAM,kBAAkB,QAG0B,CAAC;AAE1D,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;AA8DD,eAAO,MAAM,YAAY,EAAE,MA4I1B,CAAC"}
|
package/dist/plugin.js
CHANGED
|
@@ -1,13 +1,32 @@
|
|
|
1
1
|
import { tool } from "@opencode-ai/plugin";
|
|
2
2
|
import { openMemoryDb } from "./lib/db.js";
|
|
3
3
|
import { TOOL_DESCRIPTIONS, createMemoryOps, memoryInventory, oncePerSession, } from "./lib/memory-ops.js";
|
|
4
|
+
import { parseReflectionConfig, createReflection, } from "./lib/reflection.js";
|
|
4
5
|
import { canonicalScope } from "./lib/store.js";
|
|
6
|
+
export const REFLECTION_SESSION_TITLE = "[memory-reflect] lessons";
|
|
7
|
+
const REFLECTION_SYSTEM = "You extract durable lessons for a memory store. Reply with the JSON array only; do not call any tool.";
|
|
8
|
+
function transcriptText(messages, chars) {
|
|
9
|
+
const lines = [];
|
|
10
|
+
for (const { info, parts } of messages) {
|
|
11
|
+
const text = parts
|
|
12
|
+
.filter((part) => part.type === "text")
|
|
13
|
+
.map((part) => part.text ?? "")
|
|
14
|
+
.join(" ")
|
|
15
|
+
.trim();
|
|
16
|
+
if (text) {
|
|
17
|
+
lines.push(`${info.role}: ${text}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return lines.join("\n").slice(-chars);
|
|
21
|
+
}
|
|
5
22
|
export const SYSTEM_GUIDANCE = `You have persistent working memory shared with all OpenCode sessions on this machine.
|
|
6
23
|
Recent entries are listed above when present; run memory_search for anything older or more specific.
|
|
7
24
|
When you make a durable decision or learn a lasting fact, call memory_save (kinds: decision, fact, preference, gotcha).
|
|
25
|
+
When a non-obvious approach clearly fails or succeeds, save the lesson (kind=lesson) as soon as it is confirmed, and check stored lessons before retrying a failed approach.
|
|
8
26
|
Never store credentials, tokens, or API keys in memory; the store refuses them.`;
|
|
9
27
|
export const COMPACTION_CONTEXT = "The session is being compacted. After compaction, call memory_save for every durable decision still relevant " +
|
|
10
|
-
"(kind=decision)
|
|
28
|
+
"(kind=decision), any newly learned facts (kind=fact), and every reusable lesson confirmed this session " +
|
|
29
|
+
"(kind=lesson), so other sessions keep this knowledge.";
|
|
11
30
|
export function defaultDbPath(options) {
|
|
12
31
|
if (typeof options?.dbPath === "string" && options.dbPath.length > 0) {
|
|
13
32
|
return options.dbPath;
|
|
@@ -22,14 +41,78 @@ export function defaultDbPath(options) {
|
|
|
22
41
|
export function defaultScope(worktree, directory) {
|
|
23
42
|
return canonicalScope(worktree, directory);
|
|
24
43
|
}
|
|
44
|
+
/** One ephemeral child session per reflection: the transcript tail is distilled with a
|
|
45
|
+
* synchronous prompt on that child (system pins the model to JSON-only, no tools), the
|
|
46
|
+
* reply is parsed here, and the child is deleted. Fire-and-forget: the host event loop
|
|
47
|
+
* never waits on the LLM. */
|
|
48
|
+
function reflectionDeps(client, config, ops, projectScope) {
|
|
49
|
+
const model = modelRef(config.model);
|
|
50
|
+
return {
|
|
51
|
+
isSubagent: async (sessionID) => {
|
|
52
|
+
const session = await client.session.get({ path: { id: sessionID } });
|
|
53
|
+
return !!session.data?.parentID;
|
|
54
|
+
},
|
|
55
|
+
transcript: async (sessionID) => {
|
|
56
|
+
const result = await client.session.messages({ path: { id: sessionID } });
|
|
57
|
+
return transcriptText(result.data ?? [], config.transcriptChars);
|
|
58
|
+
},
|
|
59
|
+
generate: async (prompt, sessionID, signal) => {
|
|
60
|
+
const child = await client.session.create({
|
|
61
|
+
body: { parentID: sessionID, title: REFLECTION_SESSION_TITLE },
|
|
62
|
+
});
|
|
63
|
+
const id = child.data?.id;
|
|
64
|
+
if (!id) {
|
|
65
|
+
throw new Error("reflection session could not be created");
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
const reply = await client.session.prompt({
|
|
69
|
+
path: { id },
|
|
70
|
+
body: {
|
|
71
|
+
parts: [{ type: "text", text: prompt }],
|
|
72
|
+
system: REFLECTION_SYSTEM,
|
|
73
|
+
...(model ? { model } : {}),
|
|
74
|
+
},
|
|
75
|
+
signal,
|
|
76
|
+
});
|
|
77
|
+
return (reply.data?.parts ?? [])
|
|
78
|
+
.filter((part) => part.type === "text")
|
|
79
|
+
.map((part) => part.text ?? "")
|
|
80
|
+
.join("\n");
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
await client.session.delete({ path: { id } }).catch(() => { });
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
saveLesson: (title, body) => {
|
|
87
|
+
ops.save({ kind: "lesson", title, body }, projectScope);
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function modelRef(model) {
|
|
92
|
+
if (!model) {
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
const [providerID, modelID] = model.split("/", 2);
|
|
96
|
+
return providerID && modelID ? { providerID, modelID } : undefined;
|
|
97
|
+
}
|
|
25
98
|
export const MemoryPlugin = async (input, options) => {
|
|
26
99
|
const dbPath = defaultDbPath(options);
|
|
27
100
|
let db = null;
|
|
28
|
-
|
|
101
|
+
let disposed = false;
|
|
102
|
+
// After dispose the store is closed for good: a late path (tool call, in-flight
|
|
103
|
+
// reflection) must not silently reopen a connection that nobody closes again.
|
|
104
|
+
const getDb = () => {
|
|
105
|
+
if (disposed) {
|
|
106
|
+
throw new Error("memory store is closed");
|
|
107
|
+
}
|
|
108
|
+
db ??= openMemoryDb(dbPath);
|
|
109
|
+
return db;
|
|
110
|
+
};
|
|
29
111
|
const ops = createMemoryOps(getDb);
|
|
30
112
|
// Request and compaction hooks carry no directory: the instance scope is the project.
|
|
31
113
|
const projectScope = defaultScope(input.worktree, input.directory);
|
|
32
114
|
const injected = oncePerSession();
|
|
115
|
+
const reflection = parseReflectionConfig(options?.reflection);
|
|
33
116
|
const hooks = {
|
|
34
117
|
tool: {
|
|
35
118
|
memory_save: tool({
|
|
@@ -37,7 +120,7 @@ export const MemoryPlugin = async (input, options) => {
|
|
|
37
120
|
args: {
|
|
38
121
|
kind: tool.schema
|
|
39
122
|
.string()
|
|
40
|
-
.describe("Entry type, e.g. decision, fact, preference, gotcha"),
|
|
123
|
+
.describe("Entry type, e.g. decision, fact, preference, gotcha, lesson"),
|
|
41
124
|
title: tool.schema.string().describe("Short title (max 120 chars)"),
|
|
42
125
|
body: tool.schema.string().describe("Entry content (max 8000 chars)"),
|
|
43
126
|
scope: tool.schema
|
|
@@ -80,12 +163,33 @@ export const MemoryPlugin = async (input, options) => {
|
|
|
80
163
|
},
|
|
81
164
|
execute: async (args, context) => ops.forget(args, defaultScope(context.worktree, context.directory)),
|
|
82
165
|
}),
|
|
166
|
+
memory_stats: tool({
|
|
167
|
+
description: TOOL_DESCRIPTIONS.stats,
|
|
168
|
+
args: {
|
|
169
|
+
scope: tool.schema
|
|
170
|
+
.string()
|
|
171
|
+
.optional()
|
|
172
|
+
.describe("Scope key; defaults to this session's worktree"),
|
|
173
|
+
},
|
|
174
|
+
execute: async (args, context) => ops.stats(defaultScope(args.scope ?? context.worktree ?? context.directory)),
|
|
175
|
+
}),
|
|
83
176
|
},
|
|
84
177
|
};
|
|
178
|
+
if (reflection) {
|
|
179
|
+
const onIdle = createReflection(reflection, reflectionDeps(input.client, reflection, ops, projectScope));
|
|
180
|
+
hooks.event = async ({ event }) => {
|
|
181
|
+
if (event.type === "session.idle") {
|
|
182
|
+
onIdle(event.properties.sessionID);
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
}
|
|
85
186
|
return {
|
|
86
187
|
...hooks,
|
|
87
188
|
"experimental.chat.system.transform": async (input, output) => {
|
|
88
189
|
output.system.push(SYSTEM_GUIDANCE);
|
|
190
|
+
if (disposed) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
89
193
|
const inventory = injected.inject(input.sessionID, () => memoryInventory(getDb, dbPath, projectScope));
|
|
90
194
|
if (inventory !== null) {
|
|
91
195
|
output.system.push(inventory);
|
|
@@ -93,12 +197,16 @@ export const MemoryPlugin = async (input, options) => {
|
|
|
93
197
|
},
|
|
94
198
|
"experimental.session.compacting": async (_input, output) => {
|
|
95
199
|
output.context.push(COMPACTION_CONTEXT);
|
|
200
|
+
if (disposed) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
96
203
|
const inventory = memoryInventory(getDb, dbPath, projectScope);
|
|
97
204
|
if (inventory !== null) {
|
|
98
205
|
output.context.push(inventory);
|
|
99
206
|
}
|
|
100
207
|
},
|
|
101
208
|
dispose: async () => {
|
|
209
|
+
disposed = true;
|
|
102
210
|
injected.reset();
|
|
103
211
|
db?.close();
|
|
104
212
|
db = null;
|
package/dist/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAE3C,OAAO,EAAE,YAAY,EAAiB,MAAM,aAAa,CAAC;AAC1D,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,cAAc,GACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;
|
|
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.
|
|
3
|
+
"version": "0.4.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",
|
|
@@ -51,6 +51,6 @@
|
|
|
51
51
|
"test": "vitest run",
|
|
52
52
|
"test:watch": "vitest",
|
|
53
53
|
"test:coverage": "vitest run --coverage",
|
|
54
|
-
"verify": "pnpm run check && pnpm run typecheck && pnpm run test"
|
|
54
|
+
"verify": "pnpm run check && pnpm run typecheck && pnpm run test:coverage"
|
|
55
55
|
}
|
|
56
56
|
}
|