@log0u7/opencode-memory 0.3.0 → 0.5.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,6 +5,111 @@ 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.5.0] - 2026-10-20
9
+
10
+ ### Fixed
11
+
12
+ - The plugin opened its SQLite store through the `Bun.sqlite` global, which Bun 1.3
13
+ removed: the first `openMemoryDb` threw on every Bun 1.3 runtime, which is what the
14
+ installed OpenCode embeds. It now reads `process.versions.bun` and loads the `bun:sqlite`
15
+ module through the same `createRequire` seam as `node:sqlite`, so one path covers old and
16
+ new Bun. Verified against the built output under Bun 1.3.14 (open, save, search, stats,
17
+ trash and restore).
18
+
19
+ ### Added
20
+
21
+ - `memory_restore` puts a trashed entry back by id, wherever it lived. `memory_forget` now
22
+ trashes instead of dropping, so a wrong delete is recoverable; a sweep hard-deletes trash
23
+ older than 30 days so the trash cannot grow without bound.
24
+ - Entries record the agent that wrote them (`source`; distilled lessons carry
25
+ `reflection`), the session inventory names the writer, and a store created by an earlier
26
+ release gains the column on open instead of failing to read.
27
+
28
+ ### Security
29
+
30
+ - The secret guard now refuses the token formats released since it was written: GitHub
31
+ fine-grained PATs (`github_pat_`), the GitHub OAuth and server-to-server classes
32
+ (`gho_`, `ghs_`, `ghu_`, `ghr_`), GitLab rotate tokens (`glrt-`), npm granular tokens
33
+ (`npm_`), AWS temporary key ids (`ASIA`), Google OAuth access tokens (`ya29.`) and Slack
34
+ incoming webhook URLs. The existing `sk-` pattern already covered Anthropic `sk-ant-`
35
+ keys. The three patterns whose tail class allows `_` or `-` (`github_pat_`, `gl(?:pat|rt)-`
36
+ and `ya29.`) additionally require a digit or an uppercase letter in the tail, so a sentence
37
+ written as one long lowercase snake_case or hyphenated token right after a prefix is saved
38
+ instead of being refused as a credential. A run carrying a digit or an uppercase anywhere
39
+ inside the run of the pattern's tail class is still refused: the guard forgives lowercase
40
+ prose only. The older `sk-`, `AIza` and `xox[ap]-` patterns keep their original shape.
41
+
42
+ ### Changed
43
+
44
+ - `memory_search` fuses its two rankings instead of discarding one: entries matching
45
+ every query token come first, then prefix matches fill the remaining slots. A query
46
+ where two entries hold every token used to answer a limit of ten with two results.
47
+ - `memory_save` reports whether it created an entry or rewrote the one already holding that
48
+ scope and title, instead of one undifferentiated receipt: the in-place dedup rewrite is
49
+ no longer silent.
50
+ - `memory_forget` lists the titles and ids it removed (ten named, then a count of the
51
+ rest) instead of only a count, so a broad query delete is auditable. `store.forget`
52
+ now returns the removed entries rather than a number.
53
+ - `memory_stats` reports the store size on disk and the oldest live entry beside the kind
54
+ counts. The store has no size cap and no eviction, so this is the only signal that it is
55
+ growing.
56
+ - Dev-dependency bumps from the Dependabot minor-and-patch group: `@biomejs/biome`
57
+ 2.5.15, `@opencode-ai/plugin` 1.18.35 (with the transitive `@opencode-ai/sdk` 1.18.35),
58
+ `lefthook` 2.1.17, and in-range lockfile refreshes (vitest 5.0.3, `@types/node` 26.6.4).
59
+
60
+ ## [0.4.0] - 2026-10-14
61
+
62
+ ### Added
63
+
64
+ - Lesson feedback loop: the system guidance and the compaction prompt now nudge the agent
65
+ to save reusable lessons (`kind=lesson`), and lessons are injected first in later
66
+ sessions' inventory, ahead of recency noise (`store.list` gained an optional `kind`
67
+ filter).
68
+ - Optional LLM reflection at `session.idle`, off by default: with
69
+ `reflection.enabled: true` in the plugin options, the plugin distills at most
70
+ `maxLessons` durable lessons from the bounded session tail with one LLM call per
71
+ session and saves them as lessons. Options: `model` (`"provider/model"`), `maxLessons`
72
+ (3), `transcriptChars` (8000), `timeoutMs` (60000). Subagent sessions are skipped and the
73
+ run is best-effort: failures are silent.
74
+
75
+ - New `memory_stats` tool (both hosts): counts entries by kind for the project scope
76
+ (plus global) with a total, so the model can see how much memory exists, including
77
+ how many lessons reflection has written, without paging through `memory_list`.
78
+
79
+ ### Changed
80
+
81
+ - `pnpm verify` (and therefore CI) now runs the suite with coverage, so the coverage gate
82
+ the docs promise (90% lines/functions/statements, 80% branches) is actually enforced
83
+ instead of only configured.
84
+
85
+ ### Fixed
86
+
87
+ - The store database is created with restrictive permissions: the file is `0600`
88
+ (enforced on every open, regardless of the process umask, so databases created earlier
89
+ with a permissive umask are tightened too) and the directory is `0700` when the plugin
90
+ creates it, as `SECURITY.md` already promised.
91
+ - Secret detection now also refuses JWTs, PEM private key blocks, AWS access key ids,
92
+ GitLab personal access tokens, Google API keys, and Slack user/app tokens, in addition
93
+ to the existing OpenAI, GitHub, and Slack bot formats.
94
+ - `memory_forget` by id now deletes across scopes: ids are globally unique, so an exact
95
+ id match is precise anywhere, and closed worktrees stay cleanable from any project.
96
+ Query deletes keep the scope lock (a fuzzy match must never silently hit other
97
+ projects).
98
+ - Reflection no longer permanently skips a session whose first `session.idle` arrived
99
+ before the transcript reached the distillation floor: the session is marked reflected
100
+ only once a run is actually attempted, so a later, longer turn still gets exactly one
101
+ reflection.
102
+ - The README safety-limits note on `memory_forget` now matches the code: query deletes
103
+ stay in scope, id deletes are global.
104
+ - Reflection's LLM call is now bounded by a timeout (`reflection.timeoutMs`, default 60s):
105
+ a hung provider no longer wedges a session forever, and the abort reaches the SDK so the
106
+ v1 host still deletes its ephemeral child session.
107
+ - The v2 event stream now reconnects with a capped backoff after an error instead of
108
+ silently ending the reflection loop for good.
109
+ - After `dispose` (v1) or `cleanup` (v2), memory operations no longer reopen the SQLite
110
+ connection: a late tool call or in-flight reflection returns a readable error instead of
111
+ leaking a connection that nobody closes again.
112
+
8
113
  ## [0.3.0] - 2026-10-14
