@kisev/memomatic 1.0.0-dev.73.gb5cf464c7ec9 → 1.0.0-dev.74.ge851c073615d

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/README.md CHANGED
@@ -4,8 +4,8 @@
4
4
 
5
5
  `@kisev/memomatic` is a personal learning memory for agents following the OpenClaw
6
6
  memory architecture: a tiered Markdown corpus you can read as plain files, a
7
- rebuildable SQLite index with FTS5 and optional local embeddings, and a nightly
8
- `dream` consolidation sweep.
7
+ rebuildable SQLite index with FTS5 and optional local embeddings, a `sessions`
8
+ extraction pass, and a nightly `dream` consolidation sweep.
9
9
 
10
10
  ## Install
11
11
 
@@ -21,24 +21,32 @@ automatic context injection is used. For background processing, run the CLI:
21
21
 
22
22
  ```bash
23
23
  memomatic process # validate the inbox and index accepted entries (no model)
24
- memomatic dream # full sweep: inbox + sessions + consolidation
24
+ memomatic sessions # extract episodic memory from OpenCode sessions (model)
25
+ memomatic dream # consolidation: inbox + promotion + bounded rewrite + archive
25
26
  ```
26
27
 
27
- The nightly sweep is scheduled by the systemd user units in `assets/systemd/`
28
- (`memomatic-dream.service` and `memomatic-dream.timer`).
28
+ Nightly runs are scheduled by the systemd user units in `assets/systemd/`
29
+ (`memomatic-sessions.service`/`.timer` for extraction and
30
+ `memomatic-dream.service`/`.timer` for consolidation).
29
31
 
30
- Configure `dream.model` and an OpenCode provider before expecting session
31
- extraction; without a model, the session watermark is preserved. Follow the
32
+ Configure `sessions.model` (or keep a legacy `dream.model`) and an OpenCode
33
+ provider before expecting session extraction; without a model, the session
34
+ watermark is preserved. Follow the
32
35
  [setup, first-entry, and scheduling guide](../../docs/how-to/memomatic.md).
33
36
 
34
- ## CLI and Dream
37
+ ## CLI, Sessions, and Dream
35
38
 
36
39
  See [CLI conventions](../../docs/reference/cli.md) and
