pi-hermes-memory 0.6.4 → 0.6.6
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/docs/0.6/CHANGELOG.md +57 -0
- package/package.json +1 -1
- package/src/handlers/background-review.ts +63 -47
- package/src/store/db.ts +43 -1
|
@@ -0,0 +1,57 @@
|
|
|
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
|
+
|
|
26
|
+
# v0.6.5 Changelog
|
|
27
|
+
|
|
28
|
+
## Bug Fixes
|
|
29
|
+
|
|
30
|
+
### Auto-review no longer blocks interactive chat (#10)
|
|
31
|
+
|
|
32
|
+
The background auto-review was `await`ing `pi.exec()` inside the `turn_end` handler, which
|
|
33
|
+
blocked the chat from responding while the review subprocess ran. Fixed by making the
|
|
34
|
+
review subprocess fire-and-forget — the turn_end handler returns immediately, and the
|
|
35
|
+
subprocess completes asynchronously.
|
|
36
|
+
|
|
37
|
+
- `pi.exec()` is no longer awaited — handler returns immediately
|
|
38
|
+
- `reviewInProgress` guard resets in `.then()` / `.catch()` callbacks
|
|
39
|
+
- Overlapping reviews are still prevented by the guard
|
|
40
|
+
- Notifications are delivered via `.then()` callback once the subprocess completes
|
|
41
|
+
|
|
42
|
+
### Auto-review failures on Windows no longer show errors (#9)
|
|
43
|
+
|
|
44
|
+
On Windows git-bash, `pi exec` subprocesses could exit with code 1 producing
|
|
45
|
+
`[hermes] auto-review failed (exit=1): unknown error` messages every few turns. Fixed by
|
|
46
|
+
making auto-review truly best-effort — non-zero exit codes and spawn errors are silently
|
|
47
|
+
ignored.
|
|
48
|
+
|
|
49
|
+
- Suppressed error notifications for non-zero exit codes
|
|
50
|
+
- Suppressed error notifications for subprocess failures (timeout, signal, spawn)
|
|
51
|
+
- The next review cycle will retry automatically
|
|
52
|
+
|
|
53
|
+
### Crash safety
|
|
54
|
+
|
|
55
|
+
- Snapshot-building code (`getBranch()`, message parsing) is wrapped in a minimal try/catch
|
|
56
|
+
to handle expired sessions gracefully
|
|
57
|
+
- Early return resets `reviewInProgress` guard to unblock future reviews
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-hermes-memory",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.6",
|
|
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",
|
|
@@ -66,10 +66,10 @@ export function setupBackgroundReview(
|
|
|
66
66
|
toolCallsSinceReview = 0;
|
|
67
67
|
reviewInProgress = true;
|
|
68
68
|
|
|
69
|
+
// Build conversation snapshot from session entries (crash-safe)
|
|
70
|
+
let parts: string[] = [];
|
|
69
71
|
try {
|
|
70
|
-
// Build conversation snapshot from session entries
|
|
71
72
|
const entries = ctx.sessionManager.getBranch();
|
|
72
|
-
const parts: string[] = [];
|
|
73
73
|
|
|
74
74
|
for (const entry of entries) {
|
|
75
75
|
if (entry.type !== "message") continue;
|
|
@@ -79,56 +79,72 @@ export function setupBackgroundReview(
|
|
|
79
79
|
const prefix = msg.role === "user" ? "[USER]" : "[ASSISTANT]";
|
|
80
80
|
parts.push(`${prefix}: ${text}`);
|
|
81
81
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
"",
|
|
91
|
-
"--- Current Memory ---",
|
|
92
|
-
currentMemory || "(empty)",
|
|
93
|
-
"",
|
|
94
|
-
"--- Current User Profile ---",
|
|
95
|
-
currentUser || "(empty)",
|
|
96
|
-
];
|
|
97
|
-
|
|
98
|
-
if (currentProject !== null) {
|
|
99
|
-
reviewPrompt.push(
|
|
100
|
-
"",
|
|
101
|
-
"--- Current Project Memory ---",
|
|
102
|
-
currentProject || "(empty)",
|
|
103
|
-
);
|
|
104
|
-
}
|
|
82
|
+
} catch {
|
|
83
|
+
reviewInProgress = false;
|
|
84
|
+
return; // Session expired or empty — nothing to review
|
|
85
|
+
}
|
|
86
|
+
if (parts.length < 4) {
|
|
87
|
+
reviewInProgress = false;
|
|
88
|
+
return; // Not enough conversation to review
|
|
89
|
+
}
|
|
105
90
|
|
|
91
|
+
const currentMemory = store.getMemoryEntries().join("\n§\n");
|
|
92
|
+
const currentUser = store.getUserEntries().join("\n§\n");
|
|
93
|
+
const currentProject = projectStore ? projectStore.getMemoryEntries().join("\n§\n") : null;
|
|
94
|
+
|
|
95
|
+
const reviewPrompt = [
|
|
96
|
+
COMBINED_REVIEW_PROMPT,
|
|
97
|
+
"",
|
|
98
|
+
"--- Current Memory ---",
|
|
99
|
+
currentMemory || "(empty)",
|
|
100
|
+
"",
|
|
101
|
+
"--- Current User Profile ---",
|
|
102
|
+
currentUser || "(empty)",
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
if (currentProject !== null) {
|
|
106
106
|
reviewPrompt.push(
|
|
107
107
|
"",
|
|
108
|
-
"---
|
|
109
|
-
|
|
108
|
+
"--- Current Project Memory ---",
|
|
109
|
+
currentProject || "(empty)",
|
|
110
110
|
);
|
|
111
|
+
}
|
|
111
112
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
113
|
+
reviewPrompt.push(
|
|
114
|
+
"",
|
|
115
|
+
"--- Conversation to Review ---",
|
|
116
|
+
parts.join("\n\n"),
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
// Fire-and-forget: do NOT await. The review runs in a subprocess;
|
|
120
|
+
// blocking turn_end would freeze the interactive chat.
|
|
121
|
+
// Notifications are delivered via .then() once the subprocess completes.
|
|
122
|
+
//
|
|
123
|
+
// We intentionally omit ctx.signal — the signal is tied to the turn
|
|
124
|
+
// lifetime and would abort the subprocess before it finishes now that
|
|
125
|
+
// we're not awaiting. The timeout (120s) provides its own safety net.
|
|
126
|
+
const reviewPromise = pi.exec("pi", ["-p", "--no-session", reviewPrompt.join("\n")], {
|
|
127
|
+
signal: undefined,
|
|
128
|
+
timeout: 120000,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
reviewPromise
|
|
132
|
+
.then((result) => {
|
|
133
|
+
reviewInProgress = false;
|
|
134
|
+
if (result.code === 0 && result.stdout) {
|
|
135
|
+
const output = result.stdout.trim();
|
|
136
|
+
if (output && !output.toLowerCase().includes("nothing to save")) {
|
|
137
|
+
ctx.ui.notify("💾 Memory auto-reviewed and updated", "info");
|
|
138
|
+
}
|
|
121
139
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
reviewInProgress = false;
|
|
132
|
-
}
|
|
140
|
+
// Auto-review is best-effort. Non-zero exits are silently skipped —
|
|
141
|
+
// common on Windows where pi CLI may resolve differently. The next
|
|
142
|
+
// review cycle will retry.
|
|
143
|
+
})
|
|
144
|
+
.catch(() => {
|
|
145
|
+
// Best-effort: subprocess failures (timeout, signal, spawn errors)
|
|
146
|
+
// are silently ignored. The next review cycle will retry.
|
|
147
|
+
reviewInProgress = false;
|
|
148
|
+
});
|
|
133
149
|
});
|
|
134
150
|
}
|
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
|
*/
|