9
114
 
10
115
  ### Added
package/README.md CHANGED
@@ -8,14 +8,14 @@
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 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.
11
+ Every session gets six tools (`memory_save`, `memory_search`, `memory_list`, `memory_forget`, `memory_stats`, `memory_restore`). Entries are scoped per project worktree (plus a `global` scope), searched with SQLite FTS5 (BM25 ranking, entries matching every query token first, then prefix matches fill the remaining slots). 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).
18
- - Entries that look like secrets (`sk-…`, `ghp_…`, `xoxb-…`) are refused, in the title, kind, body and tags alike.
17
+ - `memory_forget` by query only deletes inside the caller's scope (plus the shared `global` scope); by id it trashes the entry wherever it lives, since ids are globally unique. Either way it reports the titles and ids it removed, so a broad query delete is auditable.
18
+ - Entries that look like secrets (`sk-…`, `github_pat_…`, `gho_…`, `glpat-…`, `xoxb-…`, webhook URLs, …) are refused, in the title, kind, body and tags alike.
19
19
 
20
20
  ## Install
21
21
 
@@ -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
@@ -55,18 +56,41 @@ memory_forget(id="...")
55
56
  | Tool | Args | Description |
56
57
  |---|---|---|
57
58
  | `memory_save` | `kind`, `title`, `body`, `scope?`, `tags?`, `expires_at?` | Save an entry. Default scope is the session worktree. |
