pi-hermes-memory 0.6.4 → 0.6.5
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 +32 -0
- package/package.json +1 -1
- package/src/handlers/background-review.ts +63 -47
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# v0.6.5 Changelog
|
|
2
|
+
|
|
3
|
+
## Bug Fixes
|
|
4
|
+
|
|
5
|
+
### Auto-review no longer blocks interactive chat (#10)
|
|
6
|
+
|
|
7
|
+
The background auto-review was `await`ing `pi.exec()` inside the `turn_end` handler, which
|
|
8
|
+
blocked the chat from responding while the review subprocess ran. Fixed by making the
|
|
9
|
+
review subprocess fire-and-forget — the turn_end handler returns immediately, and the
|
|
10
|
+
subprocess completes asynchronously.
|
|
11
|
+
|
|
12
|
+
- `pi.exec()` is no longer awaited — handler returns immediately
|
|
13
|
+
- `reviewInProgress` guard resets in `.then()` / `.catch()` callbacks
|
|
14
|
+
- Overlapping reviews are still prevented by the guard
|
|
15
|
+
- Notifications are delivered via `.then()` callback once the subprocess completes
|
|
16
|
+
|
|
17
|
+
### Auto-review failures on Windows no longer show errors (#9)
|
|
18
|
+
|
|
19
|
+
On Windows git-bash, `pi exec` subprocesses could exit with code 1 producing
|
|
20
|
+
`[hermes] auto-review failed (exit=1): unknown error` messages every few turns. Fixed by
|
|
21
|
+
making auto-review truly best-effort — non-zero exit codes and spawn errors are silently
|
|
22
|
+
ignored.
|
|
23
|
+
|
|
24
|
+
- Suppressed error notifications for non-zero exit codes
|
|
25
|
+
- Suppressed error notifications for subprocess failures (timeout, signal, spawn)
|
|
26
|
+
- The next review cycle will retry automatically
|
|
27
|
+
|
|
28
|
+
### Crash safety
|
|
29
|
+
|
|
30
|
+
- Snapshot-building code (`getBranch()`, message parsing) is wrapped in a minimal try/catch
|
|
31
|
+
to handle expired sessions gracefully
|
|
32
|
+
- 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.5",
|
|
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
|
}
|