@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 +105 -0
- package/README.md +32 -8
- package/dist/lib/db.d.ts +3 -0
- package/dist/lib/db.d.ts.map +1 -1
- package/dist/lib/db.js +50 -3
- package/dist/lib/db.js.map +1 -1
- package/dist/lib/memory-ops.d.ts +15 -6
- package/dist/lib/memory-ops.d.ts.map +1 -1
- package/dist/lib/memory-ops.js +66 -20
- 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/sqlite.d.ts +1 -1
- package/dist/lib/sqlite.d.ts.map +1 -1
- package/dist/lib/sqlite.js +12 -9
- package/dist/lib/sqlite.js.map +1 -1
- package/dist/lib/store.d.ts +51 -4
- package/dist/lib/store.d.ts.map +1 -1
- package/dist/lib/store.js +161 -57
- package/dist/lib/store.js.map +1 -1
- package/dist/plugin-v2.d.ts.map +1 -1
- package/dist/plugin-v2.js +131 -5
- 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 +119 -4
- package/dist/plugin.js.map +1 -1
- package/dist/types/entry.d.ts +4 -0
- package/dist/types/entry.d.ts.map +1 -1
- package/package.json +5 -5
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
|
|
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-…`, `
|
|
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;
|
|
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` |
|
|
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-…`, `
|
|
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
|
|
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
|
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;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
|
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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"}
|
package/dist/lib/memory-ops.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import type
|
|
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.
|
|
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.
|
|
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,
|
|
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"}
|
package/dist/lib/memory-ops.js
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import {
|
|
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 =
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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 (
|
|
37
|
-
|
|
38
|
-
return `deleted ${deleted} entr${deleted === 1 ? "y" : "ies"}`;
|
|
42
|
+
if (removed.length === 0) {
|
|
43
|
+
return "deleted 0 entries";
|
|
39
44
|
}
|
|
40
|
-
|
|
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 (
|
|
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.
|
|
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.
|
|
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
|
|
91
|
-
|
|
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
|
|
97
|
-
const
|
|
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":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,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"}
|