agenr 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.4.0] - 2026-03-30
4
+
5
+ Configurable summary models, surgeon personal knowledge protection, and documentation overhaul.
6
+
7
+ ### Added
8
+
9
+ - **Configurable continuity and episode summary models.** New `continuityModel` and `episodeModel` fields in the OpenClaw plugin config (`plugins.entries.agenr.config`) allow overriding the model used for continuity and episode summary generation independently. Falls back to the agent's primary model when unset. Use a fast model like `openai/gpt-5.4-mini` for these structured extraction tasks instead of burning Opus tokens.
10
+ - **Personal knowledge protection in surgeon.** The surgeon retirement pass now has explicit guidance that personal facts (family, pets, hardware, contacts, identity, physical environment) are durable by nature. Only retires personal entries when contradicted or clearly duplicated — not for low recall or moderate importance.
11
+ - **Corpus age awareness in surgeon.** The `_meta` table now tracks `last_bulk_ingest_at`, surfaced via `get_health_stats`. The surgeon heavily discounts `recall_count = 0` when the corpus was rebuilt within 30 days, preventing mass retirements of freshly ingested entries.
12
+ - **New documentation: `docs/EPISODES.md`.** Comprehensive episodic memory docs covering lifecycle, CLI usage, recall modes, temporal window parser, search modes, embeddings, session discovery, and architecture.
13
+ - **New documentation: `docs/SURGEON.md`.** Comprehensive surgeon docs covering tools, CLI commands, dry-run vs apply, budget governance, configuration, protection thresholds, and audit history.
14
+
15
+ ### Changed
16
+
17
+ - **Continuity summary timeout increased.** Inner timeout bumped from 15s to 30s, read-time wrapper from 20s to 35s. Prevents timeout failures when using slower models for continuity summaries.
18
+ - **Updated `docs/RECALL.md`.** Added unified recall mode routing (`auto`/`entries`/`episodes`), auto-routing rules, temporal window parser reference, and episode search pipeline documentation.
19
+ - **Updated `docs/INGEST.md`.** Added episode ingest section with full flag documentation, behavior differences from entry ingest, session discovery, surface reconstruction, and practical examples.
20
+ - **Updated `README.md`.** Added episodic memory and surgeon to features list, CLI commands table, and new "How Episodes Work" and "How the Surgeon Works" sections with doc links.
21
+
3
22
  ## [1.3.0] - 2026-03-30
4
23
 
5
24
  Episodic memory — session-level temporal recall for the brain.
package/README.md CHANGED
@@ -14,18 +14,20 @@ Local-first, durable memory infrastructure for AI agents.
14
14
 
15
15
  ## What is agenr?
16
16
 
17
- agenr gives agents a persistent brain: a local SQLite database of durable knowledge that survives across sessions, tools, and agent restarts. Instead of relying on fragile prompt state or file-based scratch memory, agents can ingest transcripts, extract decisions and lessons, store them as typed entries, and recall them later with semantic search and memory-aware ranking.
17
+ agenr gives agents a persistent brain: a local SQLite database of durable knowledge that survives across sessions, tools, and agent restarts. Instead of relying on fragile prompt state or file-based scratch memory, agents can ingest transcripts, extract decisions and lessons, store them as typed entries, generate episodic summaries of what happened, and recall them later with semantic search and memory-aware ranking.
18
18
 
19
- It exists because most agent runtimes forget everything important between sessions. Even when a tool has a built-in memory feature, it is often lossy, file-based, or tightly coupled to one surface. agenr keeps memory structured and queryable: facts, decisions, preferences, lessons, todos, events, and relationships live in one local store instead of getting flattened into prompt text.
19
+ It exists because most agent runtimes forget everything important between sessions. Even when a tool has a built-in memory feature, it is often lossy, file-based, or tightly coupled to one surface. agenr keeps memory structured and queryable: facts, decisions, preferences, lessons, tasks, milestones, relationships, and session-level episodes live in one local store instead of getting flattened into prompt text.
20
20
 
