pi-hermes-memory 0.6.5 → 0.6.7
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 +6 -0
- package/docs/0.6/CHANGELOG.md +25 -0
- package/docs/0.7/TAGGED-SESSION-SKILL-REVIEW.md +112 -0
- package/package.json +1 -1
- package/src/config.ts +8 -0
- package/src/constants.ts +2 -0
- package/src/store/db.ts +43 -1
- package/src/store/memory-store.ts +13 -6
- package/src/types.ts +6 -0
package/README.md
CHANGED
|
@@ -363,6 +363,9 @@ Create `~/.pi/agent/hermes-memory-config.json`:
|
|
|
363
363
|
"reviewEnabled": true,
|
|
364
364
|
"autoConsolidate": true,
|
|
365
365
|
"correctionDetection": true,
|
|
366
|
+
"failureInjectionEnabled": true,
|
|
367
|
+
"failureInjectionMaxAgeDays": 7,
|
|
368
|
+
"failureInjectionMaxEntries": 5,
|
|
366
369
|
"flushOnCompact": true,
|
|
367
370
|
"flushOnShutdown": true,
|
|
368
371
|
"flushMinTurns": 6
|
|
@@ -380,6 +383,9 @@ Create `~/.pi/agent/hermes-memory-config.json`:
|
|
|
380
383
|
| `reviewEnabled` | `true` | Enable/disable background learning loop |
|
|
381
384
|
| `autoConsolidate` | `true` | Auto-merge when memory hits capacity |
|
|
382
385
|
| `correctionDetection` | `true` | Detect user corrections and save immediately |
|
|
386
|
+
| `failureInjectionEnabled` | `true` | Enable/disable injecting recent failure memories into the system prompt |
|
|
387
|
+
| `failureInjectionMaxAgeDays` | `7` | Maximum age in days for injected failure memories |
|
|
388
|
+
| `failureInjectionMaxEntries` | `5` | Maximum number of failure memories to inject |
|
|
383
389
|
| `flushOnCompact` | `true` | Flush memories before Pi compacts context |
|
|
384
390
|
| `flushOnShutdown` | `true` | Flush memories when session ends |
|
|
385
391
|
| `flushMinTurns` | `6` | Minimum turns before flush triggers |
|
package/docs/0.6/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
# v0.6.6 Changelog
|
|
2
|
+
|
|
3
|
+
## Bug Fixes
|
|
4
|
+
|
|
5
|
+
### Fix legacy SQLite upgrade error: `no such column: category`
|
|
6
|
+
|
|
7
|
+
Some users upgrading from older versions had an existing `sessions.db` with a legacy
|
|
8
|
+
`memories` table that did not include v0.6 failure-memory columns. During schema init,
|
|
9
|
+
creating `idx_memories_category` failed and `/memory-index-sessions` crashed with:
|
|
10
|
+
|
|
11
|
+
`❌ Session indexing failed: no such column: category`
|
|
12
|
+
|
|
13
|
+
Fix:
|
|
14
|
+
|
|
15
|
+
- Added automatic legacy schema migration in `DatabaseManager`
|
|
16
|
+
- Detects missing `memories` columns and adds them idempotently:
|
|
17
|
+
- `category`
|
|
18
|
+
- `failure_reason`
|
|
19
|
+
- `tool_state`
|
|
20
|
+
- `corrected_to`
|
|
21
|
+
- Retries schema initialization after migration
|
|
22
|
+
- Added regression test: `should migrate legacy memories table without category column`
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
1
26
|
# v0.6.5 Changelog
|
|
2
27
|
|
|
3
28
|
## Bug Fixes
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# v0.7 Proposal: Tagged Session Review → Skill Creation
|
|
2
|
+
|
|
3
|
+
## Why
|
|
4
|
+
|
|
5
|
+
Community feedback highlighted a real gap:
|
|
6
|
+
- quick correction capture is useful,
|
|
7
|
+
- but **durable behavior should be promoted to skills intentionally**,
|
|
8
|
+
- and that flow should be reviewable in the TUI.
|
|
9
|
+
|
|
10
|
+
## Goal
|
|
11
|
+
|
|
12
|
+
Add a deterministic review workflow:
|
|
13
|
+
1. collect candidate learnings from session messages,
|
|
14
|
+
2. review them in a TUI modal,
|
|
15
|
+
3. promote selected items into a draft skill,
|
|
16
|
+
4. save via existing `skill` tool.
|
|
17
|
+
|
|
18
|
+
## Non-goals
|
|
19
|
+
|
|
20
|
+
- Auto-creating skills without user review.
|
|
21
|
+
- Replacing core memory entirely.
|
|
22
|
+
- Requiring git history for extraction.
|
|
23
|
+
|
|
24
|
+
## UX (Target)
|
|
25
|
+
|
|
26
|
+
### Command
|
|
27
|
+
- `/memory-review-candidates`
|
|
28
|
+
|
|
29
|
+
### Modal flow
|
|
30
|
+
1. **Candidate list** (tag, source session, snippet, confidence)
|
|
31
|
+
2. Actions per candidate: `approve`, `reject`, `edit`, `merge with...`
|
|
32
|
+
3. Multi-select candidates and choose `Create skill draft`
|
|
33
|
+
4. Draft editor with sections:
|
|
34
|
+
- `## When to Use`
|
|
35
|
+
- `## Procedure`
|
|
36
|
+
- `## Pitfalls`
|
|
37
|
+
- `## Verification`
|
|
38
|
+
5. Save with `skill.create`
|
|
39
|
+
|
|
40
|
+
## Candidate Sources
|
|
41
|
+
|
|
42
|
+
Priority order:
|
|
43
|
+
1. Explicit message tags from Pi sessions (when available)
|
|
44
|
+
2. Heuristic extraction from conversation patterns:
|
|
45
|
+
- repeated corrections,
|
|
46
|
+
- multi-step successful runs,
|
|
47
|
+
- repeated tool sequences,
|
|
48
|
+
- resolved failures with clear fix.
|
|
49
|
+
|
|
50
|
+
## Data Model (SQLite)
|
|
51
|
+
|
|
52
|
+
New table (proposal): `memory_candidates`
|
|
53
|
+
|
|
54
|
+
Columns:
|
|
55
|
+
- `id` INTEGER PK
|
|
56
|
+
- `session_id` TEXT
|
|
57
|
+
- `message_id` TEXT
|
|
58
|
+
- `project` TEXT
|
|
59
|
+
- `tag` TEXT
|
|
60
|
+
- `snippet` TEXT
|
|
61
|
+
- `rationale` TEXT
|
|
62
|
+
- `status` TEXT CHECK (`pending`,`approved`,`rejected`,`promoted`)
|
|
63
|
+
- `created_at` TEXT
|
|
64
|
+
- `updated_at` TEXT
|
|
65
|
+
- `promoted_skill` TEXT NULL
|
|
66
|
+
|
|
67
|
+
## Integration with Existing System
|
|
68
|
+
|
|
69
|
+
- Keep current memory + failure capture.
|
|
70
|
+
- Add a **promotion path** from memory/candidates to skills.
|
|
71
|
+
- Use existing `skill` tool as persistence layer.
|
|
72
|
+
- Use existing `session_search`/indexing infra for candidate discovery context.
|
|
73
|
+
|
|
74
|
+
## Rollout Plan
|
|
75
|
+
|
|
76
|
+
### Phase 1: Candidate staging (no modal)
|
|
77
|
+
- Create `memory_candidates` table
|
|
78
|
+
- Add extraction + `/memory-candidates` list command
|
|
79
|
+
- Add approve/reject commands
|
|
80
|
+
|
|
81
|
+
### Phase 2: TUI review modal
|
|
82
|
+
- Interactive candidate triage in one place
|
|
83
|
+
- Batch select + merge + edit
|
|
84
|
+
|
|
85
|
+
### Phase 3: Skill draft + save
|
|
86
|
+
- Generate skill draft from approved candidates
|
|
87
|
+
- Edit + save with `skill.create`
|
|
88
|
+
|
|
89
|
+
### Phase 4: Quality controls
|
|
90
|
+
- duplicate candidate suppression
|
|
91
|
+
- confidence thresholds
|
|
92
|
+
- weekly reminder: pending candidates review
|
|
93
|
+
|
|
94
|
+
## Success Criteria
|
|
95
|
+
|
|
96
|
+
- Lower noisy memory growth in `MEMORY.md`
|
|
97
|
+
- Higher percentage of reusable knowledge landing in `skills/`
|
|
98
|
+
- Fewer repeated correction loops across sessions
|
|
99
|
+
- Users report that learning feels intentional, not "whack-a-mole"
|
|
100
|
+
|
|
101
|
+
## Open Questions
|
|
102
|
+
|
|
103
|
+
1. Should candidates be extracted turn-by-turn or session-end only?
|
|
104
|
+
2. Should approved candidates auto-expire if not promoted in N days?
|
|
105
|
+
3. Should skill drafts include linked source message IDs for traceability?
|
|
106
|
+
4. Should project scope be default-on with optional cross-project merge mode?
|
|
107
|
+
|
|
108
|
+
## Notes
|
|
109
|
+
|
|
110
|
+
This proposal complements (not replaces) core memory.
|
|
111
|
+
Memory remains fast capture; skills remain durable procedure.
|
|
112
|
+
The new modal creates the missing bridge between them.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-hermes-memory",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.7",
|
|
4
4
|
"description": "🧠 Persistent memory + 🔍 session search + 🛡️ secret scanning for Pi. SQLite FTS5 search across every conversation, auto-consolidation, memory aging, procedural skills. 272 tests. Ported from Hermes agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
package/src/config.ts
CHANGED
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
DEFAULT_NUDGE_INTERVAL,
|
|
10
10
|
DEFAULT_FLUSH_MIN_TURNS,
|
|
11
11
|
DEFAULT_NUDGE_TOOL_CALLS,
|
|
12
|
+
DEFAULT_FAILURE_INJECTION_MAX_AGE_DAYS,
|
|
13
|
+
DEFAULT_FAILURE_INJECTION_MAX_ENTRIES,
|
|
12
14
|
} from "./constants.js";
|
|
13
15
|
|
|
14
16
|
const DEFAULT_CONFIG: MemoryConfig = {
|
|
@@ -22,6 +24,9 @@ const DEFAULT_CONFIG: MemoryConfig = {
|
|
|
22
24
|
flushMinTurns: DEFAULT_FLUSH_MIN_TURNS,
|
|
23
25
|
autoConsolidate: true,
|
|
24
26
|
correctionDetection: true,
|
|
27
|
+
failureInjectionEnabled: true,
|
|
28
|
+
failureInjectionMaxAgeDays: DEFAULT_FAILURE_INJECTION_MAX_AGE_DAYS,
|
|
29
|
+
failureInjectionMaxEntries: DEFAULT_FAILURE_INJECTION_MAX_ENTRIES,
|
|
25
30
|
nudgeToolCalls: DEFAULT_NUDGE_TOOL_CALLS,
|
|
26
31
|
};
|
|
27
32
|
|
|
@@ -48,6 +53,9 @@ export function loadConfig(): MemoryConfig {
|
|
|
48
53
|
if (typeof parsed.flushMinTurns === "number") config.flushMinTurns = parsed.flushMinTurns;
|
|
49
54
|
if (typeof parsed.autoConsolidate === "boolean") config.autoConsolidate = parsed.autoConsolidate;
|
|
50
55
|
if (typeof parsed.correctionDetection === "boolean") config.correctionDetection = parsed.correctionDetection;
|
|
56
|
+
if (typeof parsed.failureInjectionEnabled === "boolean") config.failureInjectionEnabled = parsed.failureInjectionEnabled;
|
|
57
|
+
if (typeof parsed.failureInjectionMaxAgeDays === "number") config.failureInjectionMaxAgeDays = parsed.failureInjectionMaxAgeDays;
|
|
58
|
+
if (typeof parsed.failureInjectionMaxEntries === "number") config.failureInjectionMaxEntries = parsed.failureInjectionMaxEntries;
|
|
51
59
|
if (typeof parsed.nudgeToolCalls === "number") config.nudgeToolCalls = parsed.nudgeToolCalls;
|
|
52
60
|
if (typeof parsed.projectCharLimit === "number") config.projectCharLimit = parsed.projectCharLimit;
|
|
53
61
|
if (typeof parsed.memoryDir === "string") config.memoryDir = parsed.memoryDir;
|
package/src/constants.ts
CHANGED
|
@@ -18,6 +18,8 @@ export const DEFAULT_NUDGE_INTERVAL = 10;
|
|
|
18
18
|
export const DEFAULT_FLUSH_MIN_TURNS = 6;
|
|
19
19
|
export const DEFAULT_NUDGE_TOOL_CALLS = 15;
|
|
20
20
|
export const DEFAULT_SKILL_TRIGGER_TOOL_CALLS = 8;
|
|
21
|
+
export const DEFAULT_FAILURE_INJECTION_MAX_AGE_DAYS = 7;
|
|
22
|
+
export const DEFAULT_FAILURE_INJECTION_MAX_ENTRIES = 5;
|
|
21
23
|
|
|
22
24
|
// ─── File names ───
|
|
23
25
|
export const MEMORY_FILE = "MEMORY.md";
|
package/src/store/db.ts
CHANGED
|
@@ -38,11 +38,53 @@ export class DatabaseManager {
|
|
|
38
38
|
db.pragma('foreign_keys = ON');
|
|
39
39
|
|
|
40
40
|
// Create tables and triggers
|
|
41
|
-
|
|
41
|
+
try {
|
|
42
|
+
db.exec(SCHEMA_SQL);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
if (!this.isLegacyMemoriesCategoryError(err)) {
|
|
45
|
+
throw err;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Legacy DB from pre-v0.6 can have memories table without the category
|
|
49
|
+
// and failure metadata columns. Add missing columns, then retry schema.
|
|
50
|
+
this.ensureMemoriesColumns(db);
|
|
51
|
+
db.exec(SCHEMA_SQL);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Extra safety: always ensure the legacy memories columns exist, even when
|
|
55
|
+
// schema execution succeeds (idempotent on upgraded DBs).
|
|
56
|
+
this.ensureMemoriesColumns(db);
|
|
42
57
|
|
|
43
58
|
return db;
|
|
44
59
|
}
|
|
45
60
|
|
|
61
|
+
private isLegacyMemoriesCategoryError(err: unknown): boolean {
|
|
62
|
+
if (!(err instanceof Error)) return false;
|
|
63
|
+
const msg = err.message.toLowerCase();
|
|
64
|
+
return msg.includes('no such column: category') || msg.includes('memories(category)');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private ensureMemoriesColumns(db: Database.Database): void {
|
|
68
|
+
const tableExists = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='memories'").get() as { name: string } | undefined;
|
|
69
|
+
if (!tableExists) return;
|
|
70
|
+
|
|
71
|
+
const columns = db.prepare('PRAGMA table_info(memories)').all() as { name: string }[];
|
|
72
|
+
const names = new Set(columns.map((c) => c.name));
|
|
73
|
+
|
|
74
|
+
if (!names.has('category')) {
|
|
75
|
+
db.exec('ALTER TABLE memories ADD COLUMN category TEXT');
|
|
76
|
+
}
|
|
77
|
+
if (!names.has('failure_reason')) {
|
|
78
|
+
db.exec('ALTER TABLE memories ADD COLUMN failure_reason TEXT');
|
|
79
|
+
}
|
|
80
|
+
if (!names.has('tool_state')) {
|
|
81
|
+
db.exec('ALTER TABLE memories ADD COLUMN tool_state TEXT');
|
|
82
|
+
}
|
|
83
|
+
if (!names.has('corrected_to')) {
|
|
84
|
+
db.exec('ALTER TABLE memories ADD COLUMN corrected_to TEXT');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
46
88
|
/**
|
|
47
89
|
* Close the database connection.
|
|
48
90
|
*/
|
|
@@ -19,6 +19,8 @@ import {
|
|
|
19
19
|
ENTRY_DELIMITER,
|
|
20
20
|
DEFAULT_MEMORY_CHAR_LIMIT,
|
|
21
21
|
DEFAULT_USER_CHAR_LIMIT,
|
|
22
|
+
DEFAULT_FAILURE_INJECTION_MAX_AGE_DAYS,
|
|
23
|
+
DEFAULT_FAILURE_INJECTION_MAX_ENTRIES,
|
|
22
24
|
MEMORY_FILE,
|
|
23
25
|
USER_FILE,
|
|
24
26
|
} from "../constants.js";
|
|
@@ -280,12 +282,17 @@ export class MemoryStore {
|
|
|
280
282
|
if (this.snapshot.user) parts.push(this.fenceBlock(this.snapshot.user));
|
|
281
283
|
|
|
282
284
|
// Add recent failure memories
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
const maxFailures =
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
|
|
285
|
+
if (this.config.failureInjectionEnabled !== false) {
|
|
286
|
+
const maxAgeDays = this.config.failureInjectionMaxAgeDays ?? DEFAULT_FAILURE_INJECTION_MAX_AGE_DAYS;
|
|
287
|
+
const maxFailures = this.config.failureInjectionMaxEntries ?? DEFAULT_FAILURE_INJECTION_MAX_ENTRIES;
|
|
288
|
+
const recentFailures = this.getFailureEntries(maxAgeDays);
|
|
289
|
+
if (recentFailures.length > 0) {
|
|
290
|
+
const failures = recentFailures.slice(0, maxFailures);
|
|
291
|
+
if (failures.length > 0) {
|
|
292
|
+
const failureBlock = this.renderFailureBlock(failures);
|
|
293
|
+
parts.push(this.fenceBlock(failureBlock));
|
|
294
|
+
}
|
|
295
|
+
}
|
|
289
296
|
}
|
|
290
297
|
|
|
291
298
|
return parts.join("\n\n");
|
package/src/types.ts
CHANGED
|
@@ -27,6 +27,12 @@ export interface MemoryConfig {
|
|
|
27
27
|
autoConsolidate: boolean;
|
|
28
28
|
/** Detect user corrections and trigger immediate memory save. Default: true */
|
|
29
29
|
correctionDetection: boolean;
|
|
30
|
+
/** Inject recent failure memories into the system prompt. Default: true */
|
|
31
|
+
failureInjectionEnabled: boolean;
|
|
32
|
+
/** Maximum age in days for injected failure memories. Default: 7 */
|
|
33
|
+
failureInjectionMaxAgeDays: number;
|
|
34
|
+
/** Maximum number of failure memories to inject. Default: 5 */
|
|
35
|
+
failureInjectionMaxEntries: number;
|
|
30
36
|
/** Tool calls before triggering background review (in addition to turn count). Default: 15 */
|
|
31
37
|
nudgeToolCalls: number;
|
|
32
38
|
/** Enable session history search via SQLite FTS5. Default: true */
|