pi-hermes-memory 0.6.3 → 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
- package/src/handlers/learn-memory.ts +32 -14
|
@@ -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
|
}
|
|
@@ -29,17 +29,28 @@ export function registerLearnMemoryCommand(pi: ExtensionAPI): void {
|
|
|
29
29
|
lines.push(" ║ 📦 What Gets Saved ║");
|
|
30
30
|
lines.push(" ╚══════════════════════════════════════════════╝");
|
|
31
31
|
lines.push("");
|
|
32
|
-
lines.push(" Type │ File
|
|
33
|
-
lines.push("
|
|
34
|
-
lines.push(" 🧠 Memory │ MEMORY.md
|
|
35
|
-
lines.push(" 👤 User Profile │ USER.md
|
|
36
|
-
lines.push("
|
|
37
|
-
lines.push("
|
|
38
|
-
lines.push("");
|
|
39
|
-
lines.push("
|
|
40
|
-
lines.push("
|
|
41
|
-
lines.push("
|
|
32
|
+
lines.push(" Type │ File │ Limit");
|
|
33
|
+
lines.push(" ────────────────┼───────────────┼────────────");
|
|
34
|
+
lines.push(" 🧠 Memory │ MEMORY.md │ 5,000 chars");
|
|
35
|
+
lines.push(" 👤 User Profile │ USER.md │ 5,000 chars");
|
|
36
|
+
lines.push(" ⚠️ Failures │ failures.md │ 10,000 chars");
|
|
37
|
+
lines.push(" 📚 Skills │ skills/*.md │ Unlimited");
|
|
38
|
+
lines.push(" 💾 Extended │ sessions.db │ Unlimited");
|
|
39
|
+
lines.push("");
|
|
40
|
+
lines.push(" Memory: Facts — env details, project conventions, tool quirks");
|
|
41
|
+
lines.push(" User: Who you are — name, preferences, communication style");
|
|
42
|
+
lines.push(" Failures: What didn't work — corrections, failures, insights");
|
|
43
|
+
lines.push(" Skills: Procedures — how to debug, deploy, test");
|
|
42
44
|
lines.push(" Extended: Searchable memories beyond the core limit");
|
|
45
|
+
lines.push("");
|
|
46
|
+
lines.push(" Memory Categories:");
|
|
47
|
+
lines.push(" ─────────────────");
|
|
48
|
+
lines.push(" [failure] What was tried but didn't work");
|
|
49
|
+
lines.push(" [correction] User corrected the agent");
|
|
50
|
+
lines.push(" [insight] Learning from experience");
|
|
51
|
+
lines.push(" [preference] User preference");
|
|
52
|
+
lines.push(" [convention] Project convention");
|
|
53
|
+
lines.push(" [tool-quirk] Tool-specific knowledge");
|
|
43
54
|
}
|
|
44
55
|
|
|
45
56
|
if (section.startsWith("🔧")) {
|
|
@@ -50,6 +61,7 @@ export function registerLearnMemoryCommand(pi: ExtensionAPI): void {
|
|
|
50
61
|
lines.push("");
|
|
51
62
|
lines.push(" memory (add/replace/remove)");
|
|
52
63
|
lines.push(" Save, update, or delete memories");
|
|
64
|
+
lines.push(" Targets: memory, user, failure, project");
|
|
53
65
|
lines.push("");
|
|
54
66
|
lines.push(" skill (create/view/patch/edit/delete)");
|
|
55
67
|
lines.push(" Save reusable procedures");
|
|
@@ -59,6 +71,8 @@ export function registerLearnMemoryCommand(pi: ExtensionAPI): void {
|
|
|
59
71
|
lines.push("");
|
|
60
72
|
lines.push(" memory_search");
|
|
61
73
|
lines.push(" Search extended memory store (unlimited)");
|
|
74
|
+
lines.push(" Filters: project, target, category");
|
|
75
|
+
lines.push(" Categories: failure, correction, insight, preference, convention, tool-quirk");
|
|
62
76
|
}
|
|
63
77
|
|
|
64
78
|
if (section.startsWith("📋")) {
|
|
@@ -86,6 +100,7 @@ export function registerLearnMemoryCommand(pi: ExtensionAPI): void {
|
|
|
86
100
|
lines.push(" • Environment facts (\"macOS M1\", \"Node 20\")");
|
|
87
101
|
lines.push(" • Corrections (\"don't use npm — use pnpm\")");
|
|
88
102
|
lines.push(" • Project conventions (\"monorepo with turborepo\")");
|
|
103
|
+
lines.push(" • Failures (\"tried localStorage — XSS vulnerability\")");
|
|
89
104
|
lines.push("");
|
|
90
105
|
lines.push(" ❌ DON'T save:");
|
|
91
106
|
lines.push(" • Task progress (\"finished implementing auth\")");
|
|
@@ -99,12 +114,13 @@ export function registerLearnMemoryCommand(pi: ExtensionAPI): void {
|
|
|
99
114
|
lines.push(" ║ 🔄 How Memory Flows ║");
|
|
100
115
|
lines.push(" ╚══════════════════════════════════════════════╝");
|
|
101
116
|
lines.push("");
|
|
102
|
-
lines.push(" 1. Session starts → Core memory injected");
|
|
117
|
+
lines.push(" 1. Session starts → Core memory + recent failures injected");
|
|
103
118
|
lines.push(" 2. During conversation → Agent saves via memory tool");
|
|
104
119
|
lines.push(" 3. Every 10 turns → Background review saves items");
|
|
105
|
-
lines.push(" 4. On correction → Immediate save");
|
|
106
|
-
lines.push(" 5.
|
|
107
|
-
lines.push(" 6.
|
|
120
|
+
lines.push(" 4. On correction → Immediate save as [correction] category");
|
|
121
|
+
lines.push(" 5. On failure → Saves what failed + why");
|
|
122
|
+
lines.push(" 6. When full → Auto-consolidation merges");
|
|
123
|
+
lines.push(" 7. Session ends → Final flush");
|
|
108
124
|
}
|
|
109
125
|
|
|
110
126
|
if (section.startsWith("🏗️")) {
|
|
@@ -117,6 +133,7 @@ export function registerLearnMemoryCommand(pi: ExtensionAPI): void {
|
|
|
117
133
|
lines.push(" ┌─────────────────────────────────────┐");
|
|
118
134
|
lines.push(" │ MEMORY.md — Facts, conventions │");
|
|
119
135
|
lines.push(" │ USER.md — Who you are │");
|
|
136
|
+
lines.push(" │ failures.md — Recent failures (7d) │");
|
|
120
137
|
lines.push(" │ Project memory — When cwd matches │");
|
|
121
138
|
lines.push(" └─────────────────────────────────────┘");
|
|
122
139
|
lines.push("");
|
|
@@ -124,6 +141,7 @@ export function registerLearnMemoryCommand(pi: ExtensionAPI): void {
|
|
|
124
141
|
lines.push(" ┌─────────────────────────────────────┐");
|
|
125
142
|
lines.push(" │ session_search(\"auth flow\") │");
|
|
126
143
|
lines.push(" │ memory_search(\"testing patterns\") │");
|
|
144
|
+
lines.push(" │ memory_search(\"auth\", cat:\"failure\")│");
|
|
127
145
|
lines.push(" └─────────────────────────────────────┘");
|
|
128
146
|
}
|
|
129
147
|
|