21
- What makes agenr different is the combination of local-first storage, semantic embeddings, hybrid recall, and adapter-friendly architecture. The core is hexagonal, so multiple agent systems can share the same brain over time. Today the production adapter is the OpenClaw memory plugin, published separately as `@agenr/openclaw-plugin`, and the CLI provides offline ingest and recall against that same database.
21
+ What makes agenr different is the combination of local-first storage, semantic embeddings, hybrid recall, episodic temporal memory, and adapter-friendly architecture. The core is hexagonal, so multiple agent systems can share the same brain over time. Today the production adapter is the OpenClaw memory plugin, published separately as `@agenr/openclaw-plugin`, and the CLI provides offline ingest, recall, and maintenance against that same database.
22
22
 
23
23
  ## Features
24
24
 
25
- - Hybrid recall: vector similarity, lexical FTS, temporal awareness, recency decay, and importance weighting.
25
+ - Hybrid recall for durable knowledge: vector similarity, lexical FTS, temporal awareness, recency decay, and importance weighting.
26
+ - Episodic memory: session-level summaries with temporal filtering and optional semantic episode search for questions like "what happened yesterday?"
26
27
  - LLM-powered knowledge extraction from conversation transcripts.
27
28
  - Semantic deduplication using exact hashes, normalized hashes, embeddings, and within-run clustering.
28
- - Session continuity with predecessor resolution, recent transcript tails, and LLM-generated session summaries.
29
+ - Session continuity with predecessor resolution, recent transcript tails, and LLM-generated continuity summaries.
30
+ - Surgeon retirement pass for corpus maintenance: inspect stale candidates, simulate recall impact, and retire semantically obsolete knowledge with audit history.
29
31
  - Agent tools for `store`, `recall`, `retire`, `update`, and `trace` through the OpenClaw plugin.
30
32
  - Native OpenClaw memory plugin that replaces OpenClaw's built-in memory slot.
31
33
  - Local-first storage with SQLite/libSQL. Memory stays on your machine; only model and embedding calls leave it.
@@ -46,7 +48,7 @@ It walks through:
46
48
  - model selection filtered by the auth method you chose
47
49
  - OpenAI embedding key setup
48
50
  - OpenClaw detection and optional plugin installation
49
- - session scanning and optional bulk ingestion of existing transcripts
51
+ - session scanning and optional bulk ingestion of existing transcripts into durable entries and episodic summaries
50
52
 
51
53
  Run `agenr init` again any time you want to re-run onboarding, reinstall the plugin, or ingest another batch of existing sessions.
52
54
 
@@ -109,10 +111,10 @@ Key config fields:
109
111
 
110
112
  | Field | What it does |
111
113
  | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
112
- | `auth` | Authentication method: `openai-api-key`, `openai-subscription`, `anthropic-api-key`, `anthropic-oauth`, or `anthropic-token`. |
114
+ | `auth` | Authentication method: `openai-api-key`, `openai-subscription`, `anthropic-api-key`, `anthropic-oauth`, or `anthropic-token`. |
113
115
  | `provider` / `model` | Default LLM provider and model used for extraction tasks unless overridden. |
114
116
  | `credentials` | Stored manual credentials. Today that can include `openaiApiKey`, `anthropicApiKey`, and `anthropicOauthToken`. The config file is written with locked-down permissions (`0600`). |
115
- | `credentials.openaiApiKey` | OpenAI key used for embeddings, and also for extraction when `auth` is `openai-api-key`. Older configs may still rely on legacy `embeddingApiKey` or `apiKey` fallback fields. |
117
+ | `credentials.openaiApiKey` | OpenAI key used for embeddings, and also for extraction when `auth` is `openai-api-key`. Older configs may still rely on legacy `embeddingApiKey` or `apiKey` fallback fields. |
116
118
  | `embeddingModel` | Embedding model. Defaults to `text-embedding-3-small`. |
117
119
  | `extractionModel` / `dedupModel` | Optional per-pipeline overrides so extraction and dedup can use different provider/model pairs. |
