@pi-unipi/unipi 2.4.2 → 2.6.0
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/CHANGELOG.md +49 -0
- package/README.md +2 -0
- package/docs/prefix-cache-architecture.md +89 -0
- package/package.json +24 -22
- package/packages/ask-user/package.json +2 -2
- package/packages/autocomplete/package.json +1 -1
- package/packages/autocomplete/src/constants.ts +2 -0
- package/packages/btw/package.json +2 -2
- package/packages/cocoindex/README.md +2 -1
- package/packages/cocoindex/index.ts +6 -13
- package/packages/cocoindex/package.json +4 -3
- package/packages/cocoindex/tools.ts +45 -11
- package/packages/compactor/README.md +4 -2
- package/packages/compactor/package.json +3 -3
- package/packages/compactor/src/session/snapshot.ts +3 -2
- package/packages/compactor/src/tools/register.ts +6 -2
- package/packages/compactor/src/tools/vcc-recall.ts +18 -3
- package/packages/core/bounded-output.ts +106 -0
- package/packages/core/constants.ts +2 -0
- package/packages/core/index.ts +1 -0
- package/packages/core/package.json +1 -1
- package/packages/core/sandbox.ts +7 -6
- package/packages/footer/package.json +2 -2
- package/packages/image/package.json +2 -2
- package/packages/info-screen/package.json +2 -2
- package/packages/input-shortcuts/package.json +2 -2
- package/packages/kanboard/package.json +2 -2
- package/packages/mcp/README.md +13 -0
- package/packages/mcp/package.json +5 -2
- package/packages/mcp/src/bridge/registry.ts +244 -137
- package/packages/mcp/src/bridge/translator.ts +96 -21
- package/packages/mcp/src/index.ts +43 -67
- package/packages/mcp/src/tui/settings-overlay.ts +3 -9
- package/packages/memory/README.md +15 -11
- package/packages/memory/bridge/mempalace_bridge.py +636 -0
- package/packages/memory/index.ts +56 -34
- package/packages/memory/mempalace.ts +169 -18
- package/packages/memory/package.json +3 -3
- package/packages/memory/storage.ts +27 -10
- package/packages/milestone/README.md +11 -3
- package/packages/milestone/hooks.ts +103 -29
- package/packages/milestone/index.ts +1 -1
- package/packages/milestone/package.json +5 -2
- package/packages/notify/package.json +2 -2
- package/packages/ralph/index.ts +12 -18
- package/packages/ralph/package.json +6 -3
- package/packages/ralph/reminder.ts +40 -0
- package/packages/ralph/tools.ts +5 -1
- package/packages/subagents/README.md +2 -0
- package/packages/subagents/package.json +1 -1
- package/packages/subagents/src/agent-manager.ts +5 -1
- package/packages/subagents/src/agent-runner.ts +2 -2
- package/packages/subagents/src/core-compat.ts +73 -0
- package/packages/subagents/src/custom-agents.ts +10 -2
- package/packages/subagents/src/index.ts +14 -3
- package/packages/subagents/src/types.ts +2 -0
- package/packages/unipi/bundled.js +1445 -686
- package/packages/updater/package.json +2 -2
- package/packages/utility/README.md +9 -0
- package/packages/utility/package.json +2 -2
- package/packages/utility/src/index.ts +48 -0
- package/packages/utility/src/lifecycle/cleanup.ts +29 -0
- package/packages/utility/src/prefix-cache.ts +263 -0
- package/packages/utility/src/types.ts +1 -1
- package/packages/web-api/package.json +2 -2
- package/packages/workflow/README.md +8 -0
- package/packages/workflow/commands.ts +16 -26
- package/packages/workflow/index.ts +165 -85
- package/packages/workflow/package.json +5 -2
|
@@ -5,15 +5,19 @@
|
|
|
5
5
|
* Naming convention: {serverName}__{toolName}
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { MCP_DEFAULTS } from "@pi-unipi/core";
|
|
8
|
+
import { MCP_DEFAULTS, boundModelOutput } from "@pi-unipi/core";
|
|
9
9
|
import type { McpTool, McpToolResult } from "../types.js";
|
|
10
10
|
import type { McpClient } from "./client.js";
|
|
11
11
|
|
|
12
|
-
/**
|
|
12
|
+
/** Client operation needed by translated tools. */
|
|
13
|
+
export type ToolCallClient = Pick<McpClient, "callTool">;
|
|
14
|
+
|
|
15
|
+
/** JSON object used as a Pi-compatible tool parameter schema. */
|
|
13
16
|
interface ToolParameters {
|
|
17
|
+
[key: string]: unknown;
|
|
14
18
|
type: "object";
|
|
15
19
|
properties: Record<string, unknown>;
|
|
16
|
-
required
|
|
20
|
+
required: unknown;
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
/** Content block returned by a pi tool */
|
|
@@ -31,6 +35,7 @@ interface PiToolResult {
|
|
|
31
35
|
/** Pi-compatible external tool */
|
|
32
36
|
export interface PiExternalTool {
|
|
33
37
|
name: string;
|
|
38
|
+
label: string;
|
|
34
39
|
description: string;
|
|
35
40
|
parameters: ToolParameters;
|
|
36
41
|
execute: (
|
|
@@ -41,6 +46,66 @@ export interface PiExternalTool {
|
|
|
41
46
|
) => Promise<PiToolResult>;
|
|
42
47
|
}
|
|
43
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Compare strings by JavaScript/Unicode UTF-16 code units.
|
|
51
|
+
*
|
|
52
|
+
* Unlike localeCompare(), this ordering does not depend on the host locale.
|
|
53
|
+
*/
|
|
54
|
+
export function compareCodeUnits(left: string, right: string): number {
|
|
55
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function isObject(value: unknown): value is Record<string, unknown> {
|
|
59
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const LITERAL_VALUE_KEYWORDS = new Set(["const", "default", "enum", "examples"]);
|
|
63
|
+
|
|
64
|
+
function canonicalizeValue(
|
|
65
|
+
value: unknown,
|
|
66
|
+
key?: string,
|
|
67
|
+
normalizeSchemaKeywords = true,
|
|
68
|
+
): unknown {
|
|
69
|
+
if (Array.isArray(value)) {
|
|
70
|
+
if (
|
|
71
|
+
normalizeSchemaKeywords &&
|
|
72
|
+
key === "required" &&
|
|
73
|
+
value.every((item) => typeof item === "string")
|
|
74
|
+
) {
|
|
75
|
+
return [...new Set(value as string[])].sort(compareCodeUnits);
|
|
76
|
+
}
|
|
77
|
+
return value.map((item) => canonicalizeValue(item, undefined, normalizeSchemaKeywords));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!isObject(value)) return value;
|
|
81
|
+
|
|
82
|
+
const canonical: Record<string, unknown> = {};
|
|
83
|
+
for (const objectKey of Object.keys(value).sort(compareCodeUnits)) {
|
|
84
|
+
// Values under these JSON Schema keywords are literal application data,
|
|
85
|
+
// not nested schemas. A property named `required` inside that data must
|
|
86
|
+
// retain array order (for example under `const`).
|
|
87
|
+
const childNormalizesSchemaKeywords =
|
|
88
|
+
normalizeSchemaKeywords && !LITERAL_VALUE_KEYWORDS.has(objectKey);
|
|
89
|
+
canonical[objectKey] = canonicalizeValue(
|
|
90
|
+
value[objectKey],
|
|
91
|
+
objectKey,
|
|
92
|
+
childNormalizesSchemaKeywords,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
return canonical;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Recursively clone and canonicalize a JSON Schema value.
|
|
100
|
+
*
|
|
101
|
+
* Object keys use locale-independent code-unit order. Arrays retain their
|
|
102
|
+
* original order, except valid `required` arrays (arrays containing only
|
|
103
|
+
* strings), which are sorted and deduplicated.
|
|
104
|
+
*/
|
|
105
|
+
export function canonicalizeJsonSchema(schema: unknown): unknown {
|
|
106
|
+
return canonicalizeValue(schema);
|
|
107
|
+
}
|
|
108
|
+
|
|
44
109
|
/**
|
|
45
110
|
* Translate an MCP tool definition to a pi-compatible external tool.
|
|
46
111
|
*
|
|
@@ -52,19 +117,21 @@ export interface PiExternalTool {
|
|
|
52
117
|
export function translateMcpTool(
|
|
53
118
|
mcpTool: McpTool,
|
|
54
119
|
serverName: string,
|
|
55
|
-
client:
|
|
120
|
+
client: ToolCallClient,
|
|
56
121
|
): PiExternalTool {
|
|
57
122
|
const separator = MCP_DEFAULTS.TOOL_NAME_SEPARATOR;
|
|
58
123
|
const toolName = `${serverName}${separator}${mcpTool.name}`;
|
|
59
124
|
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
|
|
125
|
+
// Preserve the existing Pi-facing top-level shape while cloning and
|
|
126
|
+
// canonicalizing all nested property schemas. Forwarding additional MCP
|
|
127
|
+
// top-level keywords is a separate provider-compatibility decision.
|
|
128
|
+
const inputSchema = isObject(mcpTool.inputSchema) ? mcpTool.inputSchema : {};
|
|
129
|
+
const normalizedSchema: Record<string, unknown> = {
|
|
63
130
|
type: "object",
|
|
64
|
-
properties:
|
|
65
|
-
|
|
66
|
-
required: inputSchema.required as string[] | undefined,
|
|
131
|
+
properties: isObject(inputSchema.properties) ? inputSchema.properties : {},
|
|
132
|
+
required: Array.isArray(inputSchema.required) ? inputSchema.required : [],
|
|
67
133
|
};
|
|
134
|
+
const parameters = canonicalizeJsonSchema(normalizedSchema) as ToolParameters;
|
|
68
135
|
|
|
69
136
|
const description = [
|
|
70
137
|
mcpTool.description || `MCP tool: ${mcpTool.name}`,
|
|
@@ -103,21 +170,28 @@ export function translateMcpTool(
|
|
|
103
170
|
}
|
|
104
171
|
}
|
|
105
172
|
|
|
106
|
-
if (result.isError) {
|
|
107
|
-
const errorText = blocks.map((b) => b.text).join("\n") || "Unknown error";
|
|
108
|
-
return {
|
|
109
|
-
content: [{ type: "text", text: `MCP tool error from ${serverName}: ${errorText}` }],
|
|
110
|
-
details: { error: true, server: serverName, tool: mcpTool.name },
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
|
|
114
173
|
if (blocks.length === 0) {
|
|
115
|
-
blocks.push({ type: "text", text: "(no output)" });
|
|
174
|
+
blocks.push({ type: "text", text: result.isError ? "Unknown error" : "(no output)" });
|
|
116
175
|
}
|
|
117
176
|
|
|
177
|
+
const rawText = blocks.map((block) => block.text).join("\n");
|
|
178
|
+
const wrapper = result.isError ? `MCP tool error from ${serverName}: ` : "";
|
|
179
|
+
const output = boundModelOutput(rawText, {
|
|
180
|
+
maxBytes: Math.max(1024, MCP_DEFAULTS.MAX_MODEL_OUTPUT_BYTES - Buffer.byteLength(wrapper, "utf8")),
|
|
181
|
+
artifactPrefix: `mcp-${serverName}-${mcpTool.name}`,
|
|
182
|
+
});
|
|
183
|
+
const visibleText = `${wrapper}${output.text}`;
|
|
184
|
+
|
|
118
185
|
return {
|
|
119
|
-
content:
|
|
120
|
-
details: {
|
|
186
|
+
content: [{ type: "text", text: visibleText }],
|
|
187
|
+
details: {
|
|
188
|
+
error: result.isError || undefined,
|
|
189
|
+
server: serverName,
|
|
190
|
+
tool: mcpTool.name,
|
|
191
|
+
truncated: output.truncated,
|
|
192
|
+
originalBytes: output.originalBytes,
|
|
193
|
+
artifactPath: output.artifactPath,
|
|
194
|
+
},
|
|
121
195
|
};
|
|
122
196
|
} catch (err) {
|
|
123
197
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -137,6 +211,7 @@ export function translateMcpTool(
|
|
|
137
211
|
|
|
138
212
|
return {
|
|
139
213
|
name: toolName,
|
|
214
|
+
label: toolName,
|
|
140
215
|
description,
|
|
141
216
|
parameters,
|
|
142
217
|
execute,
|
|
@@ -18,6 +18,7 @@ import type { ResolvedServer } from "./types.js";
|
|
|
18
18
|
import { loadAndResolve, getGlobalConfigDir } from "./config/manager.js";
|
|
19
19
|
import { syncCatalog, loadCatalog } from "./config/sync.js";
|
|
20
20
|
import { ServerRegistry } from "./bridge/registry.js";
|
|
21
|
+
import { compareCodeUnits } from "./bridge/translator.js";
|
|
21
22
|
import { renderMcpAddOverlay } from "./tui/add-overlay.js";
|
|
22
23
|
import { renderMcpSettingsOverlay } from "./tui/settings-overlay.js";
|
|
23
24
|
|
|
@@ -42,24 +43,32 @@ export default function (pi: ExtensionAPI) {
|
|
|
42
43
|
// Session start — load configs, start servers
|
|
43
44
|
pi.on("session_start", async (_event, ctx) => {
|
|
44
45
|
// Create registry with pi integration callbacks
|
|
46
|
+
const toolApi = pi as ExtensionAPI & {
|
|
47
|
+
registerExternalTool?: (tool: unknown) => void;
|
|
48
|
+
unregisterTool?: (toolName: string) => void;
|
|
49
|
+
unregisterExternalTool?: (toolName: string) => void;
|
|
50
|
+
};
|
|
51
|
+
const registerTool = typeof toolApi.registerTool === "function"
|
|
52
|
+
? (tool: unknown) => toolApi.registerTool(tool as Parameters<typeof toolApi.registerTool>[0])
|
|
53
|
+
: typeof toolApi.registerExternalTool === "function"
|
|
54
|
+
? (tool: unknown) => toolApi.registerExternalTool!(tool)
|
|
55
|
+
: () => {
|
|
56
|
+
throw new Error("Pi does not expose a supported MCP tool registration API");
|
|
57
|
+
};
|
|
58
|
+
const canUnregisterTools =
|
|
59
|
+
typeof toolApi.unregisterTool === "function" ||
|
|
60
|
+
typeof toolApi.unregisterExternalTool === "function";
|
|
61
|
+
const unregisterTool = typeof toolApi.unregisterTool === "function"
|
|
62
|
+
? (toolName: string) => toolApi.unregisterTool!(toolName)
|
|
63
|
+
: typeof toolApi.unregisterExternalTool === "function"
|
|
64
|
+
? (toolName: string) => toolApi.unregisterExternalTool!(toolName)
|
|
65
|
+
: () => {};
|
|
66
|
+
|
|
45
67
|
registry = new ServerRegistry({
|
|
46
68
|
emitEvent: (event, payload) => emitEvent(pi, event, payload),
|
|
47
|
-
registerTool
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
(pi as any).registerExternalTool?.(tool);
|
|
51
|
-
} catch {
|
|
52
|
-
// Tool registration may not be available in all contexts
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
unregisterTool: (toolName) => {
|
|
56
|
-
try {
|
|
57
|
-
(pi as any).unregisterTool?.(toolName) ??
|
|
58
|
-
(pi as any).unregisterExternalTool?.(toolName);
|
|
59
|
-
} catch {
|
|
60
|
-
// Ignore
|
|
61
|
-
}
|
|
62
|
-
},
|
|
69
|
+
registerTool,
|
|
70
|
+
unregisterTool,
|
|
71
|
+
canUnregisterTools,
|
|
63
72
|
});
|
|
64
73
|
|
|
65
74
|
// Load and resolve server configs
|
|
@@ -73,21 +82,13 @@ export default function (pi: ExtensionAPI) {
|
|
|
73
82
|
// Config load failure — servers will be empty, visible via /unipi:mcp-status.
|
|
74
83
|
}
|
|
75
84
|
|
|
76
|
-
//
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
// Server status visible via /unipi:mcp-status or info screen.
|
|
84
|
-
} catch (err) {
|
|
85
|
-
// Removed console.error — errors surfaced via info-screen MCP group.
|
|
86
|
-
// Server failure tracked in registry state.
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
await Promise.allSettled(startPromises);
|
|
85
|
+
// Connect/discover in parallel, then register the successful combined set
|
|
86
|
+
// after a barrier so tool order is stable across runs.
|
|
87
|
+
try {
|
|
88
|
+
await registry.startServers(servers.filter((server) => server.enabled));
|
|
89
|
+
} catch (_err) {
|
|
90
|
+
// Errors are tracked in registry state and surfaced by the info screen.
|
|
91
|
+
}
|
|
91
92
|
|
|
92
93
|
// Register info-screen group
|
|
93
94
|
const infoRegistry = getInfoRegistry();
|
|
@@ -153,16 +154,17 @@ export default function (pi: ExtensionAPI) {
|
|
|
153
154
|
`unipi:${MCP_COMMANDS.STATUS}`,
|
|
154
155
|
`unipi:${MCP_COMMANDS.RELOAD}`,
|
|
155
156
|
],
|
|
156
|
-
tools: activeServers
|
|
157
|
-
registry?.getEntry(
|
|
158
|
-
|
|
157
|
+
tools: activeServers
|
|
158
|
+
.flatMap((server) => registry?.getEntry(server.name)?.toolNames ?? [])
|
|
159
|
+
.sort(compareCodeUnits),
|
|
159
160
|
});
|
|
160
161
|
});
|
|
161
162
|
|
|
162
|
-
// Session shutdown
|
|
163
|
+
// Session shutdown tears down clients. Pi tears down this extension's tool
|
|
164
|
+
// registry itself, so do not claim per-tool unregistration here.
|
|
163
165
|
pi.on("session_shutdown", async (_event, _ctx) => {
|
|
164
166
|
if (registry) {
|
|
165
|
-
await registry.
|
|
167
|
+
await registry.disconnectAll();
|
|
166
168
|
registry = null;
|
|
167
169
|
}
|
|
168
170
|
});
|
|
@@ -305,38 +307,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
305
307
|
|
|
306
308
|
// /unipi:mcp-reload — restart all MCP servers
|
|
307
309
|
pi.registerCommand(`unipi:${MCP_COMMANDS.RELOAD}`, {
|
|
308
|
-
description: "
|
|
310
|
+
description: "Explain how to reload MCP servers safely",
|
|
309
311
|
handler: async (_args: string, ctx: ExtensionCommandContext) => {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
const all = reg.getAll();
|
|
317
|
-
if (all.length === 0) {
|
|
318
|
-
ctx.ui.notify("No MCP servers configured. Use /unipi:mcp-add to add one.", "info");
|
|
319
|
-
return;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
ctx.ui.notify(`Reloading ${all.length} MCP server(s)...`, "info");
|
|
323
|
-
|
|
324
|
-
let restarted = 0;
|
|
325
|
-
let failed = 0;
|
|
326
|
-
for (const state of all) {
|
|
327
|
-
try {
|
|
328
|
-
await reg.restartServer(state.name);
|
|
329
|
-
restarted++;
|
|
330
|
-
} catch (_err) {
|
|
331
|
-
failed++;
|
|
332
|
-
// Silently ignore — restart failure tracked in failed count.
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
const msg = failed > 0
|
|
337
|
-
? `Reloaded: ${restarted} ok, ${failed} failed`
|
|
338
|
-
: `Reloaded ${restarted} MCP server(s) successfully`;
|
|
339
|
-
ctx.ui.notify(msg, failed > 0 ? "warning" : "info");
|
|
312
|
+
// Pi 0.80 does not expose dynamic tool removal. Restarting in place can
|
|
313
|
+
// leave stale schemas in the provider-visible tool list, so require a
|
|
314
|
+
// process/extension restart to establish a clean cache epoch.
|
|
315
|
+
ctx.ui.notify("Restart Pi to reload MCP servers and tool schemas safely.", "info");
|
|
340
316
|
},
|
|
341
317
|
});
|
|
342
318
|
}
|
|
@@ -141,15 +141,9 @@ export function renderMcpSettingsOverlay(params?: {
|
|
|
141
141
|
};
|
|
142
142
|
saveMetadata(configDir, meta);
|
|
143
143
|
|
|
144
|
-
//
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
await registry.stopServer(server.name);
|
|
148
|
-
} catch {
|
|
149
|
-
// Ignore stop errors
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
144
|
+
// Pi 0.80 cannot remove a registered tool definition at runtime.
|
|
145
|
+
// Persist the setting now; the next Pi restart applies the new set as
|
|
146
|
+
// one deterministic cache epoch.
|
|
153
147
|
refreshServers();
|
|
154
148
|
refresh();
|
|
155
149
|
} catch (err) {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Persistent memory that survives across sessions. Stores facts, preferences, and decisions with semantic vector search, so the agent remembers what you told it last week.
|
|
4
4
|
|
|
5
|
-
**Primary backend: [MemPalace](https://github.com/mempalace/mempalace)** — auto-installed via `uv` on first load, with
|
|
5
|
+
**Primary backend: [MemPalace](https://github.com/mempalace/mempalace)** — auto-installed via `uv` on first load, with verified, resumable migration of existing legacy memories. If MemPalace or `uv` is unavailable, the package transparently falls back to the bundled SQLite + sqlite-vec store, so memory never hard-fails.
|
|
6
6
|
|
|
7
7
|
Two storage tiers: MemPalace (or SQLite) for vector similarity search, markdown files for a durable human-readable copy you can edit by hand. Project-scoped memories stay separate per codebase, global memories are accessible everywhere.
|
|
8
8
|
|
|
@@ -76,7 +76,7 @@ Memory has no configuration file. Storage paths are fixed:
|
|
|
76
76
|
```
|
|
77
77
|
~/.unipi/memory/ # UniPi memory root (legacy + markdown tier)
|
|
78
78
|
├── .mempalace-install # Cached MemPalace venv detection
|
|
79
|
-
├── .mempalace-migrated #
|
|
79
|
+
├── .mempalace-migrated # Versioned migration verification state
|
|
80
80
|
├── global/
|
|
81
81
|
│ ├── memory.db # Global vector DB (SQLite fallback)
|
|
82
82
|
│ └── *.md # Global memory files
|
|
@@ -94,14 +94,18 @@ On first load, the memory package:
|
|
|
94
94
|
`uv tool install mempalace` once (caches the venv python path in
|
|
95
95
|
`~/.unipi/memory/.mempalace-install`).
|
|
96
96
|
2. Pings the bridge to confirm the palace is usable.
|
|
97
|
-
3.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
97
|
+
3. Fingerprints the durable SQLite and markdown sources. If they differ from
|
|
98
|
+
the verified state in `~/.unipi/memory/.mempalace-migrated`, performs an
|
|
99
|
+
idempotent read-only migration into MemPalace drawers. Unchanged drawers are
|
|
100
|
+
skipped, new or changed memories are upserted, and the versioned state is
|
|
101
|
+
written only after every discovered record is verified in MemPalace.
|
|
102
|
+
Failed or partial migrations remain unmarked and retry on a later session.
|
|
103
|
+
Legacy files are never deleted or mutated.
|
|
104
|
+
|
|
105
|
+
Each memory operation invokes the packaged Python bridge
|
|
106
|
+
(`bridge/mempalace_bridge.py`) once via `spawnSync` (~0.5s per call). Both the
|
|
107
|
+
standalone memory package and the all-in-one umbrella tarball ship and resolve
|
|
108
|
+
this bridge. The
|
|
105
109
|
first MemPalace use on a machine also downloads the default ONNX embedding
|
|
106
110
|
model (~80MB, cached at `~/.cache/chroma/onnx_models/`).
|
|
107
111
|
|
|
@@ -109,7 +113,7 @@ model (~80MB, cached at `~/.cache/chroma/onnx_models/`).
|
|
|
109
113
|
|
|
110
114
|
```bash
|
|
111
115
|
rm ~/.unipi/memory/.mempalace-install # re-detect MemPalace next session
|
|
112
|
-
rm ~/.unipi/memory/.mempalace-migrated #
|
|
116
|
+
rm ~/.unipi/memory/.mempalace-migrated # force a full verified migration pass next session
|
|
113
117
|
```
|
|
114
118
|
|
|
115
119
|
### Backend override
|