58
- | `memory_search` | `query`, `scope?`, `limit?` | FTS5 BM25 search; strict match first, prefix OR fallback when nothing matches. |
59
+ | `memory_search` | `query`, `scope?`, `limit?` | FTS5 BM25 search; entries matching every query token first, then prefix matches fill the remaining slots. |
59
60
  | `memory_list` | `scope?`, `limit?` | Recent entries, newest first. |
60
- | `memory_forget` | `id` or `query` | Delete one entry by id, or all matching a query. |
61
+ | `memory_forget` | `id` or `query` | Trash one entry by id, or all matching a query. |
62
+ | `memory_stats` | `scope?` | Count entries by kind (plus global) with a total, plus the store size and oldest entry. |
63
+ | `memory_restore` | `id` | Put a trashed entry back, wherever it lived. |
61
64
 
62
- Entry `kind` is free-form; conventions that work well: `decision`, `fact`, `preference`, `gotcha`, `compaction`.
65
+ Entry `kind` is free-form; conventions that work well: `decision`, `fact`, `preference`, `gotcha`, `lesson`, `compaction`.
66
+
67
+ ### Feedback loop
68
+
69
+ 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.
70
+
71
+ 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.
72
+
73
+ ```json
74
+ {
75
+ "plugin": [
76
+ ["@log0u7/opencode-memory", { "reflection": { "enabled": true } }]
77
+ ]
78
+ }
79
+ ```
80
+
81
+ Options: `enabled` (default `false`), `model` (`"provider/model"`, default: the host's model), `maxLessons` (default 3, cap 10), `transcriptChars` (default 8000).
82
+
83
+ 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
84
 
64
85
  ### Notes
65
86
 
66
87
  - All sessions on the machine share one store (WAL mode, concurrent-safe).
67
- - Entries that look like secrets (`sk-…`, `ghp_…`, `xoxb-…`) are refused, in the title, kind, body and tags.
88
+ - Entries that look like secrets (`sk-…`, `github_pat_…`, `gho_…`, `glpat-…`, `xoxb-…`, webhook URLs, …) are refused, in the title, kind, body and tags.
68
89
  - 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.
90
+ - `memory_forget` never destroys anything: both forms move entries to a trash that `memory_restore` brings back by id, and a sweep drops trash older than 30 days. Query deletes still never leave the scope they are called from, so a project cannot fuzzily delete another project's entries, and by id they trash anywhere (ids are globally unique), which keeps closed worktrees cleanable. Both forms report the titles and ids they removed.
91
+ - `memory_save` reports whether it created an entry or rewrote the one already holding that scope and title, so a silent overwrite is visible to the model.
92
+ - `memory_stats` reports the store size on disk and the oldest live entry next to the kind counts. The store has no size cap and no eviction, so this is the only growth signal.
93
+ - Every entry records the agent that wrote it (`source`; distilled lessons carry `reflection`), and the session inventory names the writer, so knowledge from a sibling session or from reflection is distinguishable from what this session wrote.
70
94
  - 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
95
  - 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
96
  - 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 CHANGED
@@ -4,4 +4,7 @@ export type MemoryDb = {
4
4
  close(): void;
5
5
  };
6
6
  export declare function openMemoryDb(path: string): MemoryDb;
7
+ /** Size the database occupies on disk, WAL sidecars excluded. The store has no size cap
8
+ * and no eviction, so this is the only signal a caller gets that it is growing. */
9
+ export declare function databaseSizeBytes(db: MemoryDb): number;
7
10
  //# sourceMappingURL=db.d.ts.map
@@ -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;AA+BF,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;AA4CF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CA4BnD;AAED;mFACmF;AACnF,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,CAItD"}
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 = `
@@ -11,7 +11,9 @@ CREATE TABLE IF NOT EXISTS entries (
11
11
  tags TEXT NOT NULL DEFAULT '[]',
12
12
  created_at INTEGER NOT NULL,
13
13
  updated_at INTEGER NOT NULL,
14
- expires_at INTEGER
14
+ expires_at INTEGER,
15
+ source TEXT,
16
+ deleted_at INTEGER
15
17
  );
16
18
  CREATE VIRTUAL TABLE IF NOT EXISTS entries_fts USING fts5(
17
19
  title, body, tags,
@@ -29,14 +31,59 @@ CREATE TRIGGER IF NOT EXISTS entries_au AFTER UPDATE ON entries BEGIN
29
31
  END;
30
32
  CREATE INDEX IF NOT EXISTS entries_scope_title ON entries(scope, lower(trim(title)));
31
33
  `;
34
+ /** Columns added after the first release. `CREATE TABLE IF NOT EXISTS` never adds a column
35
+ * to an existing database, so each one is backfilled on open. */
36
+ const ADDED_COLUMNS = ["source TEXT", "deleted_at INTEGER"];
37
+ // SECURITY.md promises restrictive permissions whatever the user's umask is, so the
38
+ // directory and files are mode-enforced explicitly instead of left to the umask.
39
+ // POSIX only: chmodSync on Windows flips a read-only bit, which would corrupt usage.
40
+ // Directories the plugin did not create are left to their owner: the db file is
41
+ // 0600 anyway, so a loose parent only leaks file names.
42
+ const POSIX = process.platform !== "win32";
32
43
  export function openMemoryDb(path) {
33
44
  if (path !== ":memory:") {
34
- mkdirSync(dirname(path), { recursive: true });
45
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
35
46
  }
36
47
  const conn = openSqlite(path);
37
48
  conn.run("PRAGMA journal_mode = WAL");
38
49
  conn.run("PRAGMA busy_timeout = 5000");
39
50
  conn.exec(SCHEMA);
51
+ addMissingColumns(conn);
52
+ if (POSIX && path !== ":memory:") {
53
+ // Also fixes databases created earlier with a permissive umask.
54
+ chmodSync(path, 0o600);
55
+ for (const suffix of ["-wal", "-shm"]) {
56
+ try {
57
+ chmodSync(`${path}${suffix}`, 0o600);
58
+ }
59
+ catch (error) {
60
+ // Only tolerate a sidecar that is not written yet (or already
61
+ // checkpointed away); anything else is a real failure.
62
+ if (error.code !== "ENOENT") {
63
+ throw error;
64
+ }
65
+ }
66
+ }
67
+ }
40
68
  return { conn, close: () => conn.close() };
41
69
  }
70
+ /** Size the database occupies on disk, WAL sidecars excluded. The store has no size cap
71
+ * and no eviction, so this is the only signal a caller gets that it is growing. */
72
+ export function databaseSizeBytes(db) {
73
+ const pages = db.conn.get("PRAGMA page_count")?.page_count ?? 0;
74
+ const pageSize = db.conn.get("PRAGMA page_size")?.page_size ?? 0;
75
+ return pages * pageSize;
76
+ }
77
+ /** Backfills the columns added since a store was first created, so a database written by
78
+ * an older release opens on the current schema with its rows intact. Idempotent: the
79
+ * second open finds nothing to add. */
80
+ function addMissingColumns(conn) {
81
+ const present = new Set(conn.all("PRAGMA table_info(entries)").map((column) => column.name));
82
+ for (const column of ADDED_COLUMNS) {
83
+ const name = column.split(" ")[0] ?? "";
84
+ if (!present.has(name)) {
85
+ conn.run(`ALTER TABLE entries ADD COLUMN ${column}`);
86
+ }
87
+ }
88
+ }
42
89
  //# sourceMappingURL=db.js.map
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;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"}
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6Bd,CAAC;AAEF;iEACiE;AACjE,MAAM,aAAa,GAAG,CAAC,aAAa,EAAE,oBAAoB,CAAC,CAAC;AAE5D,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;IAClB,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAExB,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;AAED;mFACmF;AACnF,MAAM,UAAU,iBAAiB,CAAC,EAAY;IAC5C,MAAM,KAAK,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAyB,mBAAmB,CAAC,EAAE,UAAU,IAAI,CAAC,CAAC;IACxF,MAAM,QAAQ,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAwB,kBAAkB,CAAC,EAAE,SAAS,IAAI,CAAC,CAAC;IACxF,OAAO,KAAK,GAAG,QAAQ,CAAC;AAC1B,CAAC;AAED;;uCAEuC;AACvC,SAAS,iBAAiB,CAAC,IAAgB;IACzC,MAAM,OAAO,GAAG,IAAI,GAAG,CACrB,IAAI,CAAC,GAAG,CAAmB,4BAA4B,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CACtF,CAAC;IACF,KAAK,MAAM,MAAM,IAAI,aAAa,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACxC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACvB,IAAI,CAAC,GAAG,CAAC,kCAAkC,MAAM,EAAE,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -1,10 +1,11 @@
1
- import type { MemoryDb } from "./db.js";
1
+ import { type MemoryDb } from "./db.js";
2
2
  export type SaveArgs = {
3
3
  kind: string;
4
4
  title: string;
5
5
  body: string;
6
6
  tags?: string[] | undefined;
7
7
  expires_at?: number | null | undefined;
8
+ source?: string | undefined;
8
9
  };
9
10
  export type SearchArgs = {
10
11
  query: string;
@@ -17,23 +18,30 @@ export type ForgetArgs = {
17
18
  id?: string | undefined;
18
19
  query?: string | undefined;
19
20
  };
21
+ export type RestoreArgs = {
22
+ id: string;
23
+ };
20
24
  /** One shared behaviour behind both plugin entry points. Every operation returns the
21
25
  * model-facing text directly, so a caller cannot bypass caps or error handling. */
22
26
  export type MemoryOps = {
23
- save: (args: SaveArgs, scope: string) => string;
27
+ save: (args: SaveArgs, scope: string, source?: string) => string;
24
28
  search: (args: SearchArgs, scope: string) => string;
25
29
  list: (args: ListArgs, scope: string) => string;
26
30
  forget: (args: ForgetArgs, scope: string) => string;
31
+ stats: (scope: string) => string;
32
+ restore: (args: RestoreArgs) => string;
27
33
  };
28
34
  export declare function createMemoryOps(getDb: () => MemoryDb): MemoryOps;
29
35
  /** How much of the store is summarised into a prompt, worst case. */
30
36
  export declare const INVENTORY_MAX_CHARS = 1200;
31
37
  /** Tool descriptions shared by both entry points so the two hosts cannot drift. */
32
38
  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.";
39
+ readonly save: "Save a durable entry to shared working memory (decisions, facts, preferences, gotchas, lessons). Never store credentials.";
40
+ readonly search: "Search shared working memory (all sessions on this machine). Results include this project's scope and global entries: entries matching every query token come first, then entries matching some token as a prefix fill the remaining slots.";
35
41
  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.";
42
+ 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.";
43
+ 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.";
44
+ readonly restore: "Put a trashed entry back, by id. Deletes are recoverable: memory_forget reports the ids it trashed, and memory_restore brings one of them back. An entry stays restorable for 30 days.";
37
45
  };
38
46
  /**
39
47
  * Produces at most one injection per session key, and only on the first request where
@@ -48,7 +56,8 @@ export declare function oncePerSession(): {
48
56
  };
49
57
  /**
50
58
  * 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
59
+ * into the compaction context. Lessons are promoted above recency noise so a stored
60
+ * lesson survives a chatty store. `null` when there is nothing worth injecting, so a
52
61
  * caller pushes nothing at all.
53
62
  */
54
63
  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;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"}
1
+ {"version":3,"file":"memory-ops.d.ts","sourceRoot":"","sources":["../../src/lib/memory-ops.ts"],"names":[],"mappings":"AAEA,OAAO,EAAqB,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAC;AAgB3D,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;IACvC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B,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;AACjF,MAAM,MAAM,WAAW,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzC;mFACmF;AACnF,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACjE,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;IACjC,OAAO,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,MAAM,CAAC;CACxC,CAAC;AAEF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,QAAQ,GAAG,SAAS,CAuEhE;AAmBD,qEAAqE;AACrE,eAAO,MAAM,mBAAmB,OAAO,CAAC;AAIxC,mFAAmF;AACnF,eAAO,MAAM,iBAAiB;aAC5B,IAAI,EAAE,2HAA2H;aACjI,MAAM,EACJ,6OAA6O;aAC/O,IAAI,EAAE,+EAA+E;aACrF,MAAM,EACJ,yPAAyP;aAC3P,KAAK,EACH,uLAAuL;aACzL,OAAO,EACL,wLAAwL;CAClL,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,CA0Bf"}
@@ -1,17 +1,19 @@
1
1
  import { existsSync } from "node:fs";
2
- import { forget, list, renderEntries, save, search, SecretDetectedError } from "./store.js";
2
+ import { databaseSizeBytes } from "./db.js";
3
+ import { forget, list, oldestCreated, renderEntries, restore, saveEntry, search, SecretDetectedError, stats, } from "./store.js";
3
4
  export function createMemoryOps(getDb) {
4
5
  return {
5
- save: (args, scope) => run(() => {
6
- const entry = save(getDb(), {
6
+ save: (args, scope, source) => run(() => {
7
+ const { entry, created } = saveEntry(getDb(), {
7
8
  scope,
8
9
  kind: args.kind,
9
10
  title: args.title,
10
11
  body: args.body,
11
12
  tags: args.tags ?? [],
12
13
  expires_at: args.expires_at ?? null,
14
+ ...(source !== undefined ? { source } : {}),
13
15
  });
14
- return `saved ${entry.id} (${entry.scope})`;
16
+ return `saved ${entry.id} (${entry.scope}, ${created ? "created" : "updated"})`;
15
17
  }),
16
18
  search: (args, scope) => run(() => {
17
19
  const hits = search(getDb(), {
@@ -29,27 +31,66 @@ export function createMemoryOps(getDb) {
29
31
  return entries.length === 0 ? "no memory entries" : renderEntries(entries);
30
32
  }),
31
33
  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"}`;
34
+ const removed = args.id !== undefined
35
+ ? forget(getDb(), { id: args.id, scope })
36
+ : args.query !== undefined
37
+ ? forget(getDb(), { query: args.query, scope })
38
+ : null;
39
+ if (removed === null) {
40
+ return "provide id or query";
35
41
  }
36
- if (args.query !== undefined) {
37
- const deleted = forget(getDb(), { query: args.query, scope });
38
- return `deleted ${deleted} entr${deleted === 1 ? "y" : "ies"}`;
42
+ if (removed.length === 0) {
43
+ return "deleted 0 entries";
39
44
  }
40
- return "provide id or query";
45
+ const plural = removed.length === 1 ? "y" : "ies";
46
+ return `deleted ${removed.length} entr${plural}: ${renderRemoved(removed)}`;
47
+ }),
48
+ stats: (scope) => run(() => {
49
+ const counts = stats(getDb(), { scope });
50
+ if (counts.length === 0) {
51
+ return "no memory entries";
52
+ }
53
+ const total = counts.reduce((sum, row) => sum + row.count, 0);
54
+ const oldest = oldestCreated(getDb(), { scope });
55
+ const size = formatBytes(databaseSizeBytes(getDb()));
56
+ const age = oldest === null ? "" : `, oldest: ${dayOf(oldest)}`;
57
+ return `store: ${size}${age}, ${counts
58
+ .map((row) => `${row.kind}: ${row.count}`)
59
+ .join(", ")}, total: ${total}`;
60
+ }),
61
+ restore: (args) => run(() => {
62
+ const entry = restore(getDb(), { id: args.id });
63
+ return entry === null
64
+ ? "no entry with that id"
65
+ : `restored ${JSON.stringify(entry.title)} (${entry.id})`;
41
66
  }),
42
67
  };
43
68
  }
69
+ /** How many removed entries a delete report names before it summarizes the rest, so a
70
+ * broad query delete stays a bounded tool result. */
71
+ const REMOVED_REPORT_MAX = 10;
72
+ function renderRemoved(entries) {
73
+ const listed = entries
74
+ .slice(0, REMOVED_REPORT_MAX)
75
+ .map((entry) => `${JSON.stringify(entry.title)} (${entry.id})`);
76
+ const rest = entries.length - REMOVED_REPORT_MAX;
77
+ return rest > 0 ? `${listed.join(", ")}, and ${rest} more` : listed.join(", ");
78
+ }
79
+ function formatBytes(bytes) {
80
+ const kb = bytes / 1024;
81
+ return kb < 1024 ? `${kb.toFixed(1)} KB` : `${(kb / 1024).toFixed(1)} MB`;
82
+ }
44
83
  /** How much of the store is summarised into a prompt, worst case. */
45
84
  export const INVENTORY_MAX_CHARS = 1200;
46
- const INVENTORY_HEADER = "Memory already stored for this project (newest first):";
85
+ const INVENTORY_HEADER = "Memory already stored for this project (lessons first, then newest):";
47
86
  /** Tool descriptions shared by both entry points so the two hosts cannot drift. */
48
87
  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.",
88
+ save: "Save a durable entry to shared working memory (decisions, facts, preferences, gotchas, lessons). Never store credentials.",
89
+ search: "Search shared working memory (all sessions on this machine). Results include this project's scope and global entries: entries matching every query token come first, then entries matching some token as a prefix fill the remaining slots.",
51
90
  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.",
91
+ 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.",
92
+ 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.",
93
+ restore: "Put a trashed entry back, by id. Deletes are recoverable: memory_forget reports the ids it trashed, and memory_restore brings one of them back. An entry stays restorable for 30 days.",
53
94
  };
54
95
  /**
55
96
  * Produces at most one injection per session key, and only on the first request where
@@ -80,21 +121,26 @@ export function oncePerSession() {
80
121
  }
81
122
  /**
82
123
  * 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
124
+ * into the compaction context. Lessons are promoted above recency noise so a stored
125
+ * lesson survives a chatty store. `null` when there is nothing worth injecting, so a
84
126
  * caller pushes nothing at all.
85
127
  */
86
128
  export function memoryInventory(getDb, dbPath, scope) {
87
129
  if (!existsSync(dbPath)) {
88
130
  return null;
89
131
  }
90
- const entries = list(getDb(), { scope, limit: 50 });
91
- if (entries.length === 0) {
132
+ const lessons = list(getDb(), { scope, limit: 50, kind: "lesson" });
133
+ const others = list(getDb(), { scope, limit: 50 });
134
+ if (lessons.length === 0 && others.length === 0) {
92
135
  return null;
93
136
  }
137
+ const seen = new Set(lessons.map((entry) => entry.id));
138
+ const ordered = [...lessons, ...others.filter((entry) => !seen.has(entry.id))];
94
139
  const lines = [INVENTORY_HEADER];
95
140
  let size = INVENTORY_HEADER.length;
96
- for (const entry of entries) {
97
- const line = `- [${entry.kind}] ${entry.title} (updated ${dayOf(entry.updated_at)})`;
141
+ for (const entry of ordered) {
142
+ const by = entry.source === undefined ? "" : `, by ${entry.source}`;
143
+ const line = `- [${entry.kind}] ${entry.title} (updated ${dayOf(entry.updated_at)}${by})`;
98
144
  if (size + line.length + 1 > INVENTORY_MAX_CHARS) {
99
145
  break;
100
146
  }
@@ -1 +1 @@
1
- {"version":3,"file":"memory-ops.js","sourceRoot":"","sources":["../../src/lib/memory-ops.ts"],"names":[],"mappings":"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"}
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"}