118
120
  | `extractionContext` | Optional user context injected into extraction prompts to help the model decide what is worth remembering. |
@@ -128,7 +130,7 @@ Important: when agenr is running as an OpenClaw plugin, session summaries use Op
128
130
 
129
131
  ## CLI Commands
130
132
 
131
- The current CLI surface is intentionally small. Today the `db` group only exposes `reset`.
133
+ The CLI surface is still intentionally compact, but it now covers setup, recall, ingest, and corpus maintenance.
132
134
 
133
135
  | Command | What it does |
134
136
  | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------ |
@@ -138,6 +140,10 @@ The current CLI surface is intentionally small. Today the `db` group only expose
138
140
  | `agenr ingest <path>` | Default durable-entry ingest shorthand. Equivalent to `agenr ingest entries <path>`. |
139
141
  | `agenr ingest entries <path>` | Bulk-ingest one file or directory of OpenClaw transcript files into durable knowledge entries. |
140
142
  | `agenr ingest episodes <path>` | Backfill episodic summaries from OpenClaw session transcripts, including rotated `.reset.*` and `.deleted.*` files. |
143
+ | `agenr surgeon run` | Execute the surgeon retirement pass. Dry-run by default; add `--apply` to mutate the corpus. |
144
+ | `agenr surgeon status` | Show corpus health plus the latest surgeon run summary. |
145
+ | `agenr surgeon history` | Show recent surgeon runs. |
146
+ | `agenr surgeon actions <run>` | Show the audit trail for one surgeon run. |
141
147
  | `agenr db reset` | Delete and recreate the knowledge database. |
142
148
 
143
149
  The OpenClaw plugin also gives the agent five tools directly inside the runtime: `agenr_store`, `agenr_recall`, `agenr_retire`, `agenr_update`, and `agenr_trace`.
@@ -148,12 +154,15 @@ Examples:
148
154
  # Recall knowledge
149
155
  agenr recall "what decisions did we make about the API?"
150
156
 
151
- # Ingest transcripts
157
+ # Ingest transcripts into durable entries
152
158
  agenr ingest ~/.openclaw/agents/main/sessions/
153
159
 
154
- # Backfill episode summaries
160
+ # Backfill episodic summaries
155
161
  agenr ingest episodes ~/.openclaw/agents/main/sessions/ --recent 30d
156
162
 
163
+ # Run the surgeon retirement pass (dry-run by default)
164
+ agenr surgeon run --budget 2.00
165
+
157
166
  # Reset the database
158
167
  agenr db reset
