mevoric 2.2.0 → 2.3.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/package.json +1 -1
- package/server.mjs +69 -11
package/package.json
CHANGED
package/server.mjs
CHANGED
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
} from '@modelcontextprotocol/sdk/types.js';
|
|
25
25
|
import {
|
|
26
26
|
existsSync, mkdirSync, writeFileSync, readFileSync,
|
|
27
|
-
readdirSync, unlinkSync, renameSync
|
|
27
|
+
readdirSync, unlinkSync, renameSync, appendFileSync
|
|
28
28
|
} from 'fs';
|
|
29
29
|
import { resolve, dirname } from 'path';
|
|
30
30
|
import { randomBytes, randomUUID } from 'crypto';
|
|
@@ -1237,8 +1237,34 @@ async function runCapturePrompt() {
|
|
|
1237
1237
|
const clean = stripSystemTags(prompt);
|
|
1238
1238
|
if (clean.length < 5) process.exit(0);
|
|
1239
1239
|
|
|
1240
|
+
// Append to JSONL file (one line per prompt, accumulates across the session)
|
|
1240
1241
|
const tmp = tmpdir();
|
|
1241
|
-
|
|
1242
|
+
const entry = JSON.stringify({ ts: Date.now(), prompt: clean });
|
|
1243
|
+
appendFileSync(resolve(tmp, `mevoric-prompt-${sessionId}`), entry + '\n', 'utf8');
|
|
1244
|
+
|
|
1245
|
+
// Fire-and-forget POST to /ingest so this prompt is saved even if session crashes
|
|
1246
|
+
try {
|
|
1247
|
+
let convId = '';
|
|
1248
|
+
try { convId = readFileSync(resolve(tmp, 'mevoric-convid'), 'utf8').trim(); } catch {}
|
|
1249
|
+
if (!convId) convId = sessionId;
|
|
1250
|
+
|
|
1251
|
+
const project = process.cwd().split(/[\\/]/).pop();
|
|
1252
|
+
const controller = new AbortController();
|
|
1253
|
+
const timer = setTimeout(() => controller.abort(), 3000);
|
|
1254
|
+
await fetch(`${MEMORY_SERVER_URL}/ingest`, {
|
|
1255
|
+
method: 'POST',
|
|
1256
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1257
|
+
body: JSON.stringify({
|
|
1258
|
+
messages: [{ role: 'user', content: clean.slice(0, 10000) }],
|
|
1259
|
+
user_id: 'lloyd',
|
|
1260
|
+
conversation_id: convId,
|
|
1261
|
+
project
|
|
1262
|
+
}),
|
|
1263
|
+
signal: controller.signal
|
|
1264
|
+
});
|
|
1265
|
+
clearTimeout(timer);
|
|
1266
|
+
} catch {} // Best-effort — prompt is still in JSONL file for Stop hook fallback
|
|
1267
|
+
|
|
1242
1268
|
process.exit(0);
|
|
1243
1269
|
}
|
|
1244
1270
|
|
|
@@ -1261,13 +1287,26 @@ async function runIngest() {
|
|
|
1261
1287
|
const assistantMsg = data.last_assistant_message || '';
|
|
1262
1288
|
if (!sessionId || !assistantMsg) process.exit(0);
|
|
1263
1289
|
|
|
1264
|
-
// Read user
|
|
1290
|
+
// Read ALL user prompts saved by --capture-prompt (JSONL format, one per line)
|
|
1265
1291
|
const tmp = tmpdir();
|
|
1266
1292
|
const promptPath = resolve(tmp, `mevoric-prompt-${sessionId}`);
|
|
1267
|
-
let
|
|
1293
|
+
let allPrompts = [];
|
|
1268
1294
|
try {
|
|
1269
|
-
|
|
1295
|
+
const raw = readFileSync(promptPath, 'utf8');
|
|
1296
|
+
allPrompts = raw.split('\n').filter(Boolean).map(line => {
|
|
1297
|
+
try { return JSON.parse(line); } catch { return null; }
|
|
1298
|
+
}).filter(Boolean);
|
|
1270
1299
|
} catch {}
|
|
1300
|
+
// Fallback for old plain-text format (pre-JSONL)
|
|
1301
|
+
if (allPrompts.length === 0) {
|
|
1302
|
+
try {
|
|
1303
|
+
const plain = readFileSync(promptPath, 'utf8');
|
|
1304
|
+
if (plain && plain.length >= 5) allPrompts = [{ ts: Date.now(), prompt: plain }];
|
|
1305
|
+
} catch {}
|
|
1306
|
+
}
|
|
1307
|
+
const userMsg = allPrompts.length > 0 ? allPrompts[allPrompts.length - 1].prompt : '';
|
|
1308
|
+
// Clean up temp file
|
|
1309
|
+
try { unlinkSync(promptPath); } catch {}
|
|
1271
1310
|
|
|
1272
1311
|
const cleanAssistant = stripSystemTags(assistantMsg);
|
|
1273
1312
|
if (!cleanAssistant || cleanAssistant.length < 50) process.exit(0);
|
|
@@ -1286,6 +1325,17 @@ async function runIngest() {
|
|
|
1286
1325
|
else if (prev.content) existing = { exchanges: [{ role: 'context', content: prev.content }] };
|
|
1287
1326
|
} catch {}
|
|
1288
1327
|
|
|
1328
|
+
// Store all user prompts from this session, not just the last one
|
|
1329
|
+
if (allPrompts.length > 1) {
|
|
1330
|
+
for (let i = 0; i < allPrompts.length - 1; i++) {
|
|
1331
|
+
existing.exchanges.push({
|
|
1332
|
+
timestamp: new Date(allPrompts[i].ts).toISOString(),
|
|
1333
|
+
user: allPrompts[i].prompt.slice(0, 2000),
|
|
1334
|
+
assistant: ''
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
// Final exchange has the actual assistant response
|
|
1289
1339
|
existing.exchanges.push({
|
|
1290
1340
|
timestamp: new Date().toISOString(),
|
|
1291
1341
|
user: userMsg.slice(0, 2000),
|
|
@@ -1336,8 +1386,8 @@ async function runIngest() {
|
|
|
1336
1386
|
renameSync(cpTmp, cpPath);
|
|
1337
1387
|
} catch {}
|
|
1338
1388
|
|
|
1339
|
-
// --- 3. POST to memory server /ingest (
|
|
1340
|
-
if (userMsg && cleanAssistant) {
|
|
1389
|
+
// --- 3. POST to memory server /ingest — full conversation (all prompts + final response) ---
|
|
1390
|
+
if ((allPrompts.length > 0 || userMsg) && cleanAssistant) {
|
|
1341
1391
|
// Read conversation ID from temp file (written by MCP server process)
|
|
1342
1392
|
let convId = '';
|
|
1343
1393
|
try {
|
|
@@ -1346,6 +1396,17 @@ async function runIngest() {
|
|
|
1346
1396
|
if (!convId) convId = sessionId; // fallback
|
|
1347
1397
|
|
|
1348
1398
|
try {
|
|
1399
|
+
// Build messages array: all user prompts + final assistant response
|
|
1400
|
+
const messages = [];
|
|
1401
|
+
if (allPrompts.length > 0) {
|
|
1402
|
+
for (const entry of allPrompts) {
|
|
1403
|
+
messages.push({ role: 'user', content: entry.prompt.slice(0, 10000) });
|
|
1404
|
+
}
|
|
1405
|
+
} else if (userMsg) {
|
|
1406
|
+
messages.push({ role: 'user', content: userMsg.slice(0, 10000) });
|
|
1407
|
+
}
|
|
1408
|
+
messages.push({ role: 'assistant', content: cleanAssistant.slice(0, 10000) });
|
|
1409
|
+
|
|
1349
1410
|
const project = process.cwd().split(/[\\/]/).pop();
|
|
1350
1411
|
const controller = new AbortController();
|
|
1351
1412
|
const timer = setTimeout(() => controller.abort(), 15000);
|
|
@@ -1353,10 +1414,7 @@ async function runIngest() {
|
|
|
1353
1414
|
method: 'POST',
|
|
1354
1415
|
headers: { 'Content-Type': 'application/json' },
|
|
1355
1416
|
body: JSON.stringify({
|
|
1356
|
-
messages
|
|
1357
|
-
{ role: 'user', content: userMsg.slice(0, 10000) },
|
|
1358
|
-
{ role: 'assistant', content: cleanAssistant.slice(0, 10000) }
|
|
1359
|
-
],
|
|
1417
|
+
messages,
|
|
1360
1418
|
user_id: 'lloyd',
|
|
1361
1419
|
conversation_id: convId,
|
|
1362
1420
|
project
|