feat(memorycore): add tags system, session summaries, hook injection, tag filtering, and solidify compress mode

Implement 5 interconnected memorycore enhancements:

1. Tags backend: add tags TEXT column to memories table with migration,
   JSON array storage, full CRUD support via upsertMemory/getMemory/getMemories
2. LLM auto-tag extraction: extend extraction prompt to produce tags,
   parse and validate in pipeline, create CMEM from extraction results
3. Session summary API: expose rollout_summary via new REST endpoints
   GET /api/core-memory/sessions/summaries and sessions/:id/summary
4. Hook injection: increase SESSION_START_LIMIT to 1500, add Component 5
   (Recent Sessions) to UnifiedContextBuilder with 300-char budget
5. Tag filtering: add getMemoriesByTags() with json_each() for safe
   SQL matching, wire through MCP tool, CLI --tags flag, REST ?tags= param
6. Solidify compress mode: add --type compress to solidify.md with
   getRecentMemories(), archiveMemories(), buildCompressionMetadata()

Security fixes: safeParseTags() for corrupt DB data, json_each() instead
of LIKE injection, ESCAPE clause for searchSessionsByKeyword, singleton
store in unified-context-builder.
This commit is contained in:
catlog22
2026-02-23 22:56:25 +08:00
parent ab0e25895c
commit 5cae3cb3c8
10 changed files with 582 additions and 62 deletions

View File

@@ -40,6 +40,7 @@ export interface ExtractionInput {
export interface ExtractionOutput {
raw_memory: string;
rollout_summary: string;
tags: string[];
}
export interface TranscriptFilterOptions {
@@ -289,10 +290,10 @@ export class MemoryExtractionPipeline {
* Applies secret redaction and size limit enforcement.
*
* @param llmOutput - Raw text output from the LLM
* @returns Validated ExtractionOutput with raw_memory and rollout_summary
* @returns Validated ExtractionOutput with raw_memory, rollout_summary, and tags
*/
postProcess(llmOutput: string): ExtractionOutput {
let parsed: { raw_memory?: string; rollout_summary?: string } | null = null;
let parsed: { raw_memory?: string; rollout_summary?: string; tags?: string[] } | null = null;
// Mode 1: Pure JSON
try {
@@ -333,7 +334,17 @@ export class MemoryExtractionPipeline {
rolloutSummary = rolloutSummary.substring(0, MAX_SUMMARY_CHARS);
}
return { raw_memory: rawMemory, rollout_summary: rolloutSummary };
// Extract and validate tags (fallback to empty array)
let tags: string[] = [];
if (parsed.tags && Array.isArray(parsed.tags)) {
tags = parsed.tags
.filter((t: unknown) => typeof t === 'string')
.map((t: string) => t.toLowerCase().trim())
.filter((t: string) => t.length > 0)
.slice(0, 8);
}
return { raw_memory: rawMemory, rollout_summary: rolloutSummary, tags };
}
// ========================================================================
@@ -388,6 +399,14 @@ export class MemoryExtractionPipeline {
const store = getCoreMemoryStore(this.projectPath);
store.upsertStage1Output(output);
// Create/update a core memory (CMEM) from extraction results with tags
store.upsertMemory({
id: `CMEM-EXT-${sessionId}`,
content: extracted.raw_memory,
summary: extracted.rollout_summary,
tags: extracted.tags,
});
// Sync extracted content to vector index (fire-and-forget)
this.syncExtractionToVectorIndex(output);