159
168
  ```
@@ -172,7 +181,20 @@ Recall is a hybrid pipeline. Agenr embeds the query, retrieves candidates throug
172
181
 
173
182
  ## How Ingestion Works
174
183
 
175
- Ingestion is transcript-to-memory: parse OpenClaw JSONL transcripts, normalize them, choose whole-file or chunked extraction, run LLM extraction, validate the structured entries, deduplicate them within the ingest run, generate embeddings, and then persist surviving entries through the store pipeline. The current CLI ingest path is OpenClaw-transcript-specific. Details: [docs/INGEST.md](./docs/INGEST.md) and [docs/STORE.md](./docs/STORE.md).
184
+ Agenr has two ingest pipelines over the same transcript corpus:
185
+
186
+ - `agenr ingest entries <path>` extracts durable typed knowledge such as facts, decisions, preferences, lessons, milestones, and relationships.
187
+ - `agenr ingest episodes <path>` generates one narrative summary per session so the brain can answer temporal questions like "what happened last week?"
188
+
189
+ Both paths parse OpenClaw transcripts first, but they optimize for different outputs: entry ingest distills durable knowledge and runs semantic dedup across the whole ingest batch, while episode ingest does a session-by-session preflight pass, uses `sessions.json` metadata when available, reconstructs missing surface metadata for rotated files, and writes episodic summaries. Details: [docs/INGEST.md](./docs/INGEST.md) and [docs/STORE.md](./docs/STORE.md).
190
+
191
+ ## How Episodes Work
192
+
193
+ Episodes are session-level memory artifacts stored separately from durable entries. They preserve temporal narrative: what happened in a session, when it happened, which agent/session it belonged to, and optionally an embedding for semantic episode search. Recall can route narrative or time-bounded questions toward episodes automatically, or combine episode and entry results in mixed mode. For implementation details and the episode recall model, see [docs/EPISODES.md](./docs/EPISODES.md).
194
+
195
+ ## How the Surgeon Works
196
+
197
+ The surgeon is a maintenance pass for the durable-memory corpus. It evaluates retirement candidates, inspects related context, can simulate recall impact before mutation, and records runs plus per-action audit history in the database. `agenr surgeon run` is safe by default because it starts in dry-run mode; `--apply` is the explicit mutation switch. For runtime details, governance, and audit behavior, see [docs/SURGEON.md](./docs/SURGEON.md).
176
198
 
177
199
  ## Development
178
200
 
@@ -188,10 +210,10 @@ pnpm check # format + lint + typecheck + test
188
210
  | Problem | What to check |
189
211
  | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
190
212
  | `agenr init` cannot complete setup | Re-run `agenr setup` and verify the selected auth method is actually available. Subscription flows depend on the relevant external login being present, and non-OpenAI extraction setups still need a separate OpenAI embedding key. |
191
- | `openclaw plugins install @agenr/openclaw-plugin` fails | Make sure the `openclaw` CLI is installed and on `PATH`. For local development, run `pnpm build` and use `plugins.load.paths` instead. |
192
- | `agenr recall` or `agenr_recall` fails with embedding/auth errors | Embeddings always use OpenAI. Confirm `credentials.openaiApiKey` is configured, or re-run `agenr setup` to set the embedding key explicitly. |
193
- | SQLite says the database is locked | Avoid running multiple writers against the same DB at once. Stop overlapping ingest/reset runs, restart the OpenClaw gateway if needed, then retry. |
194
- | OpenClaw does not pick up the plugin | Restart the gateway, confirm `plugins.slots.memory` is `agenr`, confirm `plugins.allow` contains `agenr`, and for dev installs confirm `plugins.load.paths` points at the built `packages/openclaw-plugin` directory. |
213
+ | `openclaw plugins install @agenr/openclaw-plugin` fails | Make sure the `openclaw` CLI is installed and on `PATH`. For local development, run `pnpm build` and use `plugins.load.paths` instead. |
214
+ | `agenr recall` or `agenr_recall` fails with embedding/auth errors | Embeddings always use OpenAI. Confirm `credentials.openaiApiKey` is configured, or re-run `agenr setup` to set the embedding key explicitly. |
215
+ | SQLite says the database is locked | Avoid running multiple writers against the same DB at once. Stop overlapping ingest/reset runs, restart the OpenClaw gateway if needed, then retry. |
216
+ | OpenClaw does not pick up the plugin | Restart the gateway, confirm `plugins.slots.memory` is `agenr`, confirm `plugins.allow` contains `agenr`, and for dev installs confirm `plugins.load.paths` points at the built `packages/openclaw-plugin` directory. |
195
217
 
196
218
  ## License
197
219
 
@@ -947,6 +947,7 @@ var SCHEMA_VERSION = "4";
947
947
  var VECTOR_INDEX_NAME = "idx_entries_embedding";
948
948
  var EPISODE_VECTOR_INDEX_NAME = "idx_episodes_embedding";
949
949
  var BULK_WRITE_STATE_META_KEY = "bulk_write_state";
950
+ var LAST_BULK_INGEST_META_KEY = "last_bulk_ingest_at";
950
951
  var CREATE_ENTRIES_TABLE_SQL = `
951
952
  CREATE TABLE IF NOT EXISTS entries (
952
953
  id TEXT PRIMARY KEY,
@@ -1386,8 +1387,28 @@ async function finalizeBulkWrites(db) {
1386
1387
  sql: "DELETE FROM _meta WHERE key = ?",
1387
1388
  args: [BULK_WRITE_STATE_META_KEY]
1388
1389
  });
