pi-hermes-memory 0.7.13 → 0.7.14
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 +4 -0
- package/package.json +1 -1
- package/src/config.ts +11 -1
- package/src/handlers/auto-consolidate.ts +62 -10
- package/src/handlers/background-review.ts +3 -2
- package/src/handlers/correction-detector.ts +3 -2
- package/src/handlers/pi-child-process.ts +119 -0
- package/src/handlers/session-flush.ts +3 -2
- package/src/index.ts +3 -3
- package/src/store/db.ts +35 -35
- package/src/types.ts +6 -0
package/README.md
CHANGED
|
@@ -424,6 +424,8 @@ Create `~/.pi/agent/hermes-memory-config.json`:
|
|
|
424
424
|
"memoryDir": "~/.pi/agent/pi-hermes-memory",
|
|
425
425
|
"projectsMemoryDir": "projects-memory",
|
|
426
426
|
"sessionSearch": { "variant": "legacy" },
|
|
427
|
+
"llmModelOverride": "openrouter/deepseek/deepseek-v4-flash",
|
|
428
|
+
"llmThinkingOverride": "off",
|
|
427
429
|
"nudgeInterval": 10,
|
|
428
430
|
"nudgeToolCalls": 15,
|
|
429
431
|
"reviewRecentMessages": 0,
|
|
@@ -453,6 +455,8 @@ Create `~/.pi/agent/hermes-memory-config.json`:
|
|
|
453
455
|
| `memoryDir` | `~/.pi/agent/pi-hermes-memory` | Custom directory for extension storage files |
|
|
454
456
|
| `projectsMemoryDir` | `projects-memory` | Subdirectory under `~/.pi/agent/` for project-scoped memory |
|
|
455
457
|
| `sessionSearch` | `{ "variant": "legacy" }` | Session search implementation: `legacy` keeps the existing SQLite/FTS snippet search; `anchors` uses the opt-in Markdown request surface and returns compact JSONL line-range anchors from `~/.pi/agent/sessions/` |
|
|
458
|
+
| `llmModelOverride` | unset | Optional model override for child `pi -p` subprocess calls used by background review, correction save, session flush, and consolidation |
|
|
459
|
+
| `llmThinkingOverride` | unset | Optional thinking override for those child subprocess calls; valid values are `off`, `minimal`, `low`, `medium`, `high`, and `xhigh`. If `llmModelOverride` is set and this is omitted, child calls default to `off` |
|
|
456
460
|
| `nudgeInterval` | `10` | Turns between auto-reviews |
|
|
457
461
|
| `nudgeToolCalls` | `15` | Tool calls between auto-reviews (OR with turns) |
|
|
458
462
|
| `reviewRecentMessages` | `0` | Recent messages included in background review (`0` = all) |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-hermes-memory",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.14",
|
|
4
4
|
"description": "🧠 Persistent memory + 🔍 session search + 🛡️ secret scanning for Pi. Token-aware policy-only memory by default, SQLite FTS5 search, auto-consolidation, procedural skills. 368 tests. Ported from Hermes agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
package/src/config.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import * as os from "node:os";
|
|
4
|
-
import type { MemoryConfig, MemoryOverflowStrategy, SessionSearchVariant } from "./types.js";
|
|
4
|
+
import type { MemoryConfig, MemoryOverflowStrategy, SessionSearchVariant, ThinkingLevel } from "./types.js";
|
|
5
5
|
import {
|
|
6
6
|
DEFAULT_MEMORY_CHAR_LIMIT,
|
|
7
7
|
DEFAULT_USER_CHAR_LIMIT,
|
|
@@ -20,6 +20,7 @@ import { normalizeConfiguredMemoryDir, normalizeProjectsMemoryDir } from "./path
|
|
|
20
20
|
|
|
21
21
|
const MEMORY_OVERFLOW_STRATEGIES: readonly MemoryOverflowStrategy[] = ["auto-consolidate", "reject", "fifo-evict"];
|
|
22
22
|
const SESSION_SEARCH_VARIANTS: readonly SessionSearchVariant[] = ["legacy", "anchors"];
|
|
23
|
+
const THINKING_LEVELS: readonly ThinkingLevel[] = ["off", "minimal", "low", "medium", "high", "xhigh"];
|
|
23
24
|
|
|
24
25
|
function isMemoryOverflowStrategy(value: unknown): value is MemoryOverflowStrategy {
|
|
25
26
|
return typeof value === "string" && MEMORY_OVERFLOW_STRATEGIES.includes(value as MemoryOverflowStrategy);
|
|
@@ -29,6 +30,10 @@ function isSessionSearchVariant(value: unknown): value is SessionSearchVariant {
|
|
|
29
30
|
return typeof value === "string" && SESSION_SEARCH_VARIANTS.includes(value as SessionSearchVariant);
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
function isThinkingLevel(value: unknown): value is ThinkingLevel {
|
|
34
|
+
return typeof value === "string" && THINKING_LEVELS.includes(value as ThinkingLevel);
|
|
35
|
+
}
|
|
36
|
+
|
|
32
37
|
const DEFAULT_CONFIG: MemoryConfig = {
|
|
33
38
|
memoryMode: "policy-only",
|
|
34
39
|
memoryPolicyStyle: "full",
|
|
@@ -127,6 +132,11 @@ export function loadConfig(configPath = DEFAULT_CONFIG_PATH): MemoryConfig {
|
|
|
127
132
|
) {
|
|
128
133
|
config.sessionSearch = { variant: parsed.sessionSearch.variant };
|
|
129
134
|
}
|
|
135
|
+
if (typeof parsed.llmModelOverride === "string") {
|
|
136
|
+
const trimmed = parsed.llmModelOverride.trim();
|
|
137
|
+
if (trimmed.length > 0) config.llmModelOverride = trimmed;
|
|
138
|
+
}
|
|
139
|
+
if (isThinkingLevel(parsed.llmThinkingOverride)) config.llmThinkingOverride = parsed.llmThinkingOverride;
|
|
130
140
|
if (hasMemoryOverflowStrategy) {
|
|
131
141
|
config.autoConsolidate = config.memoryOverflowStrategy === "auto-consolidate";
|
|
132
142
|
} else if (hasLegacyAutoConsolidate) {
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
11
11
|
import { MemoryStore } from "../store/memory-store.js";
|
|
12
12
|
import { CONSOLIDATION_PROMPT, ENTRY_DELIMITER } from "../constants.js";
|
|
13
|
-
import type { ConsolidationResult } from "../types.js";
|
|
13
|
+
import type { ConsolidationResult, MemoryConfig } from "../types.js";
|
|
14
|
+
import { execChildPrompt } from "./pi-child-process.js";
|
|
14
15
|
|
|
15
16
|
type MemoryTarget = "memory" | "user" | "failure";
|
|
16
17
|
type ToolMemoryTarget = MemoryTarget | "project";
|
|
@@ -26,6 +27,20 @@ function labelForTarget(target: MemoryTarget, toolTarget: ToolMemoryTarget): str
|
|
|
26
27
|
return "Memory";
|
|
27
28
|
}
|
|
28
29
|
|
|
30
|
+
function describeConsolidationFailure(
|
|
31
|
+
result: { code: number; stderr?: string; killed?: boolean },
|
|
32
|
+
timeoutMs: number,
|
|
33
|
+
): string {
|
|
34
|
+
const stderr = result.stderr?.trim();
|
|
35
|
+
const terminated = result.killed || result.code === 143;
|
|
36
|
+
|
|
37
|
+
if (terminated) {
|
|
38
|
+
return `Consolidation subprocess was terminated (likely timeout or cancellation). Timeout: ${timeoutMs}ms. Consider increasing consolidationTimeoutMs if this is a manual run.`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return `Consolidation process exited with code ${result.code}: ${stderr?.slice(0, 200) || "unknown error"}`;
|
|
42
|
+
}
|
|
43
|
+
|
|
29
44
|
export async function triggerConsolidation(
|
|
30
45
|
pi: ExtensionAPI,
|
|
31
46
|
store: MemoryStore,
|
|
@@ -33,6 +48,7 @@ export async function triggerConsolidation(
|
|
|
33
48
|
signal?: AbortSignal,
|
|
34
49
|
timeoutMs: number = 60000,
|
|
35
50
|
toolTarget: ToolMemoryTarget = target,
|
|
51
|
+
llmConfig: Pick<MemoryConfig, "llmModelOverride" | "llmThinkingOverride"> = {},
|
|
36
52
|
): Promise<ConsolidationResult> {
|
|
37
53
|
const entries = entriesForTarget(store, target);
|
|
38
54
|
const currentContent = entries.join(ENTRY_DELIMITER);
|
|
@@ -47,17 +63,18 @@ export async function triggerConsolidation(
|
|
|
47
63
|
].join("\n");
|
|
48
64
|
|
|
49
65
|
try {
|
|
50
|
-
const result = await
|
|
66
|
+
const result = await execChildPrompt(pi, prompt, llmConfig, {
|
|
51
67
|
signal,
|
|
52
|
-
|
|
53
|
-
|
|
68
|
+
timeoutMs,
|
|
69
|
+
retryWithoutOverrides: true,
|
|
70
|
+
}) as { code: number; stdout?: string; stderr?: string; killed?: boolean };
|
|
54
71
|
|
|
55
72
|
if (result.code === 0) {
|
|
56
73
|
return { consolidated: true };
|
|
57
74
|
}
|
|
58
75
|
return {
|
|
59
76
|
consolidated: false,
|
|
60
|
-
error:
|
|
77
|
+
error: describeConsolidationFailure(result, timeoutMs),
|
|
61
78
|
};
|
|
62
79
|
} catch (err) {
|
|
63
80
|
return {
|
|
@@ -76,10 +93,12 @@ export function registerConsolidateCommand(
|
|
|
76
93
|
timeoutMs: number = 60000,
|
|
77
94
|
projectStore: MemoryStore | null = null,
|
|
78
95
|
projectName?: string | null,
|
|
96
|
+
llmConfig: Pick<MemoryConfig, "llmModelOverride" | "llmThinkingOverride"> = {},
|
|
79
97
|
): void {
|
|
80
98
|
pi.registerCommand("memory-consolidate", {
|
|
81
99
|
description: "Manually trigger memory consolidation to free up space",
|
|
82
100
|
handler: async (_args, ctx) => {
|
|
101
|
+
const manualTimeoutMs = Math.max(timeoutMs, 180000);
|
|
83
102
|
const results: string[] = [];
|
|
84
103
|
const targets: Array<{
|
|
85
104
|
label: string;
|
|
@@ -100,6 +119,16 @@ export function registerConsolidateCommand(
|
|
|
100
119
|
});
|
|
101
120
|
}
|
|
102
121
|
|
|
122
|
+
try {
|
|
123
|
+
ctx.ui.notify(
|
|
124
|
+
`🔄 Starting memory consolidation for ${targets.length} target${targets.length === 1 ? "" : "s"}...`,
|
|
125
|
+
"info",
|
|
126
|
+
);
|
|
127
|
+
} catch {
|
|
128
|
+
// Best-effort only. If the command context is already stale, continue
|
|
129
|
+
// with the consolidation work rather than failing before it starts.
|
|
130
|
+
}
|
|
131
|
+
|
|
103
132
|
for (const item of targets) {
|
|
104
133
|
const entries = entriesForTarget(item.store, item.target);
|
|
105
134
|
|
|
@@ -108,7 +137,24 @@ export function registerConsolidateCommand(
|
|
|
108
137
|
continue;
|
|
109
138
|
}
|
|
110
139
|
|
|
111
|
-
|
|
140
|
+
try {
|
|
141
|
+
ctx.ui.notify(
|
|
142
|
+
`⏳ Consolidating ${item.label}...`,
|
|
143
|
+
"info",
|
|
144
|
+
);
|
|
145
|
+
} catch {
|
|
146
|
+
// Best-effort progress feedback only.
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const result = await triggerConsolidation(
|
|
150
|
+
pi,
|
|
151
|
+
item.store,
|
|
152
|
+
item.target,
|
|
153
|
+
ctx.signal,
|
|
154
|
+
manualTimeoutMs,
|
|
155
|
+
item.toolTarget,
|
|
156
|
+
llmConfig,
|
|
157
|
+
);
|
|
112
158
|
|
|
113
159
|
if (result.consolidated) {
|
|
114
160
|
await item.store.loadFromDisk();
|
|
@@ -118,10 +164,16 @@ export function registerConsolidateCommand(
|
|
|
118
164
|
}
|
|
119
165
|
}
|
|
120
166
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
167
|
+
const summary = `\n 🔄 Memory Consolidation\n ${"─".repeat(30)}\n${results.map((r) => ` ${r}`).join("\n")}`;
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
ctx.ui.notify(summary, "info");
|
|
171
|
+
} catch {
|
|
172
|
+
// Child consolidation can indirectly trigger a runtime reload/session
|
|
173
|
+
// replacement. If that happens, the original command ctx is stale by
|
|
174
|
+
// the time we reach the final summary, so the command should exit
|
|
175
|
+
// quietly instead of surfacing a stale-ctx error.
|
|
176
|
+
}
|
|
125
177
|
},
|
|
126
178
|
});
|
|
127
179
|
}
|
|
@@ -12,6 +12,7 @@ import { MemoryStore } from "../store/memory-store.js";
|
|
|
12
12
|
import { COMBINED_REVIEW_PROMPT } from "../constants.js";
|
|
13
13
|
import type { MemoryConfig } from "../types.js";
|
|
14
14
|
import { applyRecentMessageLimit, collectMessageParts } from "./message-parts.js";
|
|
15
|
+
import { execChildPrompt } from "./pi-child-process.js";
|
|
15
16
|
|
|
16
17
|
export function setupBackgroundReview(
|
|
17
18
|
pi: ExtensionAPI,
|
|
@@ -116,9 +117,9 @@ export function setupBackgroundReview(
|
|
|
116
117
|
// We intentionally omit ctx.signal — the signal is tied to the turn
|
|
117
118
|
// lifetime and would abort the subprocess before it finishes now that
|
|
118
119
|
// we're not awaiting. The timeout (120s) provides its own safety net.
|
|
119
|
-
const reviewPromise =
|
|
120
|
+
const reviewPromise = execChildPrompt(pi, reviewPrompt.join("\n"), config, {
|
|
120
121
|
signal: undefined,
|
|
121
|
-
|
|
122
|
+
timeoutMs: 120000,
|
|
122
123
|
});
|
|
123
124
|
|
|
124
125
|
reviewPromise
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
} from "../constants.js";
|
|
26
26
|
import type { MemoryConfig } from "../types.js";
|
|
27
27
|
import { getMessageText } from "../types.js";
|
|
28
|
+
import { execChildPrompt } from "./pi-child-process.js";
|
|
28
29
|
|
|
29
30
|
/**
|
|
30
31
|
* Extract the directive part from a correction message.
|
|
@@ -205,9 +206,9 @@ export function setupCorrectionDetector(
|
|
|
205
206
|
recentParts.join("\n\n"),
|
|
206
207
|
);
|
|
207
208
|
|
|
208
|
-
const result = await
|
|
209
|
+
const result = await execChildPrompt(pi, prompt.join("\n"), config, {
|
|
209
210
|
signal: ctx.signal,
|
|
210
|
-
|
|
211
|
+
timeoutMs: 30000,
|
|
211
212
|
});
|
|
212
213
|
|
|
213
214
|
if (result.code === 0 && result.stdout) {
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { MemoryConfig, ThinkingLevel } from "../types.js";
|
|
3
|
+
|
|
4
|
+
type ChildLlmConfig = Pick<MemoryConfig, "llmModelOverride" | "llmThinkingOverride">;
|
|
5
|
+
|
|
6
|
+
interface PiExecResult {
|
|
7
|
+
code: number;
|
|
8
|
+
stdout?: string;
|
|
9
|
+
stderr?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface ExecChildPromptOptions {
|
|
13
|
+
signal?: AbortSignal;
|
|
14
|
+
timeoutMs: number;
|
|
15
|
+
retryWithoutOverrides?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const OVERRIDE_FAILURE_SUBJECT = /\b(model|provider|thinking)\b/i;
|
|
19
|
+
const OVERRIDE_FAILURE_REASON = /\b(not found|unknown|invalid|unsupported|unavailable|unrecognized|no match|no matches|cannot resolve|failed to resolve)\b/i;
|
|
20
|
+
|
|
21
|
+
function normalizedModelOverride(config: ChildLlmConfig): string | undefined {
|
|
22
|
+
const trimmed = config.llmModelOverride?.trim();
|
|
23
|
+
return trimmed ? trimmed : undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function effectiveThinkingOverride(config: ChildLlmConfig): ThinkingLevel | undefined {
|
|
27
|
+
return config.llmThinkingOverride ?? (normalizedModelOverride(config) ? "off" : undefined);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function hasChildLlmOverrides(config: ChildLlmConfig): boolean {
|
|
31
|
+
return normalizedModelOverride(config) !== undefined || effectiveThinkingOverride(config) !== undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function inheritedExtensionArgs(argv: string[] = process.argv.slice(2)): string[] {
|
|
35
|
+
const args: string[] = [];
|
|
36
|
+
|
|
37
|
+
for (let i = 0; i < argv.length; i++) {
|
|
38
|
+
const current = argv[i];
|
|
39
|
+
if (current === "-e" || current === "--extension") {
|
|
40
|
+
const next = argv[i + 1];
|
|
41
|
+
if (typeof next === "string" && next.length > 0) {
|
|
42
|
+
args.push(current, next);
|
|
43
|
+
i++;
|
|
44
|
+
}
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (current.startsWith("--extension=")) {
|
|
49
|
+
args.push(current);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return args;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function buildChildPiPromptArgs(prompt: string, config: ChildLlmConfig, argv: string[] = process.argv.slice(2)): string[] {
|
|
57
|
+
const args = ["-p", "--no-session"];
|
|
58
|
+
const model = normalizedModelOverride(config);
|
|
59
|
+
const thinking = effectiveThinkingOverride(config);
|
|
60
|
+
const inheritedExtensions = inheritedExtensionArgs(argv);
|
|
61
|
+
|
|
62
|
+
if (model) args.push("--model", model);
|
|
63
|
+
if (thinking) args.push("--thinking", thinking);
|
|
64
|
+
args.push(...inheritedExtensions);
|
|
65
|
+
args.push(prompt);
|
|
66
|
+
|
|
67
|
+
return args;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function basePromptArgs(prompt: string): string[] {
|
|
71
|
+
return ["-p", "--no-session", prompt];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function shouldRetryWithoutOverridesFromText(text: string | undefined): boolean {
|
|
75
|
+
if (!text) return false;
|
|
76
|
+
return OVERRIDE_FAILURE_SUBJECT.test(text) && OVERRIDE_FAILURE_REASON.test(text);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function shouldRetryWithoutOverrides(result: PiExecResult): boolean {
|
|
80
|
+
return shouldRetryWithoutOverridesFromText(result.stderr) || shouldRetryWithoutOverridesFromText(result.stdout);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function shouldRetryWithoutOverridesForError(error: unknown): boolean {
|
|
84
|
+
return shouldRetryWithoutOverridesFromText(String(error));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function execChildPrompt(
|
|
88
|
+
pi: Pick<ExtensionAPI, "exec">,
|
|
89
|
+
prompt: string,
|
|
90
|
+
config: ChildLlmConfig,
|
|
91
|
+
options: ExecChildPromptOptions,
|
|
92
|
+
): Promise<PiExecResult> {
|
|
93
|
+
const execOptions = {
|
|
94
|
+
signal: options.signal,
|
|
95
|
+
timeout: options.timeoutMs,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
const result = await pi.exec("pi", buildChildPiPromptArgs(prompt, config), execOptions) as PiExecResult;
|
|
100
|
+
if (
|
|
101
|
+
result.code === 0 ||
|
|
102
|
+
!options.retryWithoutOverrides ||
|
|
103
|
+
!hasChildLlmOverrides(config) ||
|
|
104
|
+
!shouldRetryWithoutOverrides(result)
|
|
105
|
+
) {
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
} catch (error) {
|
|
109
|
+
if (
|
|
110
|
+
!options.retryWithoutOverrides ||
|
|
111
|
+
!hasChildLlmOverrides(config) ||
|
|
112
|
+
!shouldRetryWithoutOverridesForError(error)
|
|
113
|
+
) {
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return pi.exec("pi", basePromptArgs(prompt), execOptions) as Promise<PiExecResult>;
|
|
119
|
+
}
|
|
@@ -9,6 +9,7 @@ import { MemoryStore } from "../store/memory-store.js";
|
|
|
9
9
|
import { FLUSH_PROMPT } from "../constants.js";
|
|
10
10
|
import type { MemoryConfig } from "../types.js";
|
|
11
11
|
import { collectMessageParts } from "./message-parts.js";
|
|
12
|
+
import { execChildPrompt } from "./pi-child-process.js";
|
|
12
13
|
|
|
13
14
|
export function setupSessionFlush(
|
|
14
15
|
pi: ExtensionAPI,
|
|
@@ -42,9 +43,9 @@ export function setupSessionFlush(
|
|
|
42
43
|
].join("\n");
|
|
43
44
|
|
|
44
45
|
try {
|
|
45
|
-
await
|
|
46
|
+
await execChildPrompt(pi, flushMessage, config, {
|
|
46
47
|
signal,
|
|
47
|
-
|
|
48
|
+
timeoutMs,
|
|
48
49
|
});
|
|
49
50
|
} catch {
|
|
50
51
|
// Best-effort flush — never block shutdown
|
package/src/index.ts
CHANGED
|
@@ -179,15 +179,15 @@ export default function (pi: ExtensionAPI) {
|
|
|
179
179
|
|
|
180
180
|
// ── 7. Setup auto-consolidation (inject consolidator into stores) ──
|
|
181
181
|
store.setConsolidator(async (target, signal) => {
|
|
182
|
-
return triggerConsolidation(pi, store, target, signal, config.consolidationTimeoutMs);
|
|
182
|
+
return triggerConsolidation(pi, store, target, signal, config.consolidationTimeoutMs, target, config);
|
|
183
183
|
});
|
|
184
184
|
if (projectStore) {
|
|
185
185
|
projectStore.setConsolidator(async (target, signal) => {
|
|
186
186
|
const toolTarget = target === "memory" ? "project" : target;
|
|
187
|
-
return triggerConsolidation(pi, projectStore, target, signal, config.consolidationTimeoutMs, toolTarget);
|
|
187
|
+
return triggerConsolidation(pi, projectStore, target, signal, config.consolidationTimeoutMs, toolTarget, config);
|
|
188
188
|
});
|
|
189
189
|
}
|
|
190
|
-
registerConsolidateCommand(pi, store, config.consolidationTimeoutMs, projectStore, projectName);
|
|
190
|
+
registerConsolidateCommand(pi, store, config.consolidationTimeoutMs, projectStore, projectName, config);
|
|
191
191
|
|
|
192
192
|
// ── 8. Setup correction detection ──
|
|
193
193
|
setupCorrectionDetector(pi, store, projectStore, config, dbManager, projectName);
|
package/src/store/db.ts
CHANGED
|
@@ -25,50 +25,50 @@ type BunDatabaseInstance = {
|
|
|
25
25
|
transaction?: (fn: any) => any;
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
function
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const isBunIncompat = msg.includes('better-sqlite3 is not yet supported in bun') || msg.includes('not yet supported in bun');
|
|
37
|
-
if (!isBunIncompat) {
|
|
38
|
-
throw err;
|
|
39
|
-
}
|
|
40
|
-
if (!isBunRuntime) {
|
|
41
|
-
throw err;
|
|
28
|
+
function createBunCompatDatabaseCtor(require: NodeRequire): DatabaseCtor {
|
|
29
|
+
const bunSqlite = require('bun:sqlite') as { Database: new (dbPath: string) => BunDatabaseInstance };
|
|
30
|
+
|
|
31
|
+
return class BunCompatDatabase implements DatabaseLike {
|
|
32
|
+
private readonly db: BunDatabaseInstance;
|
|
33
|
+
|
|
34
|
+
constructor(dbPath: string) {
|
|
35
|
+
this.db = new bunSqlite.Database(dbPath);
|
|
42
36
|
}
|
|
43
37
|
|
|
44
|
-
|
|
38
|
+
prepare(sql: string): StatementLike {
|
|
39
|
+
return this.db.prepare(sql);
|
|
40
|
+
}
|
|
45
41
|
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
exec(sql: string): void {
|
|
43
|
+
this.db.exec(sql);
|
|
44
|
+
}
|
|
48
45
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
close(): void {
|
|
47
|
+
this.db.close();
|
|
48
|
+
}
|
|
52
49
|
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
transaction(fn: any): any {
|
|
51
|
+
if (!this.db.transaction) {
|
|
52
|
+
return undefined;
|
|
55
53
|
}
|
|
54
|
+
return this.db.transaction(fn);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
56
58
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
function loadDatabaseCtor(): DatabaseCtor {
|
|
60
|
+
const require = createRequire(import.meta.url);
|
|
61
|
+
const isBunRuntime = typeof (globalThis as { Bun?: unknown }).Bun !== 'undefined';
|
|
60
62
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
if (isBunRuntime) {
|
|
64
|
+
return createBunCompatDatabaseCtor(require);
|
|
65
|
+
}
|
|
64
66
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
};
|
|
67
|
+
try {
|
|
68
|
+
const mod = require('better-sqlite3') as { default?: DatabaseCtor } | DatabaseCtor;
|
|
69
|
+
return (mod as { default?: DatabaseCtor }).default ?? (mod as DatabaseCtor);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
throw err;
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
|
package/src/types.ts
CHANGED
|
@@ -8,6 +8,8 @@ export type MemoryOverflowStrategy = "auto-consolidate" | "reject" | "fifo-evict
|
|
|
8
8
|
|
|
9
9
|
export type SessionSearchVariant = "legacy" | "anchors";
|
|
10
10
|
|
|
11
|
+
export type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
|
|
12
|
+
|
|
11
13
|
export interface SessionSearchConfig {
|
|
12
14
|
/** Session search implementation variant. Default: legacy */
|
|
13
15
|
variant: SessionSearchVariant;
|
|
@@ -46,6 +48,10 @@ export interface MemoryConfig {
|
|
|
46
48
|
projectsMemoryDir?: string;
|
|
47
49
|
/** Session search configuration. Default: { variant: "legacy" } */
|
|
48
50
|
sessionSearch?: SessionSearchConfig;
|
|
51
|
+
/** Override model used for child pi -p subprocess LLM calls. Default: unset */
|
|
52
|
+
llmModelOverride?: string;
|
|
53
|
+
/** Override thinking level used for child pi -p subprocess LLM calls. Default: unset */
|
|
54
|
+
llmThinkingOverride?: ThinkingLevel;
|
|
49
55
|
/** Strategy when memory is full. Default: auto-consolidate */
|
|
50
56
|
memoryOverflowStrategy?: MemoryOverflowStrategy;
|
|
51
57
|
/** Legacy alias for memoryOverflowStrategy. Default: true */
|