37
40
  [Dream controls](../../docs/how-to/memomatic.md#observe-limit-and-resume-dream).
38
- `dream --plan` inspects pending work without a model; `dream --dry-run` may call
39
- models. Normal Dream drains the eligible snapshot with per-fragment checkpoints,
40
- reusing one OpenCode server and unchanged embeddings. The first legacy-cursor
41
- migration checks prior history once without discarding existing memory.
41
+ `sessions --plan` inspects pending session work without a model; `dream --plan`
42
+ counts pending inbox files and promotion candidates. `--dry-run` previews
43
+ either command and may still call models. Sessions drain the eligible snapshot
44
+ with per-fragment checkpoints, reusing one OpenCode server and unchanged
45
+ embeddings; Dream then promotes usage-gated episodic entries into `MEMORY.md`
46
+ through bounded consolidation and archives old entries. The first
47
+ legacy-cursor migration checks prior history once without discarding existing
48
+ memory. `status` shows the queue: pending inbox files, session backlog,
49
+ promotion candidates, and the last run of each sweep.
42
50
 
43
51
  ## Inbox
44
52
 
@@ -63,13 +71,14 @@ is exposed in search responses.
63
71
 
64
72
  ## Surfaces
65
73
 
66
- - `memomatic` CLI: `process`, `dream`, `search`, `status`, `index`.
74
+ - `memomatic` CLI: `process`, `sessions`, `dream`, `search`, `status`, `index`.
67
75
  - MCP stdio server with `memory_search`, `memory_get`, `memory_write`, and
68
76
  `memory_forget` tools.
69
77
 
70
- The model initiates memory searches through visible MCP tool calls. Dream
71
- processes OpenCode history separately; connecting another host does not import
72
- its session history. Agentomatic and memomatic are installed independently.
78
+ The model initiates memory searches through visible MCP tool calls. The
79
+ `sessions` command processes OpenCode history separately; connecting another
80
+ host does not import its session history. Agentomatic and memomatic are
81
+ installed independently.
73
82
 
74
83
  Nothing is deleted without the explicit directives documented in
75
84
  `MEMORY_RULES.md`.
package/README.ru.md CHANGED
@@ -4,8 +4,8 @@
4
4
 
5
5
  `@kisev/memomatic` — персональная обучающая память для агентов по архитектуре
6
6
  памяти OpenClaw: ярусный Markdown-корпус, который можно читать как обычные файлы,
7
- пересобираемый SQLite-индекс с FTS5 и опциональными локальными эмбеддингами и
8
- ночной проход консолидации `dream`.
7
+ пересобираемый SQLite-индекс с FTS5 и опциональными локальными эмбеддингами,
8
+ проход извлечения `sessions` и ночной проход консолидации `dream`.
9
9
 
10
10
  ## Установка
11
11
 
@@ -21,24 +21,32 @@ npm install --global @kisev/memomatic
21
21
 
22
22
  ```bash
23
23
  memomatic process # валидация inbox и индексация принятого (без модели)
24
- memomatic dream # полный проход: inbox + сессии + консолидация
24
+ memomatic sessions # извлечение эпизодической памяти из сессий OpenCode (модель)
25
+ memomatic dream # консолидация: inbox + продвижение + перезапись + архив
25
26
  ```
26
27
 
27
- Ночной проход планируют user-юниты systemd из `assets/systemd/`
28
- (`memomatic-dream.service` и `memomatic-dream.timer`).
28
+ Ночные проходы планируют user-юниты systemd из `assets/systemd/`
29
+ (`memomatic-sessions.service`/`.timer` для извлечения и
30
+ `memomatic-dream.service`/`.timer` для консолидации).
29
31
 
30
- Перед извлечением сессий настройте `dream.model` и провайдера OpenCode;
31
- без модели отметка обработанных сессий сохраняется. См.
32
+ Перед извлечением сессий настройте `sessions.model` (или оставьте старый
33
+ `dream.model`) и провайдера OpenCode; без модели отметка обработанных сессий
34
+ сохраняется. См.
32
35
  [настройку, первую запись и расписание](../../docs/ru/how-to/memomatic.md).
33
36
 
34
- ## CLI и Dream
37
+ ## CLI, Sessions и Dream
35
38
 
36
39
  См. [соглашения CLI](../../docs/ru/reference/cli.md) и
37
40
  [управление Dream](../../docs/ru/how-to/memomatic.md#наблюдение-ограничения-и-продолжение-dream).
38
- `dream --plan` читает очередь без модели; `dream --dry-run` может вызывать модели.
39
- Обычный Dream обрабатывает весь подходящий снимок с контрольными точками фрагментов,
40
- переиспользуя один сервер OpenCode и неизменённые эмбеддинги. Первая миграция старого
41
- курсора перепроверяет историю один раз, сохраняя накопленную память.
41
+ `sessions --plan` читает очередь сессий без модели; `dream --plan` считает
42
+ ожидающие файлы inbox и кандидатов на продвижение. `--dry-run` показывает
43
+ превью любой команды и может вызывать модели. Sessions обрабатывают весь
44
+ подходящий снимок с контрольными точками фрагментов, переиспользуя один сервер
45
+ OpenCode и неизменённые эмбеддинги; Dream затем продвигает эпизодические записи,
46
+ прошедшие пороги использования, в `MEMORY.md` через ограниченную консолидацию и
47
+ архивирует старое. Первая миграция старого курсора перепроверяет историю один
48
+ раз, сохраняя накопленную память. `status` показывает очередь: ожидающие файлы
49
+ inbox, бэклог сессий, кандидатов на продвижение и время последнего прохода.
42
50
 
43
51
  ## Inbox
44
52
 
@@ -63,12 +71,12 @@ memomatic dream # полный проход: inbox + сессии + кон
63
71
 
64
72
  ## Поверхности
65
73
 
66
- - CLI `memomatic`: `process`, `dream`, `search`, `status`, `index`.
74
+ - CLI `memomatic`: `process`, `sessions`, `dream`, `search`, `status`, `index`.
67
75
  - MCP-сервер stdio с инструментами `memory_search`, `memory_get`,
68
76
  `memory_write` и `memory_forget`.
69
77
 
70
- Модель сама инициирует поиск памяти через видимые MCP-вызовы. Dream отдельно
71
- обрабатывает историю OpenCode; подключение другого хоста не импортирует его
72
- сессии. Agentomatic и memomatic устанавливаются независимо.
78
+ Модель сама инициирует поиск памяти через видимые MCP-вызовы. Команда
79
+ `sessions` отдельно обрабатывает историю OpenCode; подключение другого хоста
80
+ не импортирует его сессии. Agentomatic и memomatic устанавливаются независимо.
73
81
 
74
82
  Без явных директив, описанных в `MEMORY_RULES.md`, ничего не удаляется.
@@ -1,8 +1,8 @@
1
1
  [Unit]
2
- Description=Run the Memomatic dream sweep nightly
2
+ Description=Run the Memomatic dream sweep nightly after session extraction
3
3
 
4
4
  [Timer]
5
- OnCalendar=*-*-* 03:30:00
5
+ OnCalendar=*-*-* 04:30:00
6
6
  RandomizedDelaySec=30m
7
7
  Persistent=true
8
8
 
@@ -0,0 +1,9 @@
1
+ [Unit]
2
+ Description=Memomatic sessions: extract episodic memory from OpenCode sessions
3
+ Documentation=https://github.com/kisev/skills#memomatic
4
+ After=network-online.target
5
+
6
+ [Service]
7
+ Type=oneshot
8
+ ExecStart=/bin/sh -lc 'exec memomatic sessions'
9
+ Environment=MEMOMATIC_SESSIONS=1
@@ -0,0 +1,10 @@
1
+ [Unit]
2
+ Description=Run the Memomatic session extraction early nightly
3
+
4
+ [Timer]
5
+ OnCalendar=*-*-* 02:00:00
6
+ RandomizedDelaySec=30m
7
+ Persistent=true
8
+
9
+ [Install]
10
+ WantedBy=timers.target
package/dist/cli.js CHANGED
@@ -1,13 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  import { readFileSync } from "node:fs";
3
+ import { readdir, stat } from "node:fs/promises";
3
4
  import { isAbsolute } from "node:path";
4
5
  import { openMemomatic, rebuildIndex, searchMemory } from "./service.js";
5
6
  import { runDream } from "./dream.js";
7
+ import { runSessions } from "./sessions.js";
6
8
  import { processInbox, withRunLock } from "./inbox.js";
7
9
  import { OpenCodeExecutor } from "./executor.js";
8
10
  import { normalizeSettings } from "./settings.js";
9
11
  import { planIngestion } from "./ingestion.js";
10
12
  import { opencodeDatabasePath } from "./ingest.js";
13
+ import { promotionCandidates, relevanceByFts } from "./gates.js";
11
14
  import { memomaticPaths } from "./paths.js";
12
15
  import { program, common, Option, configFile, applyConfig, reporter, cancellation, duration, integer, fail, } from "./generated/cli.js";
13
16
  const version = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
@@ -49,26 +52,35 @@ const run = async (command, action) => {
49
52
  context = await openMemomatic({
50
53
  readOnly: opts.dryRun || opts.plan || command.name() === "status",
51
54
  configure: (settings) => {
55
+ const sessions = { ...settings.sessions };
52
56
  const dream = { ...settings.dream };
53
57
  for (const [flag, key] of [
54
58
  ["model", "model"],
55
59
  ["variant", "variant"],
56
60
  ["timeout", "timeoutMs"],
57
61
  ["maxDuration", "maxDurationMs"],
62
+ ["retries", "retries"],
63
+ ]) {
64
+ if (opts[flag] !== undefined) {
65
+ Object.assign(sessions, { [key]: opts[flag] });
66
+ Object.assign(dream, { [key]: opts[flag] });
67
+ }
68
+ }
69
+ for (const [flag, key] of [
58
70
  ["maxSessions", "maxSessions"],
59
71
  ["chunkChars", "maxChars"],
60
- ["retries", "retries"],
61
72
  ["idle", "idleMs"],
62
73
  ["opencodeUrl", "opencodeUrl"],
63
74
  ])
64
75
  if (opts[flag] !== undefined)
65
- Object.assign(dream, { [key]: opts[flag] });
66
- return normalizeSettings({ ...settings, dream });
76
+ Object.assign(sessions, { [key]: opts[flag] });
77
+ return normalizeSettings({ ...settings, sessions, dream });
67
78
  },
68
79
  });
69
- const signal = context.settings.dream.maxDurationMs && !opts.maxDuration
70
- ? AbortSignal.any([limited, AbortSignal.timeout(context.settings.dream.maxDurationMs)])
80
+ const deadline = (section) => context.settings[section].maxDurationMs && !opts.maxDuration
81
+ ? AbortSignal.any([limited, AbortSignal.timeout(context.settings[section].maxDurationMs)])
71
82
  : limited;
83
+ const signal = command.name() === "sessions" ? deadline("sessions") : deadline("dream");
72
84
  await action(context, opts, { signal, observe: log.emit });
73
85
  }
74
86
  finally {
@@ -78,39 +90,61 @@ const run = async (command, action) => {
78
90
  }
79
91
  };
80
92
  const output = (value, opts, summary) => console.log(opts.json || !process.stdout.isTTY ? JSON.stringify(value, null, 2) : summary);
81
- const dream = add("dream", "Drain the session snapshot, checkpointing completed fragments")
82
- .option("--plan", "count pending work without model calls or writes")
83
- .option("--dry-run", "preview without persistent writes (model calls still occur)")
84
- .addOption(new Option("--model <provider/model>", "OpenCode model; defaults to settings.dream.model").env("MEMOMATIC_MODEL"))
93
+ const modelOptions = (command) => command
94
+ .addOption(new Option("--model <provider/model>", "OpenCode model; defaults to settings").env("MEMOMATIC_MODEL"))
85
95
  .addOption(new Option("--variant <name>", "provider reasoning variant").env("MEMOMATIC_VARIANT"))
86
96
  .addOption(new Option("--timeout <duration>", "per-model-call timeout (default 3m)")
87
97
  .argParser(duration)
88
98
  .env("MEMOMATIC_TIMEOUT"))
89
- .addOption(new Option("--max-duration <duration>", "whole-run deadline; 0 means drain the snapshot")
99
+ .addOption(new Option("--max-duration <duration>", "whole-run deadline; 0 means unlimited")
90
100
  .argParser(duration)
91
101
  .env("MEMOMATIC_MAX_DURATION"))
102
+ .addOption(new Option("--retries <count>", "additional model attempts (default 1)")
103
+ .argParser(integer)
104
+ .env("MEMOMATIC_RETRIES"));
105
+ const executor = (context, runtime) => {
106
+ const sections = {
107
+ sessions: context.settings.sessions,
108
+ dream: context.settings.dream,
109
+ };
110
+ const create = (section) => {
111
+ const config = sections[section];
112
+ return config.model
113
+ ? new OpenCodeExecutor({
114
+ model: config.model,
115
+ variant: config.variant,
116
+ url: "opencodeUrl" in config ? config.opencodeUrl : null,
117
+ timeoutMs: config.timeoutMs,
118
+ retries: config.retries,
119
+ ...runtime,
120
+ })
121
+ : null;
122
+ };
123
+ return create;
124
+ };
125
+ const sessions = add("sessions", "Drain the OpenCode session snapshot into episodic memory, checkpointing fragments")
126
+ .option("--plan", "count pending work without model calls or writes")
127
+ .option("--dry-run", "preview without persistent writes (model calls still occur)");
128
+ modelOptions(sessions)
92
129
  .addOption(new Option("--max-sessions <count>", "optional session limit; 0 means all")
93
130
  .argParser(integer)
94
131
  .env("MEMOMATIC_MAX_SESSIONS"))
95
132
  .addOption(new Option("--chunk-chars <count>", "maximum new text per fragment (default 24000)")
96
133
  .argParser(integer)
97
134
  .env("MEMOMATIC_CHUNK_CHARS"))
98
- .addOption(new Option("--retries <count>", "additional model attempts (default 1)")
99
- .argParser(integer)
100
- .env("MEMOMATIC_RETRIES"))
101
135
  .addOption(new Option("--idle <duration>", "minimum session idle age (default 10m)")
102
136
  .argParser(duration)
103
137
  .env("MEMOMATIC_IDLE"))
104
138
  .addOption(new Option("--opencode-url <url>", "reuse an existing OpenCode server; otherwise start one per run").env("MEMOMATIC_OPENCODE_URL"))
105
139
  .addOption(new Option("--database <path>", "OpenCode SQLite session database").env("MEMOMATIC_DATABASE"))
106
- .addHelpText("after", "\nExamples:\n memomatic dream\n memomatic dream --max-duration 15m --log-format json\n memomatic dream --dry-run --max-sessions 2\n\nCompleted fragments survive cancellation. Logs use stderr; --json keeps stdout machine-readable.");
107
- dream.action(async () => run(dream, async (context, opts, runtime) => {
140
+ .addHelpText("after", "\nExamples:\n memomatic sessions\n memomatic sessions --max-duration 15m --log-format json\n memomatic sessions --dry-run --max-sessions 2\n\nCompleted fragments survive cancellation. Logs use stderr; --json keeps stdout machine-readable.");
141
+ sessions.action(async () => run(sessions, async (context, opts, runtime) => {
108
142
  if (opts.plan) {
109
143
  const start = Date.now();
110
144
  const plan = planIngestion(opts.database ?? opencodeDatabasePath(), context.store, {
111
- before: Date.now() - context.settings.dream.idleMs,
112
- maxChars: context.settings.dream.maxChars,
113
- maxSessions: context.settings.dream.maxSessions,
145
+ before: Date.now() - context.settings.sessions.idleMs,
146
+ maxChars: context.settings.sessions.maxChars,
147
+ maxSessions: context.settings.sessions.maxSessions,
114
148
  });
115
149
  const summary = {
116
150
  sessions: plan.sessions,
@@ -125,27 +159,43 @@ dream.action(async () => run(dream, async (context, opts, runtime) => {
125
159
  output(summary, opts, `${summary.sessions} sessions, ${summary.fragments} fragments, ${summary.characters} characters (${summary.scanMs}ms scan). No model calls.`);
126
160
  return;
127
161
  }
128
- const executor = context.settings.dream.model
129
- ? new OpenCodeExecutor({
130
- model: context.settings.dream.model,
131
- variant: context.settings.dream.variant,
132
- url: context.settings.dream.opencodeUrl,
133
- timeoutMs: context.settings.dream.timeoutMs,
134
- retries: context.settings.dream.retries,
135
- ...runtime,
136
- })
137
- : null;
162
+ const create = executor(context, runtime);
163
+ const model = create("sessions");
138
164
  try {
139
- const execute = () => runDream(context, executor, {
165
+ const execute = () => runSessions(context, model, {
140
166
  ...runtime,
141
167
  dryRun: opts.dryRun,
142
168
  databaseFile: opts.database,
143
169
  });
144
170
  const result = opts.dryRun ? await execute() : await withRunLock(context.paths, execute);
145
- output({ ...result, modelUsage: executor?.usage ?? null }, opts, `Dream complete: ${result.sessionsIngested} sessions, ${result.candidatesExtracted} candidates, ${result.elapsedMs}ms`);
171
+ output({ ...result, modelUsage: model?.usage ?? null }, opts, `Sessions complete: ${result.sessionsIngested} sessions, ${result.candidatesExtracted} candidates, ${result.elapsedMs}ms`);
146
172
  }
147
173
  finally {
148
- await executor?.close();
174
+ await model?.close();
175
+ }
176
+ }));
177
+ const dream = add("dream", "Consolidate memory: inbox, promotion, bounded rewrite, archive")
178
+ .option("--plan", "count pending work without model calls or writes")
179
+ .option("--dry-run", "preview without persistent writes (model calls still occur)");
180
+ modelOptions(dream).addHelpText("after", "\nExamples:\n memomatic dream\n memomatic dream --max-duration 15m --log-format json\n memomatic dream --dry-run\n\nSession extraction lives in `memomatic sessions`. Logs use stderr; --json keeps stdout machine-readable.");
181
+ dream.action(async () => run(dream, async (context, opts, runtime) => {
182
+ if (opts.plan) {
183
+ const relevance = relevanceByFts(context.store, context.settings);
184
+ const candidates = promotionCandidates(context.store, context.settings, relevance);
185
+ const inboxFiles = (await readdir(context.paths.inboxDir).catch(() => [])).filter((name) => name.endsWith(".md")).length;
186
+ const summary = { inboxFiles, promotionCandidates: candidates.length };
187
+ output(summary, opts, `${summary.inboxFiles} inbox files, ${summary.promotionCandidates} promotion candidates. No model calls.`);
188
+ return;
189
+ }
190
+ const create = executor(context, runtime);
191
+ const model = create("dream");
192
+ try {
193
+ const execute = () => runDream(context, model, { ...runtime, dryRun: opts.dryRun });
194
+ const result = opts.dryRun ? await execute() : await withRunLock(context.paths, execute);
195
+ output({ ...result, modelUsage: model?.usage ?? null }, opts, `Dream complete: ${result.promoted.length} promoted, ${result.superseded.length} superseded, ${result.elapsedMs}ms`);
196
+ }
197
+ finally {
198
+ await model?.close();
149
199
  }
150
200
  }));
151
201
  const processCommand = add("process", "Process explicit inbox entries without LLM extraction").option("--dry-run", "preview without writes");
@@ -162,12 +212,54 @@ index.action(() => run(index, async (context, opts, runtime) => {
162
212
  else
163
213
  console.log(`${entries} entries indexed`);
164
214
  }));
165
- const status = add("status", "Read corpus/index status without indexing or model calls");
166
- status.action(() => run(status, async (context, opts) => output({
167
- entries: context.store.allEntries().length,
168
- fts5: context.store.hasFts(),
169
- stateRoot: context.paths.stateRoot,
170
- }, opts, `${context.store.allEntries().length} indexed entries in ${context.paths.stateRoot}`)));
215
+ const status = add("status", "Read corpus/index/queue status without indexing or model calls");
216
+ status.action(() => run(status, async (context, opts, runtime) => {
217
+ const inboxFiles = (await readdir(context.paths.inboxDir).catch(() => [])).filter((name) => name.endsWith(".md")).length;
218
+ let sessionBacklog = null;
219
+ const databaseFile = opencodeDatabasePath();
220
+ if (await stat(databaseFile).catch(() => null)) {
221
+ try {
222
+ const plan = planIngestion(databaseFile, context.store, {
223
+ before: Date.now() - context.settings.sessions.idleMs,
224
+ maxChars: context.settings.sessions.maxChars,
225
+ maxSessions: context.settings.sessions.maxSessions,
226
+ });
227
+ sessionBacklog = {
228
+ sessions: plan.sessions,
229
+ messages: plan.messages,
230
+ fragments: plan.fragments.length,
231
+ characters: plan.characters,
232
+ cachedFragments: plan.cached,
233
+ cachedSessions: plan.cachedSessions,
234
+ unscannedSessions: plan.unscannedSessions,
235
+ };
236
+ }
237
+ catch (error) {
238
+ runtime.observe({
239
+ phase: "status.sessions",
240
+ message: `Cannot read OpenCode sessions: ${error.message}`,
241
+ level: "warn",
242
+ });
243
+ }
244
+ }
245
+ const relevance = relevanceByFts(context.store, context.settings);
246
+ const candidates = promotionCandidates(context.store, context.settings, relevance);
247
+ const report = {
248
+ entries: context.store.allEntries().length,
249
+ fts5: context.store.hasFts(),
250
+ stateRoot: context.paths.stateRoot,
251
+ queue: {
252
+ inboxFiles,
253
+ sessions: sessionBacklog,
254
+ promotionCandidates: candidates.length,
255
+ },
256
+ lastRun: {
257
+ dream: context.store.getMeta("last-dream-at"),
258
+ sessions: context.store.getMeta("last-sessions-at"),
259
+ },
260
+ };
261
+ output(report, opts, `${report.entries} indexed entries, ${inboxFiles} inbox files, ${sessionBacklog ? `${sessionBacklog.fragments} pending fragments` : "no session database"}, ${candidates.length} promotion candidates in ${context.paths.stateRoot}`);
262
+ }));
171
263
  const search = add("search <query...>", "Find relevant memory or return no matches")
172
264
  .option("--explain", "include relevance diagnostics")
173
265
  .addOption(new Option("--project <name>", "restrict to a project and user-level memory").env("MEMOMATIC_PROJECT"));
package/dist/dream.d.ts CHANGED
@@ -2,42 +2,20 @@ import { type InboxReport } from "./inbox.js";
2
2
  import { type ModelExecutor } from "./executor.js";
3
3
  import { type OperationOptions } from "./operations.js";
4
4
  import type { MemomaticContext } from "./service.js";
5
+ export declare const LAST_RUN_KEY = "last-dream-at";
5
6
  export type DreamReport = {
6
7
  inbox: InboxReport;
7
- sessionsIngested: number;
8
- candidatesExtracted: number;
9
8
  promoted: string[];
10
9
  superseded: string[];
11
10
  dropped: string[];
12
- rejected: Array<{
13
- text: string;
14
- reason: string;
15
- }>;
16
11
  archived: string[];
17
12
  appendOnlyFallback: boolean;
18
13
  dryRun: boolean;
19
- ingestion?: {
20
- messages: number;
21
- fragments: number;
22
- completed: number;
23
- cached: number;
24
- characters: number;
25
- remaining: number;
26
- cachedSessions: number;
27
- unscannedSessions: number;
28
- };
29
14
  elapsedMs?: number;
30
15
  };
31
16
  export type DreamOptions = OperationOptions & {
32
17
  dryRun?: boolean;
33
- databaseFile?: string;
34
18
  };
35
- export type ExtractedCandidate = {
36
- text: string;
37
- key: string | null;
38
- reason: string;
39
- };
40
- export declare function parseExtraction(response: string): ExtractedCandidate[];
41
19
  export type ConsolidationOperation = {
42
20
  op: "add";
43
21
  line: string;