1390
+ await db.execute({
1391
+ sql: `
1392
+ INSERT INTO _meta (key, value)
1393
+ VALUES (?, ?)
1394
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value
1395
+ `,
1396
+ args: [LAST_BULK_INGEST_META_KEY, (/* @__PURE__ */ new Date()).toISOString()]
1397
+ });
1389
1398
  });
1390
1399
  }
1400
+ async function getLastBulkIngestAt(db) {
1401
+ try {
1402
+ const result = await db.execute({
1403
+ sql: "SELECT value FROM _meta WHERE key = ? LIMIT 1",
1404
+ args: [LAST_BULK_INGEST_META_KEY]
1405
+ });
1406
+ const row = result.rows[0];
1407
+ return row?.value ? String(row.value) : null;
1408
+ } catch {
1409
+ return null;
1410
+ }
1411
+ }
1391
1412
  async function getSchemaVersion(db) {
1392
1413
  try {
1393
1414
  const result = await db.execute("SELECT value FROM _meta WHERE key = 'schema_version' LIMIT 1");
@@ -2227,6 +2248,7 @@ export {
2227
2248
  getEntry,
2228
2249
  retireEntry,
2229
2250
  updateEntry,
2251
+ getLastBulkIngestAt,
2230
2252
  createDatabase,
2231
2253
  DEFAULT_SURGEON_COST_CAP,
2232
2254
  DEFAULT_SURGEON_DAILY_COST_CAP,
package/dist/cli.js CHANGED
@@ -20,6 +20,7 @@ import {
20
20
  deserializeTags,
21
21
  getAuthMethodDefinition,
22
22
  getEntry,
23
+ getLastBulkIngestAt,
23
24
  isAgenrAuthMethod,
24
25
  mapEntryRow,
25
26
  readBoolean,
@@ -34,7 +35,7 @@ import {
34
35
  retireEntry,
35
36
  updateEntry,
36
37
  writeConfig
37
- } from "./chunk-2FKQCRGD.js";
38
+ } from "./chunk-DSP74MEN.js";
38
39
  import {
39
40
  parseRelativeDate,
40
41
  recall
@@ -7659,6 +7660,12 @@ function getSurgeonSystemPrompt() {
7659
7660
  "",
7660
7661
  "Use `source_file` and `source_context` as provenance clues. A transcript path, session file, or narrow snippet can explain why something looks like a temporary handoff or progress artifact. Provenance is evidence, not a retirement reason by itself.",
7661
7662
  "",
7663
+ "## Corpus Age Awareness",
7664
+ "",
7665
+ "`get_health_stats` returns `lastBulkIngestAt` - the timestamp of the most recent bulk ingest (corpus rebuild). When the corpus has been recently rebuilt (within the last 30 days), treat `recall_count = 0` as carrying almost no signal. Every entry in a freshly rebuilt corpus starts at zero recalls regardless of its actual importance. In this window, weight content value, type durability, and subject uniqueness much more heavily than recall history.",
7666
+ "",
7667
+ "Even outside the rebuild window, never use `recall_count = 0` as the sole or primary retirement reason. Zero recall is a weak staleness signal that must be combined with other evidence: content obsolescence, clear supersession, or expired temporal relevance.",
7668
+ "",
7662
7669
  "## Budget Awareness",
7663
7670
  "",
7664
7671
  "Use your budget carefully, but do not stop early just because a batch looks healthy. Keep paginating while candidates remain and your budget allows it. `complete_pass` will reject shallow completion when too much of the candidate pool remains unexplored.",
@@ -7718,6 +7725,20 @@ function getSurgeonRetirementPassPrompt() {
7718
7725
  "- `lesson` - Rarely retire unless the lesson is tied to a tool, pattern, or situation that no longer applies.",
7719
7726
  "- `relationship` - Retire when the relationship is clearly outdated or no longer real.",
7720
7727
  "",
7728
+ "## Personal Knowledge",
7729
+ "",
7730
+ "Personal facts about the user - family members, pets, relationships, hardware, home setup, contact information, vehicles, physical environment, and identity details - are durable by nature even when importance is moderate. These entries represent the user's life context, not transient project state. They have lasting retrieval value because future sessions need this context to be helpful and personable.",
7731
+ "",
7732
+ "Retire personal knowledge entries only when:",
7733
+ "- Explicitly contradicted by a newer entry (for example, a pet has passed away or a family member has moved)",
7734
+ "- The entry is a clear duplicate of another active entry with identical or better coverage",
7735
+ "",
7736
+ "Do NOT retire personal knowledge entries because:",
7737
+ "- They have low or zero recall count",
7738
+ "- They have moderate importance (5-7)",
7739
+ "- They lack project tags",
7740
+ "- They seem like trivia - if it's about the user's life, it is not trivia to them",
7741
+ "",
7721
7742
  "## Common Retirement Patterns",
7722
7743
  "",
7723
7744
  "- Old unrecalled temporaries",
@@ -7726,6 +7747,12 @@ function getSurgeonRetirementPassPrompt() {
7726
7747
  "- Resolved problems and obsolete workarounds",
7727
7748
  "- Older entries that are clearly covered better by newer entries on the same subject or cluster",
7728
7749
  "",
7750
+ "## What Looks Like a Pattern But Is Not",
7751
+ "",
7752
+ "- Personal facts with zero recall - these are the user's life, not expired project context",
7753
+ "- Hardware, infrastructure, and environment details - these have ongoing operational value",
7754
+ "- Contact information and identity details - the user expects the system to know these",
7755
+ "",
7729
7756
  "Use `source_file` and `source_context` to judge whether an entry came from a narrow session artifact, a handoff, a progress note, or a durable source. Provenance can explain why a memory existed and whether it was meant to last.",
7730
7757
  "",
7731
7758
  "## Budget Awareness",
@@ -8035,18 +8062,20 @@ function createHealthStatsTool(deps) {
8035
8062
  description: "Inspect current corpus health and the latest surgeon run summary.",
8036
8063
  parameters: GET_HEALTH_STATS_SCHEMA,
8037
8064
  async execute() {
8038
- const [health, lastRun] = await Promise.all([
8065
+ const [health, lastRun, lastBulkIngestAt] = await Promise.all([
8039
8066
  getSurgeonHealthStats(deps.executor, {
8040
8067
  protectRecalledDays: deps.protection.protectRecalledDays,
8041
8068
  protectMinImportance: deps.protection.protectMinImportance,
8042
8069
  now: deps.now()
8043
8070
  }),
8044
- getLastSurgeonRun(deps.executor)
8071
+ getLastSurgeonRun(deps.executor),
8072
+ getLastBulkIngestAt(deps.db)
8045
8073
  ]);
8046
8074
  return toolResult({
8047
8075
  now: deps.now().toISOString(),
8048
8076
  health,
8049
- lastRun
8077
+ lastRun,
8078
+ lastBulkIngestAt
8050
8079
  });
8051
8080
  }
8052
8081
  };
@@ -8641,6 +8670,7 @@ async function runSurgeon(options, deps) {
8641
8670
  budgetTracker
8642
8671
  });
8643
8672
  const tools = createSurgeonTools({
8673
+ db: deps.db,
8644
8674
  executor: deps.db,
8645
8675
  runId,
8646
8676
  project: options.project,
@@ -9,7 +9,7 @@ import {
9
9
  readConfig,
10
10
  resolveEmbeddingApiKey,
11
11
  resolveEmbeddingModel
12
- } from "./chunk-2FKQCRGD.js";
12
+ } from "./chunk-DSP74MEN.js";
13
13
  import {
14
14
  recall
15
15
  } from "./chunk-EUPZHNOY.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agenr",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Agent memory - local-first knowledge infrastructure for AI agents",
5
5
  "type": "module",
6
6
  "bin": {