@ectplsm/relic 0.3.2 → 0.5.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/LICENCE.md +1 -1
- package/README.md +20 -13
- package/dist/adapters/mikoshi/client.d.ts +21 -0
- package/dist/adapters/mikoshi/client.js +84 -0
- package/dist/core/ports/index.d.ts +1 -0
- package/dist/core/ports/mikoshi.d.ts +937 -0
- package/dist/core/ports/mikoshi.js +209 -0
- package/dist/core/sync/crypto.d.ts +55 -0
- package/dist/core/sync/crypto.js +189 -0
- package/dist/core/sync/memory-hash.d.ts +35 -0
- package/dist/core/sync/memory-hash.js +61 -0
- package/dist/core/sync/memory-merge.d.ts +13 -0
- package/dist/core/sync/memory-merge.js +84 -0
- package/dist/core/sync/normalize.d.ts +10 -0
- package/dist/core/sync/normalize.js +21 -0
- package/dist/core/sync/persona-hash.d.ts +15 -0
- package/dist/core/sync/persona-hash.js +36 -0
- package/dist/core/usecases/archive-cursor-update.d.ts +6 -0
- package/dist/core/usecases/archive-cursor-update.js +18 -0
- package/dist/core/usecases/archive-pending.d.ts +18 -1
- package/dist/core/usecases/archive-pending.js +37 -2
- package/dist/core/usecases/claw-pull.d.ts +44 -0
- package/dist/core/usecases/claw-pull.js +117 -0
- package/dist/core/usecases/extract.d.ts +0 -1
- package/dist/core/usecases/extract.js +13 -27
- package/dist/core/usecases/index.d.ts +11 -3
- package/dist/core/usecases/index.js +10 -2
- package/dist/core/usecases/inject.d.ts +1 -0
- package/dist/core/usecases/inject.js +2 -0
- package/dist/core/usecases/memory-write.d.ts +5 -0
- package/dist/core/usecases/memory-write.js +26 -16
- package/dist/core/usecases/mikoshi-clone.d.ts +29 -0
- package/dist/core/usecases/mikoshi-clone.js +94 -0
- package/dist/core/usecases/mikoshi-download.d.ts +29 -0
- package/dist/core/usecases/mikoshi-download.js +94 -0
- package/dist/core/usecases/mikoshi-memory-pull.d.ts +38 -0
- package/dist/core/usecases/mikoshi-memory-pull.js +142 -0
- package/dist/core/usecases/mikoshi-memory-push.d.ts +34 -0
- package/dist/core/usecases/mikoshi-memory-push.js +105 -0
- package/dist/core/usecases/mikoshi-memory-sync.d.ts +36 -0
- package/dist/core/usecases/mikoshi-memory-sync.js +175 -0
- package/dist/core/usecases/mikoshi-pull.d.ts +44 -0
- package/dist/core/usecases/mikoshi-pull.js +124 -0
- package/dist/core/usecases/mikoshi-push.d.ts +39 -0
- package/dist/core/usecases/mikoshi-push.js +127 -0
- package/dist/core/usecases/mikoshi-status.d.ts +37 -0
- package/dist/core/usecases/mikoshi-status.js +92 -0
- package/dist/core/usecases/sync.d.ts +0 -5
- package/dist/core/usecases/sync.js +23 -61
- package/dist/interfaces/cli/commands/claw.js +134 -85
- package/dist/interfaces/cli/commands/config.js +60 -1
- package/dist/interfaces/cli/commands/create.js +17 -16
- package/dist/interfaces/cli/commands/init.js +11 -10
- package/dist/interfaces/cli/commands/list.js +9 -7
- package/dist/interfaces/cli/commands/migrate.js +8 -7
- package/dist/interfaces/cli/commands/mikoshi.d.ts +2 -0
- package/dist/interfaces/cli/commands/mikoshi.js +565 -0
- package/dist/interfaces/cli/commands/refresh-samples.js +10 -9
- package/dist/interfaces/cli/index.js +2 -0
- package/dist/interfaces/cli/output.d.ts +5 -0
- package/dist/interfaces/cli/output.js +16 -0
- package/dist/interfaces/cli/spinner.d.ts +13 -0
- package/dist/interfaces/cli/spinner.js +47 -0
- package/dist/interfaces/mcp/index.js +109 -38
- package/dist/shared/config.d.ts +25 -5
- package/dist/shared/config.js +33 -5
- package/dist/shared/engram-composer.d.ts +1 -1
- package/dist/shared/engram-composer.js +9 -5
- package/package.json +1 -1
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
2
|
+
const INTERVAL_MS = 80;
|
|
3
|
+
/**
|
|
4
|
+
* Start a CLI spinner that overwrites the current line.
|
|
5
|
+
*
|
|
6
|
+
* Falls back to a single static line when stdout is not a TTY
|
|
7
|
+
* (e.g. CI, piped output).
|
|
8
|
+
*/
|
|
9
|
+
export function startSpinner(message) {
|
|
10
|
+
const isTTY = process.stdout.isTTY ?? false;
|
|
11
|
+
let currentMessage = message;
|
|
12
|
+
if (!isTTY) {
|
|
13
|
+
process.stdout.write(`${message}\n`);
|
|
14
|
+
return {
|
|
15
|
+
update(msg) {
|
|
16
|
+
currentMessage = msg;
|
|
17
|
+
process.stdout.write(`${msg}\n`);
|
|
18
|
+
},
|
|
19
|
+
stop(finalMessage) {
|
|
20
|
+
if (finalMessage) {
|
|
21
|
+
process.stdout.write(`${finalMessage}\n`);
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
let frameIndex = 0;
|
|
27
|
+
const render = () => {
|
|
28
|
+
const frame = FRAMES[frameIndex % FRAMES.length];
|
|
29
|
+
process.stdout.write(`\r${frame} ${currentMessage}`);
|
|
30
|
+
frameIndex++;
|
|
31
|
+
};
|
|
32
|
+
render();
|
|
33
|
+
const timer = setInterval(render, INTERVAL_MS);
|
|
34
|
+
return {
|
|
35
|
+
update(msg) {
|
|
36
|
+
currentMessage = msg;
|
|
37
|
+
},
|
|
38
|
+
stop(finalMessage) {
|
|
39
|
+
clearInterval(timer);
|
|
40
|
+
// Clear the spinner line
|
|
41
|
+
process.stdout.write("\r\x1b[2K");
|
|
42
|
+
if (finalMessage) {
|
|
43
|
+
process.stdout.write(`${finalMessage}\n`);
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -3,8 +3,8 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { ArchiveSearch, ArchiveSearchEngramNotFoundError, } from "../../core/usecases/archive-search.js";
|
|
6
|
-
import { ArchivePending, ArchivePendingEngramNotFoundError, } from "../../core/usecases/archive-pending.js";
|
|
7
|
-
import { ArchiveCursorUpdate, } from "../../core/usecases/archive-cursor-update.js";
|
|
6
|
+
import { ArchivePending, ArchiveCursorCorruptedError, ArchivePendingEngramNotFoundError, } from "../../core/usecases/archive-pending.js";
|
|
7
|
+
import { ArchiveCursorUpdate, ArchiveCursorAdvanceOverflowError, } from "../../core/usecases/archive-cursor-update.js";
|
|
8
8
|
import { MemoryWrite, MemoryWriteEngramNotFoundError, } from "../../core/usecases/memory-write.js";
|
|
9
9
|
import { CreateEngram, EngramAlreadyExistsError, InvalidEngramIdError, } from "../../core/usecases/create-engram.js";
|
|
10
10
|
import { DEFAULT_SOUL, DEFAULT_IDENTITY } from "../../shared/templates.js";
|
|
@@ -13,6 +13,63 @@ import { join } from "node:path";
|
|
|
13
13
|
import { existsSync } from "node:fs";
|
|
14
14
|
import { readFile, writeFile } from "node:fs/promises";
|
|
15
15
|
import { resolveDistillationBatchSize, resolveEngramsPath, } from "../../shared/config.js";
|
|
16
|
+
async function appendLongTermAndUserProfile(engramsPath, id, long_term, user_profile) {
|
|
17
|
+
let longTermWritten = false;
|
|
18
|
+
if (long_term) {
|
|
19
|
+
const memoryMdPath = join(engramsPath, id, "MEMORY.md");
|
|
20
|
+
if (existsSync(memoryMdPath)) {
|
|
21
|
+
const existing = await readFile(memoryMdPath, "utf-8");
|
|
22
|
+
const separator = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
23
|
+
await writeFile(memoryMdPath, existing + separator + long_term + "\n", "utf-8");
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
await writeFile(memoryMdPath, long_term + "\n", "utf-8");
|
|
27
|
+
}
|
|
28
|
+
longTermWritten = true;
|
|
29
|
+
}
|
|
30
|
+
let userProfileWritten = false;
|
|
31
|
+
if (user_profile) {
|
|
32
|
+
const userMdPath = join(engramsPath, id, "USER.md");
|
|
33
|
+
await writeFile(userMdPath, user_profile + "\n", "utf-8");
|
|
34
|
+
userProfileWritten = true;
|
|
35
|
+
}
|
|
36
|
+
return { longTermWritten, userProfileWritten };
|
|
37
|
+
}
|
|
38
|
+
function assertDateCoverage(writes, expectedDates, skippedDates) {
|
|
39
|
+
const writeDates = writes.map((write) => write.date);
|
|
40
|
+
const duplicateWriteDates = findDuplicates(writeDates);
|
|
41
|
+
if (duplicateWriteDates.length > 0) {
|
|
42
|
+
throw new Error(`Duplicate write dates are not allowed: ${duplicateWriteDates.join(", ")}`);
|
|
43
|
+
}
|
|
44
|
+
const duplicateSkippedDates = findDuplicates(skippedDates);
|
|
45
|
+
if (duplicateSkippedDates.length > 0) {
|
|
46
|
+
throw new Error(`Duplicate skipped_dates are not allowed: ${duplicateSkippedDates.join(", ")}`);
|
|
47
|
+
}
|
|
48
|
+
const overlappedDates = writeDates.filter((date) => skippedDates.includes(date));
|
|
49
|
+
if (overlappedDates.length > 0) {
|
|
50
|
+
throw new Error(`Dates cannot appear in both writes and skipped_dates: ${[...new Set(overlappedDates)].join(", ")}`);
|
|
51
|
+
}
|
|
52
|
+
const expectedSet = new Set(expectedDates);
|
|
53
|
+
const coveredDates = [...writeDates, ...skippedDates];
|
|
54
|
+
const unexpectedDates = coveredDates.filter((date) => !expectedSet.has(date));
|
|
55
|
+
if (unexpectedDates.length > 0) {
|
|
56
|
+
throw new Error(`Unexpected dates: ${[...new Set(unexpectedDates)].join(", ")}`);
|
|
57
|
+
}
|
|
58
|
+
const coveredSet = new Set(coveredDates);
|
|
59
|
+
const missingDates = expectedDates.filter((date) => !coveredSet.has(date));
|
|
60
|
+
if (missingDates.length > 0) {
|
|
61
|
+
throw new Error(`Missing dates: ${missingDates.join(", ")}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function findDuplicates(values) {
|
|
65
|
+
const counts = new Map();
|
|
66
|
+
for (const value of values) {
|
|
67
|
+
counts.set(value, (counts.get(value) ?? 0) + 1);
|
|
68
|
+
}
|
|
69
|
+
return [...counts.entries()]
|
|
70
|
+
.filter(([, count]) => count > 1)
|
|
71
|
+
.map(([value]) => value);
|
|
72
|
+
}
|
|
16
73
|
const server = new McpServer({
|
|
17
74
|
name: "relic",
|
|
18
75
|
version: "0.1.0",
|
|
@@ -153,16 +210,34 @@ server.tool("relic_archive_pending", "Get un-distilled archive entries since the
|
|
|
153
210
|
],
|
|
154
211
|
};
|
|
155
212
|
}
|
|
156
|
-
const
|
|
213
|
+
const dates = [...new Set(result.entries.map((entry) => entry.date).filter(Boolean))];
|
|
214
|
+
const header = [
|
|
215
|
+
`cursor: ${result.cursor}`,
|
|
216
|
+
`total: ${result.total}`,
|
|
217
|
+
`returned: ${result.entries.length}`,
|
|
218
|
+
`remaining: ${result.remaining}`,
|
|
219
|
+
`dates: ${dates.length > 0 ? dates.join(", ") : "unknown"}`,
|
|
220
|
+
].join(" | ");
|
|
157
221
|
const body = result.entries
|
|
158
|
-
.map((
|
|
222
|
+
.map((entry, i) => {
|
|
223
|
+
const lines = [`[entry ${result.cursor + i + 1}]`];
|
|
224
|
+
if (entry.date) {
|
|
225
|
+
lines.push(`date: ${entry.date}`);
|
|
226
|
+
}
|
|
227
|
+
if (entry.summary) {
|
|
228
|
+
lines.push(`summary: ${entry.summary}`);
|
|
229
|
+
}
|
|
230
|
+
lines.push(entry.raw);
|
|
231
|
+
return lines.join("\n");
|
|
232
|
+
})
|
|
159
233
|
.join("\n\n---\n\n");
|
|
160
234
|
return {
|
|
161
235
|
content: [{ type: "text", text: `${header}\n\n${body}` }],
|
|
162
236
|
};
|
|
163
237
|
}
|
|
164
238
|
catch (err) {
|
|
165
|
-
if (err instanceof ArchivePendingEngramNotFoundError
|
|
239
|
+
if (err instanceof ArchivePendingEngramNotFoundError ||
|
|
240
|
+
err instanceof ArchiveCursorCorruptedError) {
|
|
166
241
|
return {
|
|
167
242
|
content: [{ type: "text", text: err.message }],
|
|
168
243
|
isError: true,
|
|
@@ -174,7 +249,19 @@ server.tool("relic_archive_pending", "Get un-distilled archive entries since the
|
|
|
174
249
|
// --- relic_memory_write ---
|
|
175
250
|
server.tool("relic_memory_write", "Write distilled memory to an Engram's memory file and advance the archive cursor. Call this after reviewing pending archive entries and distilling them into key insights.", {
|
|
176
251
|
id: z.string().describe("Engram ID"),
|
|
177
|
-
|
|
252
|
+
writes: z
|
|
253
|
+
.array(z.object({
|
|
254
|
+
date: z.string().describe("Archive entry date for this memory file (YYYY-MM-DD)"),
|
|
255
|
+
content: z.string().describe("Distilled memory content for memory/YYYY-MM-DD.md"),
|
|
256
|
+
}))
|
|
257
|
+
.min(0)
|
|
258
|
+
.describe("Distilled memory writes grouped by actual archive dates"),
|
|
259
|
+
expected_dates: z
|
|
260
|
+
.array(z.string())
|
|
261
|
+
.describe("All archive dates that must be explicitly covered by writes or skipped_dates"),
|
|
262
|
+
skipped_dates: z
|
|
263
|
+
.array(z.string())
|
|
264
|
+
.describe("Archive dates intentionally skipped because they produced no durable memory"),
|
|
178
265
|
count: z
|
|
179
266
|
.number()
|
|
180
267
|
.describe("Number of archive entries distilled (from relic_archive_pending returned count)"),
|
|
@@ -186,10 +273,6 @@ server.tool("relic_memory_write", "Write distilled memory to an Engram's memory
|
|
|
186
273
|
.string()
|
|
187
274
|
.optional()
|
|
188
275
|
.describe("User profile updates to write to USER.md (preferences, tendencies, work style — about the human, not the project)"),
|
|
189
|
-
date: z
|
|
190
|
-
.string()
|
|
191
|
-
.optional()
|
|
192
|
-
.describe("Date for memory file (YYYY-MM-DD, default: today)"),
|
|
193
276
|
path: z
|
|
194
277
|
.string()
|
|
195
278
|
.optional()
|
|
@@ -197,36 +280,21 @@ server.tool("relic_memory_write", "Write distilled memory to an Engram's memory
|
|
|
197
280
|
}, async (args) => {
|
|
198
281
|
const engramsPath = await resolveEngramsPath(args.path);
|
|
199
282
|
try {
|
|
200
|
-
// Step 1: Write daily memory
|
|
201
283
|
const memoryWrite = new MemoryWrite(engramsPath);
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
await writeFile(memoryMdPath, existing + separator + args.long_term + "\n", "utf-8");
|
|
211
|
-
}
|
|
212
|
-
else {
|
|
213
|
-
await writeFile(memoryMdPath, args.long_term + "\n", "utf-8");
|
|
214
|
-
}
|
|
215
|
-
longTermWritten = true;
|
|
216
|
-
}
|
|
217
|
-
// Step 2.5: Write USER.md if user_profile is provided
|
|
218
|
-
let userProfileWritten = false;
|
|
219
|
-
if (args.user_profile) {
|
|
220
|
-
const userMdPath = join(engramsPath, args.id, "USER.md");
|
|
221
|
-
await writeFile(userMdPath, args.user_profile + "\n", "utf-8");
|
|
222
|
-
userProfileWritten = true;
|
|
223
|
-
}
|
|
224
|
-
// Step 3: Advance cursor by the number of distilled entries
|
|
284
|
+
assertDateCoverage(args.writes, args.expected_dates, args.skipped_dates);
|
|
285
|
+
const writeResults = await memoryWrite.executeBatch(args.id, args.writes);
|
|
286
|
+
const writeSummary = writeResults.length > 0
|
|
287
|
+
? writeResults
|
|
288
|
+
.map((result) => `${result.date} (${result.appended ? "appended" : "created"})`)
|
|
289
|
+
.join(", ")
|
|
290
|
+
: "no files";
|
|
291
|
+
const { longTermWritten, userProfileWritten } = await appendLongTermAndUserProfile(engramsPath, args.id, args.long_term, args.user_profile);
|
|
225
292
|
const cursorUpdate = new ArchiveCursorUpdate(engramsPath);
|
|
226
293
|
const cursorResult = await cursorUpdate.execute(args.id, args.count);
|
|
227
|
-
const parts = [
|
|
228
|
-
|
|
229
|
-
|
|
294
|
+
const parts = [`Memory written to ${writeSummary}.`];
|
|
295
|
+
if (args.skipped_dates.length > 0) {
|
|
296
|
+
parts.push(`Skipped dates: ${args.skipped_dates.join(", ")}.`);
|
|
297
|
+
}
|
|
230
298
|
if (longTermWritten) {
|
|
231
299
|
parts.push("Long-term memory (MEMORY.md) updated.");
|
|
232
300
|
}
|
|
@@ -244,7 +312,10 @@ server.tool("relic_memory_write", "Write distilled memory to an Engram's memory
|
|
|
244
312
|
};
|
|
245
313
|
}
|
|
246
314
|
catch (err) {
|
|
247
|
-
if (err instanceof MemoryWriteEngramNotFoundError
|
|
315
|
+
if (err instanceof MemoryWriteEngramNotFoundError ||
|
|
316
|
+
err instanceof ArchiveCursorAdvanceOverflowError ||
|
|
317
|
+
err instanceof ArchiveCursorCorruptedError ||
|
|
318
|
+
err instanceof Error) {
|
|
248
319
|
return {
|
|
249
320
|
content: [{ type: "text", text: err.message }],
|
|
250
321
|
isError: true,
|
package/dist/shared/config.d.ts
CHANGED
|
@@ -5,21 +5,27 @@ declare const CONFIG_PATH: string;
|
|
|
5
5
|
export declare const RelicConfigSchema: z.ZodObject<{
|
|
6
6
|
/** Engramデータの格納ディレクトリ */
|
|
7
7
|
engramsPath: z.ZodDefault<z.ZodString>;
|
|
8
|
-
/** Mikoshi APIのベースURL
|
|
9
|
-
mikoshiUrl: z.
|
|
8
|
+
/** Mikoshi APIのベースURL */
|
|
9
|
+
mikoshiUrl: z.ZodDefault<z.ZodString>;
|
|
10
|
+
/** Mikoshi APIキー */
|
|
11
|
+
mikoshiApiKey: z.ZodOptional<z.ZodString>;
|
|
12
|
+
/** Mikoshi memory 暗号化パスフレーズ */
|
|
13
|
+
mikoshiPassphrase: z.ZodOptional<z.ZodString>;
|
|
10
14
|
/** --engram 未指定時に召喚するデフォルトEngram ID */
|
|
11
15
|
defaultEngram: z.ZodOptional<z.ZodString>;
|
|
12
16
|
/** claw inject/extract/sync で使うClawディレクトリ (default: ~/.openclaw) */
|
|
13
17
|
clawPath: z.ZodOptional<z.ZodString>;
|
|
14
18
|
/** システムプロンプトに含める直近メモリエントリ数 (default: 2) */
|
|
15
19
|
memoryWindowSize: z.ZodDefault<z.ZodNumber>;
|
|
16
|
-
/** 一度の記憶蒸留で扱う archive エントリ数 (default:
|
|
20
|
+
/** 一度の記憶蒸留で扱う archive エントリ数 (default: 100) */
|
|
17
21
|
distillationBatchSize: z.ZodDefault<z.ZodNumber>;
|
|
18
22
|
}, "strip", z.ZodTypeAny, {
|
|
19
23
|
memoryWindowSize: number;
|
|
20
24
|
distillationBatchSize: number;
|
|
21
25
|
engramsPath: string;
|
|
22
|
-
mikoshiUrl
|
|
26
|
+
mikoshiUrl: string;
|
|
27
|
+
mikoshiApiKey?: string | undefined;
|
|
28
|
+
mikoshiPassphrase?: string | undefined;
|
|
23
29
|
defaultEngram?: string | undefined;
|
|
24
30
|
clawPath?: string | undefined;
|
|
25
31
|
}, {
|
|
@@ -27,6 +33,8 @@ export declare const RelicConfigSchema: z.ZodObject<{
|
|
|
27
33
|
distillationBatchSize?: number | undefined;
|
|
28
34
|
engramsPath?: string | undefined;
|
|
29
35
|
mikoshiUrl?: string | undefined;
|
|
36
|
+
mikoshiApiKey?: string | undefined;
|
|
37
|
+
mikoshiPassphrase?: string | undefined;
|
|
30
38
|
defaultEngram?: string | undefined;
|
|
31
39
|
clawPath?: string | undefined;
|
|
32
40
|
}>;
|
|
@@ -72,7 +80,19 @@ export declare function resolveClawPath(cliOverride?: string): Promise<string |
|
|
|
72
80
|
export declare function resolveMemoryWindowSize(): Promise<number>;
|
|
73
81
|
/**
|
|
74
82
|
* 記憶蒸留のバッチ件数を解決する。
|
|
75
|
-
* 優先順位: config.distillationBatchSize >
|
|
83
|
+
* 優先順位: config.distillationBatchSize > 100 (デフォルト)
|
|
76
84
|
*/
|
|
77
85
|
export declare function resolveDistillationBatchSize(): Promise<number>;
|
|
86
|
+
/**
|
|
87
|
+
* Mikoshi URLを解決する。
|
|
88
|
+
*/
|
|
89
|
+
export declare function resolveMikoshiUrl(): Promise<string>;
|
|
90
|
+
/**
|
|
91
|
+
* Mikoshi APIキーを解決する。未設定時はundefined。
|
|
92
|
+
*/
|
|
93
|
+
export declare function resolveMikoshiApiKey(): Promise<string | undefined>;
|
|
94
|
+
/**
|
|
95
|
+
* Mikoshi パスフレーズを解決する。未設定時はundefined。
|
|
96
|
+
*/
|
|
97
|
+
export declare function resolveMikoshiPassphrase(): Promise<string | undefined>;
|
|
78
98
|
export { CONFIG_PATH, RELIC_DIR, TEMPLATES_DIR };
|
package/dist/shared/config.js
CHANGED
|
@@ -12,16 +12,20 @@ const CONFIG_PATH = join(RELIC_DIR, "config.json");
|
|
|
12
12
|
export const RelicConfigSchema = z.object({
|
|
13
13
|
/** Engramデータの格納ディレクトリ */
|
|
14
14
|
engramsPath: z.string().default(join(homedir(), ".relic", "engrams")),
|
|
15
|
-
/** Mikoshi APIのベースURL
|
|
16
|
-
mikoshiUrl: z.string().
|
|
15
|
+
/** Mikoshi APIのベースURL */
|
|
16
|
+
mikoshiUrl: z.string().default("https://mikoshi.ectplsm.com"),
|
|
17
|
+
/** Mikoshi APIキー */
|
|
18
|
+
mikoshiApiKey: z.string().optional(),
|
|
19
|
+
/** Mikoshi memory 暗号化パスフレーズ */
|
|
20
|
+
mikoshiPassphrase: z.string().optional(),
|
|
17
21
|
/** --engram 未指定時に召喚するデフォルトEngram ID */
|
|
18
22
|
defaultEngram: z.string().optional(),
|
|
19
23
|
/** claw inject/extract/sync で使うClawディレクトリ (default: ~/.openclaw) */
|
|
20
24
|
clawPath: z.string().optional(),
|
|
21
25
|
/** システムプロンプトに含める直近メモリエントリ数 (default: 2) */
|
|
22
26
|
memoryWindowSize: z.number().int().min(1).default(2),
|
|
23
|
-
/** 一度の記憶蒸留で扱う archive エントリ数 (default:
|
|
24
|
-
distillationBatchSize: z.number().int().min(1).default(
|
|
27
|
+
/** 一度の記憶蒸留で扱う archive エントリ数 (default: 100) */
|
|
28
|
+
distillationBatchSize: z.number().int().min(1).default(100),
|
|
25
29
|
});
|
|
26
30
|
/**
|
|
27
31
|
* 設定ファイルを読み込む。存在しなければデフォルト値を返す。
|
|
@@ -159,11 +163,35 @@ export async function resolveMemoryWindowSize() {
|
|
|
159
163
|
}
|
|
160
164
|
/**
|
|
161
165
|
* 記憶蒸留のバッチ件数を解決する。
|
|
162
|
-
* 優先順位: config.distillationBatchSize >
|
|
166
|
+
* 優先順位: config.distillationBatchSize > 100 (デフォルト)
|
|
163
167
|
*/
|
|
164
168
|
export async function resolveDistillationBatchSize() {
|
|
165
169
|
await ensureInitialized();
|
|
166
170
|
const config = await loadConfig();
|
|
167
171
|
return config.distillationBatchSize;
|
|
168
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Mikoshi URLを解決する。
|
|
175
|
+
*/
|
|
176
|
+
export async function resolveMikoshiUrl() {
|
|
177
|
+
await ensureInitialized();
|
|
178
|
+
const config = await loadConfig();
|
|
179
|
+
return config.mikoshiUrl;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Mikoshi APIキーを解決する。未設定時はundefined。
|
|
183
|
+
*/
|
|
184
|
+
export async function resolveMikoshiApiKey() {
|
|
185
|
+
await ensureInitialized();
|
|
186
|
+
const config = await loadConfig();
|
|
187
|
+
return config.mikoshiApiKey;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Mikoshi パスフレーズを解決する。未設定時はundefined。
|
|
191
|
+
*/
|
|
192
|
+
export async function resolveMikoshiPassphrase() {
|
|
193
|
+
await ensureInitialized();
|
|
194
|
+
const config = await loadConfig();
|
|
195
|
+
return config.mikoshiPassphrase;
|
|
196
|
+
}
|
|
169
197
|
export { CONFIG_PATH, RELIC_DIR, TEMPLATES_DIR };
|
|
@@ -9,7 +9,7 @@ export interface ComposeOptions {
|
|
|
9
9
|
currentDate?: string;
|
|
10
10
|
/** システムプロンプトに含める直近メモリエントリ数(デフォルト: 2) */
|
|
11
11
|
memoryWindowSize?: number;
|
|
12
|
-
/** 一度の記憶蒸留で扱う archive エントリ数(デフォルト:
|
|
12
|
+
/** 一度の記憶蒸留で扱う archive エントリ数(デフォルト: 100) */
|
|
13
13
|
distillationBatchSize?: number;
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
@@ -46,7 +46,7 @@ export function composeEngram(files, options) {
|
|
|
46
46
|
*/
|
|
47
47
|
function composeRelicSection(meta, distillationBatchSize, currentDate) {
|
|
48
48
|
const today = currentDate ?? new Date().toISOString().split("T")[0];
|
|
49
|
-
const batchSize = distillationBatchSize ??
|
|
49
|
+
const batchSize = distillationBatchSize ?? 100;
|
|
50
50
|
return `# Relic System
|
|
51
51
|
|
|
52
52
|
- engramId: ${meta.id}
|
|
@@ -68,16 +68,20 @@ To recall past context, use \`relic_archive_search\` to search your archive by k
|
|
|
68
68
|
|
|
69
69
|
When the user asks you to organize or distill memories:
|
|
70
70
|
1. Call \`relic_archive_pending\` **once** to get un-distilled session entries (up to ${batchSize})
|
|
71
|
-
2. Review and
|
|
72
|
-
|
|
71
|
+
2. Review the archive entry dates and group the distilled memory by the **actual archive entry date**, not the distillation date
|
|
72
|
+
3. Distill them into:
|
|
73
|
+
- **writes**: one item per date, each with \`date\` and \`content\`, for \`memory/YYYY-MM-DD.md\`
|
|
74
|
+
- **expected_dates**: every archive date present in the pending batch
|
|
75
|
+
- **skipped_dates**: dates that had pending entries but produced no durable memory worth saving
|
|
73
76
|
- **long_term** (optional): only the most important, enduring facts for \`MEMORY.md\` (e.g. project architecture decisions, key constraints, hard rules)
|
|
74
77
|
- **user_profile** (optional): full updated user profile for \`USER.md\` (preferences, tendencies, work style — about the human, not the project)
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
4. Call \`relic_memory_write\` with \`writes\`, \`expected_dates\`, \`skipped_dates\`, \`count\`, and optionally \`long_term\` and/or \`user_profile\`
|
|
79
|
+
5. If \`remaining > 0\`, inform the user how many entries are still pending — do NOT fetch more automatically
|
|
77
80
|
|
|
78
81
|
**Important:**
|
|
79
82
|
- Write distilled memories in the **same language the user is using** in the current conversation
|
|
80
83
|
- Do NOT loop or repeat the distillation process — one round per user request
|
|
84
|
+
- Every archive date in the pending batch must appear in exactly one of \`writes.date\` or \`skipped_dates\`
|
|
81
85
|
- \`long_term\` should be highly selective — only facts that matter across all future sessions (objective facts and rules)
|
|
82
86
|
- \`user_profile\` is a complete replacement, not an append — write the full profile each time you update it (user tendencies, preferences, work style)
|
|
83
87
|
- Distilled memories are loaded into your prompt on future sessions`;
|