fix: portal stability, session list DB fallback, and UX polish

- Free stale Memind listeners on 8081 before startup and exit cleanly on EADDRINUSE
- Backfill owned sessions missing from Goose via h5_conversation_messages summaries
- Strip Memind task orchestration prefixes from user-facing chat text
- Repair missing public docx links before release MindSpace link checks

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-07-05 11:26:34 +08:00
parent bdeab23b83
commit 00a00a1f69
9 changed files with 278 additions and 44 deletions
+59
View File
@@ -6,6 +6,7 @@ import {
normalizeApiUrl,
resolveChatCompletionsUrl,
} from './llm-providers.mjs';
import { deriveUserFacingText } from './conversation-display.mjs';
const httpsDispatcher = new Agent({
connect: { rejectUnauthorized: false },
@@ -419,11 +420,69 @@ export function createConversationMemoryService(pool, options = {}) {
}));
}
async function loadSessionListSummaries(userId, sessionIds = []) {
if (!isEnabled() || !pool || !userId || !Array.isArray(sessionIds) || sessionIds.length === 0) {
return new Map();
}
const ids = [...new Set(sessionIds.filter(Boolean))];
if (!ids.length) return new Map();
const placeholders = ids.map(() => '?').join(', ');
const [rows] = await pool.query(
`SELECT agent_session_id, role, text, raw_json, sequence_no, created_at, updated_at
FROM h5_conversation_messages
WHERE user_id = ? AND agent_session_id IN (${placeholders})
ORDER BY sequence_no ASC, created_at ASC`,
[userId, ...ids],
);
const buckets = new Map();
for (const row of rows) {
const sessionId = String(row.agent_session_id ?? '').trim();
if (!sessionId) continue;
if (!buckets.has(sessionId)) {
buckets.set(sessionId, { messages: [], maxUpdatedAt: 0 });
}
const bucket = buckets.get(sessionId);
bucket.messages.push(row);
bucket.maxUpdatedAt = Math.max(
bucket.maxUpdatedAt,
Number(row.updated_at ?? row.created_at ?? 0),
);
}
const summaries = new Map();
for (const sessionId of ids) {
const bucket = buckets.get(sessionId);
if (!bucket || bucket.messages.length === 0) continue;
const firstUser = bucket.messages.find((row) => String(row.role ?? '') === 'user');
let title = '';
if (firstUser) {
let text = String(firstUser.text ?? '').trim();
if (firstUser.raw_json) {
try {
const parsed = JSON.parse(firstUser.raw_json);
text = extractConversationMessageText(parsed) || text;
} catch {
// keep text column fallback
}
}
title = deriveUserFacingText(text).slice(0, 80);
}
summaries.set(sessionId, {
title: title || sessionId,
messageCount: bucket.messages.length,
updatedAt: bucket.maxUpdatedAt
? new Date(bucket.maxUpdatedAt).toISOString()
: undefined,
});
}
return summaries;
}
return {
isEnabled,
saveConversationMessages,
analyzeUser,
saveAndAnalyze,
listMemories,
loadSessionListSummaries,
};
}