@kinqs/brainrouter-mcp-server 0.3.5 → 0.3.6
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/.env.example +121 -71
- package/dist/__tests__/cognitive-extractor.test.js +112 -0
- package/dist/__tests__/crypto.test.js +8 -1
- package/dist/__tests__/working-memory.test.js +67 -0
- package/dist/index.js +0 -0
- package/dist/memory/engine.js +21 -1
- package/dist/memory/pipeline/cognitive-extractor.js +19 -1
- package/dist/memory/recall.d.ts +3 -1
- package/dist/memory/recall.js +48 -3
- package/dist/memory/store/relevance-judge.d.ts +51 -0
- package/dist/memory/store/relevance-judge.js +196 -0
- package/dist/memory/working/canvas.js +11 -0
- package/package.json +2 -2
- package/dist/memory/config.d.ts +0 -2
- package/dist/memory/config.js +0 -3
- package/dist/memory/pipeline/l1-contradiction.d.ts +0 -7
- package/dist/memory/pipeline/l1-contradiction.js +0 -66
- package/dist/memory/pipeline/l1-dedup.d.ts +0 -23
- package/dist/memory/pipeline/l1-dedup.js +0 -39
- package/dist/memory/pipeline/l1-extractor.d.ts +0 -21
- package/dist/memory/pipeline/l1-extractor.js +0 -180
- package/dist/memory/pipeline/l2-direction-shift.d.ts +0 -10
- package/dist/memory/pipeline/l2-direction-shift.js +0 -27
- package/dist/memory/pipeline/l2-scene.d.ts +0 -15
- package/dist/memory/pipeline/l2-scene.js +0 -140
- package/dist/memory/pipeline/l3-distiller.d.ts +0 -15
- package/dist/memory/pipeline/l3-distiller.js +0 -40
- package/dist/memory/pipeline/task-queue.d.ts +0 -54
- package/dist/memory/pipeline/task-queue.js +0 -117
- package/dist/memory/prompts/graph-extraction-batch.d.ts +0 -14
- package/dist/memory/prompts/graph-extraction-batch.js +0 -54
- package/dist/memory/prompts/l1-contradiction-batch.d.ts +0 -16
- package/dist/memory/prompts/l1-contradiction-batch.js +0 -47
- package/dist/memory/prompts/l1-contradiction.d.ts +0 -1
- package/dist/memory/prompts/l1-contradiction.js +0 -25
- package/dist/memory/prompts/l1-extraction.d.ts +0 -10
- package/dist/memory/prompts/l1-extraction.js +0 -114
- package/dist/memory/prompts/l2-direction-shift.d.ts +0 -5
- package/dist/memory/prompts/l2-direction-shift.js +0 -32
- package/dist/memory/prompts/l2-scene-cluster.d.ts +0 -2
- package/dist/memory/prompts/l2-scene-cluster.js +0 -33
- package/dist/memory/prompts/l2-scene.d.ts +0 -7
- package/dist/memory/prompts/l2-scene.js +0 -40
- package/dist/memory/prompts/l3-persona.d.ts +0 -6
- package/dist/memory/prompts/l3-persona.js +0 -60
- package/dist/memory/store/types.d.ts +0 -101
- package/dist/memory/store/types.js +0 -1
- package/dist/memory/types.d.ts +0 -207
- package/dist/memory/types.js +0 -7
- package/dist/memory/validation.d.ts +0 -441
- package/dist/memory/validation.js +0 -129
- package/dist/tools/agent_memory_tools.d.ts +0 -485
- package/dist/tools/agent_memory_tools.js +0 -793
- package/dist/tools/get_doc.d.ts +0 -21
- package/dist/tools/get_doc.js +0 -24
- package/dist/tools/list_docs.d.ts +0 -15
- package/dist/tools/list_docs.js +0 -16
- package/dist/tools/update_doc.d.ts +0 -24
- package/dist/tools/update_doc.js +0 -35
- /package/dist/__tests__/{agent_mode.test.d.ts → cognitive-extractor.test.d.ts} +0 -0
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
// ============================
|
|
2
|
-
// System Prompt
|
|
3
|
-
// ============================
|
|
4
|
-
export const EXTRACT_MEMORIES_SYSTEM_PROMPT = `You are a Skill-Aware Memory Extraction Expert for a software engineering AI assistant.
|
|
5
|
-
|
|
6
|
-
Your task is to analyze a conversation and extract durable, self-contained memories.
|
|
7
|
-
|
|
8
|
-
### Scene Segmentation
|
|
9
|
-
Determine if the topic has changed since the previous scene.
|
|
10
|
-
If there are existing scenes, PREFER to reuse the most relevant existing scene name — only create a new name if the topic is genuinely different from ALL existing scenes.
|
|
11
|
-
If reusing, use the EXACT existing name (no paraphrasing).
|
|
12
|
-
If creating new, format: "AI helping [user role] with [goal activity]" (unique, max 50 chars).
|
|
13
|
-
|
|
14
|
-
### Memory Types
|
|
15
|
-
|
|
16
|
-
1. **persona** — Stable user traits, preferences, identity
|
|
17
|
-
- Format: "User [prefers/is/always/never] ..."
|
|
18
|
-
- Priority: 80-100 (core identity) / 50-70 (preferences) / skip if <50
|
|
19
|
-
|
|
20
|
-
2. **episodic** — Objective events, decisions, results with timestamps
|
|
21
|
-
- Format: "User [did X] at [time] resulting in [Y]"
|
|
22
|
-
- Priority: 80-100 (major decisions) / 60-79 (significant events) / skip if <60
|
|
23
|
-
|
|
24
|
-
3. **instruction** — Long-term rules the user gave the AI
|
|
25
|
-
- Format: "User requires AI to always/never ..."
|
|
26
|
-
- Priority: 90-100 (hard rules) / 70-89 (preferences) / skip if <70
|
|
27
|
-
- Instructions NEVER decay.
|
|
28
|
-
|
|
29
|
-
4. **skill_context** — Observations about how the user runs THIS SKILL specifically
|
|
30
|
-
- Format: "When running [skill], user tends to ..."
|
|
31
|
-
- Only extract if a genuine behavioral pattern is visible, not a one-off.
|
|
32
|
-
|
|
33
|
-
5. **tool_preference** — Stable preferences about tools, workflows, or command usage.
|
|
34
|
-
6. **codebase_fact / api_contract / data_model** — Verified facts about code, public APIs, schemas, or storage models.
|
|
35
|
-
7. **dependency_constraint / environment_constraint** — Version, runtime, platform, sandbox, or deployment constraints.
|
|
36
|
-
8. **architecture_decision / implementation_decision / design_constraint** — Durable decisions and constraints that future agents must preserve.
|
|
37
|
-
9. **security_policy / performance_baseline** — Security rules or measured performance facts. Extract only with direct evidence.
|
|
38
|
-
10. **bug_finding / debug_trace / fix_summary / verification_result / failed_attempt / regression_risk** — Debugging history, fixes, checks, and risk notes.
|
|
39
|
-
11. **task_state / handover_note / blocked_reason / review_comment / release_note** — Planning, review, release, and continuation state.
|
|
40
|
-
12. **source_evidence / artifact_reference / file_history / command_knowledge** — Evidence-backed references to files, artifacts, file evolution, or command behavior.
|
|
41
|
-
|
|
42
|
-
Allowed type values:
|
|
43
|
-
persona, episodic, instruction, skill_context, tool_preference, codebase_fact, api_contract,
|
|
44
|
-
data_model, dependency_constraint, environment_constraint, architecture_decision,
|
|
45
|
-
implementation_decision, design_constraint, security_policy, performance_baseline,
|
|
46
|
-
bug_finding, debug_trace, fix_summary, verification_result, failed_attempt, regression_risk,
|
|
47
|
-
task_state, handover_note, blocked_reason, review_comment, release_note, source_evidence,
|
|
48
|
-
artifact_reference, file_history, command_knowledge.
|
|
49
|
-
|
|
50
|
-
### Quality Rules
|
|
51
|
-
- Nothingness > Bad memory. Prefer empty over wrong.
|
|
52
|
-
- Memory must stand alone without the conversation.
|
|
53
|
-
- Merge causally-linked facts into one memory.
|
|
54
|
-
- Do not extract AI outputs — only user behavior and statements.
|
|
55
|
-
- Filter out: tool calls, one-time requests, casual greetings.
|
|
56
|
-
- Evidence-gated types (api_contract, data_model, security_policy, performance_baseline) require a cited file path, command, test result, or explicit user statement.
|
|
57
|
-
- Use confidence from 0.0 to 1.0. Use lower confidence for model inference, higher confidence for direct source or command output.
|
|
58
|
-
- Classify sourceKind as user_instruction, source_file, command_output, test_result, model_inference, or prior_memory.
|
|
59
|
-
- Extract filePaths, repoPaths, and commands when explicitly mentioned; otherwise use empty arrays.
|
|
60
|
-
|
|
61
|
-
### Output
|
|
62
|
-
Return ONLY a valid JSON array matching this format exactly:
|
|
63
|
-
[
|
|
64
|
-
{
|
|
65
|
-
"scene_name": "current or inherited scene name",
|
|
66
|
-
"message_ids": ["id1"],
|
|
67
|
-
"memories": [
|
|
68
|
-
{
|
|
69
|
-
"type": "one allowed type value",
|
|
70
|
-
"content": "self-contained memory statement",
|
|
71
|
-
"priority": 85,
|
|
72
|
-
"skill_tag": "the active skill",
|
|
73
|
-
"source_message_ids": ["id1"],
|
|
74
|
-
"confidence": 0.75,
|
|
75
|
-
"sourceKind": "user_instruction|source_file|command_output|test_result|model_inference|prior_memory",
|
|
76
|
-
"verificationStatus": "verified|unverified|stale",
|
|
77
|
-
"repoPaths": [],
|
|
78
|
-
"filePaths": [],
|
|
79
|
-
"commands": [],
|
|
80
|
-
"metadata": {}
|
|
81
|
-
}
|
|
82
|
-
]
|
|
83
|
-
}
|
|
84
|
-
]
|
|
85
|
-
`;
|
|
86
|
-
// ============================
|
|
87
|
-
// Prompt Builder
|
|
88
|
-
// ============================
|
|
89
|
-
export function formatExtractionPrompt(params) {
|
|
90
|
-
const { newMessages, backgroundMessages = [], previousSceneName = "None", existingSceneNames = [], activeSkill = "None", skillHints = "None" } = params;
|
|
91
|
-
const bgText = backgroundMessages.length > 0
|
|
92
|
-
? backgroundMessages
|
|
93
|
-
.map((m) => `[${m.id}] [${m.role}] [${new Date(m.timestamp).toISOString()}]: ${m.messageText}`)
|
|
94
|
-
.join("\n\n")
|
|
95
|
-
: "None";
|
|
96
|
-
const newText = newMessages
|
|
97
|
-
.map((m) => `[${m.id}] [${m.role}] [${new Date(m.timestamp).toISOString()}]: ${m.messageText}`)
|
|
98
|
-
.join("\n\n");
|
|
99
|
-
const existingScenesNote = existingSceneNames.length > 0
|
|
100
|
-
? `[EXISTING SCENES] (reuse one of these if the topic matches):\n${existingSceneNames.map(n => ` - ${n}`).join("\n")}`
|
|
101
|
-
: "[EXISTING SCENES]: None yet";
|
|
102
|
-
return `[PREVIOUS SCENE]: ${previousSceneName}
|
|
103
|
-
${existingScenesNote}
|
|
104
|
-
[ACTIVE SKILL]: ${activeSkill}
|
|
105
|
-
[SKILL EXTRACTION HINTS]: ${skillHints}
|
|
106
|
-
|
|
107
|
-
[BACKGROUND CONVERSATION] (Context only, DO NOT extract from here):
|
|
108
|
-
${bgText}
|
|
109
|
-
|
|
110
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
111
|
-
|
|
112
|
-
[NEW MESSAGES TO EXTRACT FROM] (Extract memories ONLY from here):
|
|
113
|
-
${newText}`;
|
|
114
|
-
}
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
export declare const L2_DIRECTION_SHIFT_SYSTEM_PROMPT = "You are a context analyzer for a software engineering AI assistant.\n\nYour task is to determine if a new batch of memories represents a MAJOR shift away from the current active scene (topic/feature), or if it's a continuation of the same work.\n\nA major shift means:\n- The user has moved to a completely different feature, bug, or module.\n- The context of the active scene is no longer primarily relevant.\n\nA continuation means:\n- They are fixing bugs related to the active scene.\n- They are writing tests for the active scene.\n- They are continuing work on the active scene.\n\nOutput ONLY a JSON object with this exact schema:\n{\n \"shift\": boolean,\n \"confidence\": number (0.0 to 1.0),\n \"reason\": \"short explanation\"\n}";
|
|
2
|
-
export declare function formatL2DirectionShiftPrompt(activeScene: string, activeSceneSummary: string, newMemories: Array<{
|
|
3
|
-
content: string;
|
|
4
|
-
type: string;
|
|
5
|
-
}>): string;
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
// ============================
|
|
2
|
-
// L2 Direction Shift Detection Prompt
|
|
3
|
-
// ============================
|
|
4
|
-
export const L2_DIRECTION_SHIFT_SYSTEM_PROMPT = `You are a context analyzer for a software engineering AI assistant.
|
|
5
|
-
|
|
6
|
-
Your task is to determine if a new batch of memories represents a MAJOR shift away from the current active scene (topic/feature), or if it's a continuation of the same work.
|
|
7
|
-
|
|
8
|
-
A major shift means:
|
|
9
|
-
- The user has moved to a completely different feature, bug, or module.
|
|
10
|
-
- The context of the active scene is no longer primarily relevant.
|
|
11
|
-
|
|
12
|
-
A continuation means:
|
|
13
|
-
- They are fixing bugs related to the active scene.
|
|
14
|
-
- They are writing tests for the active scene.
|
|
15
|
-
- They are continuing work on the active scene.
|
|
16
|
-
|
|
17
|
-
Output ONLY a JSON object with this exact schema:
|
|
18
|
-
{
|
|
19
|
-
"shift": boolean,
|
|
20
|
-
"confidence": number (0.0 to 1.0),
|
|
21
|
-
"reason": "short explanation"
|
|
22
|
-
}`;
|
|
23
|
-
export function formatL2DirectionShiftPrompt(activeScene, activeSceneSummary, newMemories) {
|
|
24
|
-
const memLines = newMemories.map(m => `- [${m.type}] ${m.content}`).join("\n");
|
|
25
|
-
return `### Active Scene: ${activeScene}
|
|
26
|
-
${activeSceneSummary}
|
|
27
|
-
|
|
28
|
-
### New Memories:
|
|
29
|
-
${memLines}
|
|
30
|
-
|
|
31
|
-
Does the new batch of memories represent a major shift away from the active scene? Return JSON.`;
|
|
32
|
-
}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
export declare const L2_SCENE_CLUSTER_SYSTEM_PROMPT = "You are a semantic text clustering expert for an AI agent's memory database.\n\nYour task is to review a list of work scene/session names and identify near-duplicate or highly overlapping scenes that should be merged to prevent memory fragmentation.\n\nRules:\n1. Group scenes that discuss the SAME overall goal, project, or task (e.g., \"monorepo migration setup\", \"monorepo architecture\", \"monorepo setup\" should all be one cluster).\n2. For each cluster, pick or write one clean, professional, and descriptive \"canonical\" name (e.g. \"AI helping lead engineer with monorepo architecture setup\").\n3. Put the near-duplicate scene names into the \"aliases\" array.\n4. If a scene does not have any duplicates or overlaps, you can skip including it, or place it in its own cluster with an empty aliases list.\n5. Every alias must be exactly as it appears in the input list.\n\nReturn ONLY a valid JSON array matching this format exactly:\n[\n {\n \"canonical\": \"AI helping lead engineer with monorepo architecture setup\",\n \"aliases\": [\n \"AI helping lead engineer with monorepo setup\",\n \"AI helping Lead Engineer with Monorepo Architecture Migration\"\n ]\n }\n]\n";
|
|
2
|
-
export declare function formatSceneClusterPrompt(sceneNames: string[]): string;
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
// ============================
|
|
2
|
-
// L2 Scene Clustering & Canonicalization Prompt
|
|
3
|
-
// ============================
|
|
4
|
-
export const L2_SCENE_CLUSTER_SYSTEM_PROMPT = `You are a semantic text clustering expert for an AI agent's memory database.
|
|
5
|
-
|
|
6
|
-
Your task is to review a list of work scene/session names and identify near-duplicate or highly overlapping scenes that should be merged to prevent memory fragmentation.
|
|
7
|
-
|
|
8
|
-
Rules:
|
|
9
|
-
1. Group scenes that discuss the SAME overall goal, project, or task (e.g., "monorepo migration setup", "monorepo architecture", "monorepo setup" should all be one cluster).
|
|
10
|
-
2. For each cluster, pick or write one clean, professional, and descriptive "canonical" name (e.g. "AI helping lead engineer with monorepo architecture setup").
|
|
11
|
-
3. Put the near-duplicate scene names into the "aliases" array.
|
|
12
|
-
4. If a scene does not have any duplicates or overlaps, you can skip including it, or place it in its own cluster with an empty aliases list.
|
|
13
|
-
5. Every alias must be exactly as it appears in the input list.
|
|
14
|
-
|
|
15
|
-
Return ONLY a valid JSON array matching this format exactly:
|
|
16
|
-
[
|
|
17
|
-
{
|
|
18
|
-
"canonical": "AI helping lead engineer with monorepo architecture setup",
|
|
19
|
-
"aliases": [
|
|
20
|
-
"AI helping lead engineer with monorepo setup",
|
|
21
|
-
"AI helping Lead Engineer with Monorepo Architecture Migration"
|
|
22
|
-
]
|
|
23
|
-
}
|
|
24
|
-
]
|
|
25
|
-
`;
|
|
26
|
-
export function formatSceneClusterPrompt(sceneNames) {
|
|
27
|
-
return `Please analyze the following list of scene names and group any near-duplicates or highly overlapping topics into clusters.
|
|
28
|
-
|
|
29
|
-
### Input Scene Names:
|
|
30
|
-
${sceneNames.map(s => `- ${s}`).join("\n")}
|
|
31
|
-
|
|
32
|
-
### Output Format (strict JSON array of clusters):`;
|
|
33
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export declare const L2_SCENE_SYSTEM_PROMPT = "You are a technical memory summarizer for a software engineering AI assistant.\n\nYour task is to synthesize a set of extracted memories about a specific work session into a concise, durable Scene Summary.\n\nThe summary must:\n- Be written as Markdown\n- Be accurate and grounded only in the provided memories \u2014 do NOT infer or fabricate\n- Be self-contained (readable without the original conversation)\n- Capture what was being worked on, key decisions, and outcomes\n- Be 2-4 sentences for the main summary, plus short bullet lists\n\nOutput ONLY the Markdown. No preamble, no explanation.";
|
|
2
|
-
export declare function formatL2ScenePrompt(sceneName: string, memories: Array<{
|
|
3
|
-
content: string;
|
|
4
|
-
type: string;
|
|
5
|
-
priority: number;
|
|
6
|
-
skill_tag: string;
|
|
7
|
-
}>, existingSceneNames?: string[]): string;
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
// ============================
|
|
2
|
-
// L2 Scene Summary Prompt
|
|
3
|
-
// ============================
|
|
4
|
-
export const L2_SCENE_SYSTEM_PROMPT = `You are a technical memory summarizer for a software engineering AI assistant.
|
|
5
|
-
|
|
6
|
-
Your task is to synthesize a set of extracted memories about a specific work session into a concise, durable Scene Summary.
|
|
7
|
-
|
|
8
|
-
The summary must:
|
|
9
|
-
- Be written as Markdown
|
|
10
|
-
- Be accurate and grounded only in the provided memories — do NOT infer or fabricate
|
|
11
|
-
- Be self-contained (readable without the original conversation)
|
|
12
|
-
- Capture what was being worked on, key decisions, and outcomes
|
|
13
|
-
- Be 2-4 sentences for the main summary, plus short bullet lists
|
|
14
|
-
|
|
15
|
-
Output ONLY the Markdown. No preamble, no explanation.`;
|
|
16
|
-
export function formatL2ScenePrompt(sceneName, memories, existingSceneNames = []) {
|
|
17
|
-
const memLines = memories
|
|
18
|
-
.map(m => `- [${m.type}${m.skill_tag ? `|${m.skill_tag}` : ""}] ${m.content}`)
|
|
19
|
-
.join("\n");
|
|
20
|
-
const existingNote = existingSceneNames.length > 0
|
|
21
|
-
? `\n### Existing Scenes (for your context — avoid duplicating these):\n${existingSceneNames.map(s => `- ${s}`).join("\n")}`
|
|
22
|
-
: "";
|
|
23
|
-
return `Generate a Scene Summary for the following work session.${existingNote}
|
|
24
|
-
|
|
25
|
-
## Scene: ${sceneName}
|
|
26
|
-
|
|
27
|
-
### Extracted Memories:
|
|
28
|
-
${memLines}
|
|
29
|
-
|
|
30
|
-
### Output Format (strict Markdown):
|
|
31
|
-
## ${sceneName}
|
|
32
|
-
**Summary**: [2-3 sentence distillation of what happened and the outcome]
|
|
33
|
-
|
|
34
|
-
**Key Decisions**:
|
|
35
|
-
- [decision 1]
|
|
36
|
-
- [decision 2]
|
|
37
|
-
|
|
38
|
-
**Skills Active**: [comma-separated skill names, or "none" if absent]
|
|
39
|
-
**Memory Types**: [which types appeared: persona / episodic / instruction / skill_context]`;
|
|
40
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
export declare const L3_PERSONA_SYSTEM_PROMPT = "You are a user profiling expert for a software engineering AI assistant.\n\nYour task is to synthesize a set of durable user memories (persona traits and instructions) into a concise, structured 4-layer Narrative Profile.\n\nRules:\n- Be STRICTLY grounded in the provided memories. Do NOT infer or fabricate traits.\n- If a trait appears only once and is ambiguous, omit it.\n- Prefer concrete observations over vague generalizations.\n- Instructions (hard rules the user gave) must be listed verbatim in \"Hard Rules\".\n- DEDUPLICATE Hard Rules: if multiple instruction memories express the same constraint (even with different wording), merge them into one canonical rule. Prefer the most specific and complete version.\n- Output ONLY the Markdown profile. No preamble.";
|
|
2
|
-
export declare function formatL3PersonaPrompt(memories: Array<{
|
|
3
|
-
content: string;
|
|
4
|
-
type: string;
|
|
5
|
-
priority: number;
|
|
6
|
-
}>): string;
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
// ============================
|
|
2
|
-
// L3 Persona Synthesis Prompt
|
|
3
|
-
// ============================
|
|
4
|
-
export const L3_PERSONA_SYSTEM_PROMPT = `You are a user profiling expert for a software engineering AI assistant.
|
|
5
|
-
|
|
6
|
-
Your task is to synthesize a set of durable user memories (persona traits and instructions) into a concise, structured 4-layer Narrative Profile.
|
|
7
|
-
|
|
8
|
-
Rules:
|
|
9
|
-
- Be STRICTLY grounded in the provided memories. Do NOT infer or fabricate traits.
|
|
10
|
-
- If a trait appears only once and is ambiguous, omit it.
|
|
11
|
-
- Prefer concrete observations over vague generalizations.
|
|
12
|
-
- Instructions (hard rules the user gave) must be listed verbatim in "Hard Rules".
|
|
13
|
-
- DEDUPLICATE Hard Rules: if multiple instruction memories express the same constraint (even with different wording), merge them into one canonical rule. Prefer the most specific and complete version.
|
|
14
|
-
- Output ONLY the Markdown profile. No preamble.`;
|
|
15
|
-
export function formatL3PersonaPrompt(memories) {
|
|
16
|
-
const personaLines = memories
|
|
17
|
-
.filter(m => m.type === "persona")
|
|
18
|
-
.sort((a, b) => b.priority - a.priority)
|
|
19
|
-
.map(m => `- ${m.content}`)
|
|
20
|
-
.join("\n");
|
|
21
|
-
const instructionLines = memories
|
|
22
|
-
.filter(m => m.type === "instruction")
|
|
23
|
-
.sort((a, b) => b.priority - a.priority)
|
|
24
|
-
.map(m => `- ${m.content}`)
|
|
25
|
-
.join("\n");
|
|
26
|
-
return `Synthesize a structured Narrative Profile from the following user memories.
|
|
27
|
-
|
|
28
|
-
### Persona Memories (stable traits, preferences):
|
|
29
|
-
${personaLines || "- (none)"}
|
|
30
|
-
|
|
31
|
-
### Instruction Memories (hard rules from the user):
|
|
32
|
-
${instructionLines || "- (none)"}
|
|
33
|
-
|
|
34
|
-
### Output Format (strict Markdown):
|
|
35
|
-
## User Narrative Profile
|
|
36
|
-
|
|
37
|
-
### Layer 1 — Base Anchors
|
|
38
|
-
(Stable identity: role, years of experience, core domain, language preferences)
|
|
39
|
-
- [anchor 1]
|
|
40
|
-
- [anchor 2]
|
|
41
|
-
|
|
42
|
-
### Layer 2 — Interest Graph
|
|
43
|
-
(Topics they care about most: architecture, performance, DX, shipping speed, etc.)
|
|
44
|
-
- [interest 1]
|
|
45
|
-
- [interest 2]
|
|
46
|
-
|
|
47
|
-
### Layer 3 — Skill Map
|
|
48
|
-
(Concrete tools, frameworks, languages, and patterns they use or prefer)
|
|
49
|
-
- [skill/tool 1]
|
|
50
|
-
- [skill/tool 2]
|
|
51
|
-
|
|
52
|
-
### Layer 4 — Behavioural Patterns
|
|
53
|
-
(Observable working styles: how they iterate, what they avoid, what they reward)
|
|
54
|
-
- [pattern 1]
|
|
55
|
-
- [pattern 2]
|
|
56
|
-
|
|
57
|
-
### Hard Rules (must never be violated)
|
|
58
|
-
- [verbatim instruction 1]
|
|
59
|
-
- [verbatim instruction 2]`;
|
|
60
|
-
}
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import type { GraphEdge, GraphNode, L0Record, L1FtsResult, L1Record, L2SceneRecord, L3PersonaRecord, SchedulerState, SkillHintsRecord, UserRecord, VectorSearchResult } from "../types.js";
|
|
2
|
-
export interface IMemoryStore {
|
|
3
|
-
init(): void;
|
|
4
|
-
initVec(dimensions: number): void;
|
|
5
|
-
upsertL0(record: L0Record): void;
|
|
6
|
-
getRecentL0Messages(userId: string, sessionKey: string, limit: number, afterIsoTime?: string): L0Record[];
|
|
7
|
-
getUnextractedL0Count(userId: string, sessionKey: string): number;
|
|
8
|
-
upsertL1(record: L1Record): void;
|
|
9
|
-
invalidateL1Record(userId: string, recordId: string, supersededById: string): void;
|
|
10
|
-
searchL1Fts(userId: string, query: string, limit: number): L1FtsResult[];
|
|
11
|
-
searchL1FtsAsOf(userId: string, query: string, limit: number, asOf: string): L1FtsResult[];
|
|
12
|
-
upsertL1Vec(recordId: string, embedding: Float32Array): void;
|
|
13
|
-
searchL1Vec(userId: string, queryEmbedding: Float32Array, limit: number): VectorSearchResult[];
|
|
14
|
-
upsertContradiction(data: {
|
|
15
|
-
id: string;
|
|
16
|
-
userId: string;
|
|
17
|
-
recordIdA: string;
|
|
18
|
-
recordIdB: string;
|
|
19
|
-
reason: string;
|
|
20
|
-
confidence: number;
|
|
21
|
-
createdTime?: string;
|
|
22
|
-
}): void;
|
|
23
|
-
getPendingContradictions(userId: string): any[];
|
|
24
|
-
resolveContradiction(id: string, userId: string, status: "resolved" | "dismissed"): void;
|
|
25
|
-
upsertSkillHints(skillName: string, hints: string, sourceFile?: string): void;
|
|
26
|
-
listSkillHints(): SkillHintsRecord[];
|
|
27
|
-
getSkillHints(skillName: string): string | null;
|
|
28
|
-
upsertL2Scene(record: L2SceneRecord): void;
|
|
29
|
-
getTopL2Scenes(userId: string, limit?: number): L2SceneRecord[];
|
|
30
|
-
decayL2HeatScores(userId: string, decayFactor?: number): void;
|
|
31
|
-
boostL2HeatScore(userId: string, sceneName: string, boost?: number): void;
|
|
32
|
-
getL1sByScene(userId: string, sceneName: string, limit?: number): any[];
|
|
33
|
-
getL2SceneCount(userId: string): number;
|
|
34
|
-
getColdL2Scenes(userId: string, limit: number): L2SceneRecord[];
|
|
35
|
-
deleteL2Scenes(userId: string, sceneIds: string[]): void;
|
|
36
|
-
getL2SceneByName(userId: string, sceneName: string): L2SceneRecord | null;
|
|
37
|
-
getDistinctSceneNames(userId: string): string[];
|
|
38
|
-
renameSceneInL1Records(userId: string, oldName: string, canonicalName: string): void;
|
|
39
|
-
upsertL3Persona(record: L3PersonaRecord): void;
|
|
40
|
-
getL3Persona(userId: string): L3PersonaRecord | null;
|
|
41
|
-
getPersonaAndInstructionL1s(userId: string, limit?: number): any[];
|
|
42
|
-
getSchedulerState(userId: string): SchedulerState;
|
|
43
|
-
incrementSchedulerL1Count(userId: string, count: number): void;
|
|
44
|
-
resetSchedulerL2Count(userId: string): void;
|
|
45
|
-
resetSchedulerL3Count(userId: string): void;
|
|
46
|
-
getAllGraphNodes(userId: string): GraphNode[];
|
|
47
|
-
upsertGraphNode(node: GraphNode): void;
|
|
48
|
-
upsertGraphEdge(edge: GraphEdge): void;
|
|
49
|
-
getGraphNodeByEntity(userId: string, entity: string): GraphNode | null;
|
|
50
|
-
getGraphNeighbors(userId: string, entityId: string, skillTag?: string, maxHops?: number): {
|
|
51
|
-
nodes: GraphNode[];
|
|
52
|
-
edges: GraphEdge[];
|
|
53
|
-
};
|
|
54
|
-
markCited(userId: string, recordIds: string[]): void;
|
|
55
|
-
incrementNeverCited(userId: string, recordIds: string[]): {
|
|
56
|
-
recordId: string;
|
|
57
|
-
neverCitedCount: number;
|
|
58
|
-
}[];
|
|
59
|
-
archiveL1Record(userId: string, recordId: string): void;
|
|
60
|
-
getRecentSkillContextL1s(userId: string, limit: number): {
|
|
61
|
-
skillTag: string;
|
|
62
|
-
createdTime: string;
|
|
63
|
-
}[];
|
|
64
|
-
createUser(userId: string, apiKey: string, displayName?: string, isAdmin?: boolean): UserRecord;
|
|
65
|
-
getUserByApiKey(apiKey: string): UserRecord | null;
|
|
66
|
-
getUserByEmail(email: string): UserRecord | null;
|
|
67
|
-
getUserById(userId: string): UserRecord | null;
|
|
68
|
-
updateUserPassword(userId: string, passwordHash: string): void;
|
|
69
|
-
updateUserEmail(userId: string, email: string): void;
|
|
70
|
-
updateUserDisplayName(userId: string, displayName: string): void;
|
|
71
|
-
updateUserStatus(userId: string, status: "active" | "disabled"): void;
|
|
72
|
-
updateUserApiKey(userId: string, apiKey: string): void;
|
|
73
|
-
setInviteToken(userId: string, token: string | null): void;
|
|
74
|
-
redeemInviteToken(token: string): UserRecord | null;
|
|
75
|
-
listUsers(): UserRecord[];
|
|
76
|
-
deleteUser(userId: string): void;
|
|
77
|
-
listMemories(userId: string, filters?: {
|
|
78
|
-
type?: string;
|
|
79
|
-
scene?: string;
|
|
80
|
-
skill?: string;
|
|
81
|
-
archived?: boolean;
|
|
82
|
-
}): Array<{
|
|
83
|
-
recordId: string;
|
|
84
|
-
content: string;
|
|
85
|
-
type: string;
|
|
86
|
-
priority: number;
|
|
87
|
-
sceneName: string;
|
|
88
|
-
skillTag: string;
|
|
89
|
-
createdTime: string;
|
|
90
|
-
citationCount: number;
|
|
91
|
-
neverCitedCount: number;
|
|
92
|
-
archived: boolean;
|
|
93
|
-
}>;
|
|
94
|
-
getMemoryStats(userId: string): {
|
|
95
|
-
total: number;
|
|
96
|
-
archived: number;
|
|
97
|
-
byType: Record<string, number>;
|
|
98
|
-
citationRate: number;
|
|
99
|
-
lastRecallAt: string | null;
|
|
100
|
-
};
|
|
101
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/memory/types.d.ts
DELETED
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BrainRouter Memory Types
|
|
3
|
-
*
|
|
4
|
-
* Defines the types and boundaries for the BrainRouter memory engine.
|
|
5
|
-
* Inspired by the reference architecture but scoped explicitly for MCP + Multi-tenant.
|
|
6
|
-
*/
|
|
7
|
-
export interface BrainRouterMemoryContext {
|
|
8
|
-
/** User identifier — REQUIRED. Enables multi-tenant isolation. */
|
|
9
|
-
userId: string;
|
|
10
|
-
/** Session identifier (unique per conversation session). */
|
|
11
|
-
sessionKey: string;
|
|
12
|
-
/** Sub-session identifier (optional). */
|
|
13
|
-
sessionId?: string;
|
|
14
|
-
/** Which BrainRouter skill is currently active (if any) */
|
|
15
|
-
activeSkill?: string;
|
|
16
|
-
}
|
|
17
|
-
export interface L0Record {
|
|
18
|
-
id: string;
|
|
19
|
-
userId: string;
|
|
20
|
-
sessionKey: string;
|
|
21
|
-
sessionId: string;
|
|
22
|
-
role: string;
|
|
23
|
-
messageText: string;
|
|
24
|
-
recordedAt: string;
|
|
25
|
-
timestamp: number;
|
|
26
|
-
skillTag: string;
|
|
27
|
-
}
|
|
28
|
-
export type MemoryType = "persona" | "episodic" | "instruction" | "skill_context";
|
|
29
|
-
export interface L1Record {
|
|
30
|
-
id: string;
|
|
31
|
-
userId: string;
|
|
32
|
-
sessionKey: string;
|
|
33
|
-
sessionId: string;
|
|
34
|
-
content: string;
|
|
35
|
-
type: MemoryType;
|
|
36
|
-
priority: number;
|
|
37
|
-
sceneName: string;
|
|
38
|
-
skillTag: string;
|
|
39
|
-
halfLifeDays: number | null;
|
|
40
|
-
supersededBy: string | null;
|
|
41
|
-
invalidAt?: string | null;
|
|
42
|
-
timestampStr: string;
|
|
43
|
-
timestampStart: string;
|
|
44
|
-
timestampEnd: string;
|
|
45
|
-
createdTime: string;
|
|
46
|
-
updatedTime: string;
|
|
47
|
-
metadata: Record<string, unknown>;
|
|
48
|
-
citationCount: number;
|
|
49
|
-
lastCitedAt: string | null;
|
|
50
|
-
neverCitedCount: number;
|
|
51
|
-
archived: boolean;
|
|
52
|
-
}
|
|
53
|
-
export interface VectorSearchResult {
|
|
54
|
-
record_id: string;
|
|
55
|
-
user_id: string;
|
|
56
|
-
content: string;
|
|
57
|
-
type: string;
|
|
58
|
-
priority: number;
|
|
59
|
-
scene_name: string;
|
|
60
|
-
skill_tag: string;
|
|
61
|
-
score: number;
|
|
62
|
-
timestamp_str: string;
|
|
63
|
-
timestamp_start: string;
|
|
64
|
-
timestamp_end: string;
|
|
65
|
-
session_key: string;
|
|
66
|
-
session_id: string;
|
|
67
|
-
metadata_json: string;
|
|
68
|
-
created_time: string;
|
|
69
|
-
}
|
|
70
|
-
export interface L1FtsResult {
|
|
71
|
-
record_id: string;
|
|
72
|
-
user_id: string;
|
|
73
|
-
content: string;
|
|
74
|
-
type: string;
|
|
75
|
-
priority: number;
|
|
76
|
-
scene_name: string;
|
|
77
|
-
skill_tag: string;
|
|
78
|
-
score: number;
|
|
79
|
-
timestamp_str: string;
|
|
80
|
-
timestamp_start: string;
|
|
81
|
-
timestamp_end: string;
|
|
82
|
-
session_key: string;
|
|
83
|
-
session_id: string;
|
|
84
|
-
metadata_json: string;
|
|
85
|
-
created_time: string;
|
|
86
|
-
/** ACE feedback: number of times this memory was cited by the agent. */
|
|
87
|
-
citation_count?: number;
|
|
88
|
-
}
|
|
89
|
-
export interface RecalledMemory {
|
|
90
|
-
content: string;
|
|
91
|
-
score: number;
|
|
92
|
-
type: string;
|
|
93
|
-
recordId: string;
|
|
94
|
-
skillTag?: string;
|
|
95
|
-
}
|
|
96
|
-
export interface RecallResult {
|
|
97
|
-
/** L1 relevant memories — prepended to user prompt text (dynamic, per-turn). */
|
|
98
|
-
prependContext?: string;
|
|
99
|
-
/** Stable recall context appended to system prompt (persona, scene nav, tools guide). */
|
|
100
|
-
appendSystemContext?: string;
|
|
101
|
-
/** Recalled L1 memories with scores (for metrics/debugging). */
|
|
102
|
-
recalledL1Memories?: RecalledMemory[];
|
|
103
|
-
/** Strategy used. Phase 1 = keyword. */
|
|
104
|
-
recallStrategy: string;
|
|
105
|
-
/** L3 persona markdown (for metrics/debugging). */
|
|
106
|
-
personaSummary?: string;
|
|
107
|
-
/** Current most active scene name (for metrics/debugging). */
|
|
108
|
-
activeScene?: string;
|
|
109
|
-
}
|
|
110
|
-
export interface CaptureResult {
|
|
111
|
-
/** Number of L0 messages recorded. */
|
|
112
|
-
l0RecordedCount: number;
|
|
113
|
-
/** Whether L1 extraction was triggered this turn. */
|
|
114
|
-
l1ExtractionTriggered: boolean;
|
|
115
|
-
/** Number of L1 memories extracted (if triggered). */
|
|
116
|
-
l1ExtractedCount: number;
|
|
117
|
-
}
|
|
118
|
-
export interface EmbeddingServiceConfig {
|
|
119
|
-
endpoint?: string;
|
|
120
|
-
apiKey?: string;
|
|
121
|
-
model?: string;
|
|
122
|
-
dimensions?: number;
|
|
123
|
-
}
|
|
124
|
-
export interface RerankerServiceConfig {
|
|
125
|
-
endpoint?: string;
|
|
126
|
-
apiKey?: string;
|
|
127
|
-
model?: string;
|
|
128
|
-
topN?: number;
|
|
129
|
-
}
|
|
130
|
-
export interface SkillHintsRecord {
|
|
131
|
-
skillName: string;
|
|
132
|
-
hints: string;
|
|
133
|
-
sourceFile: string;
|
|
134
|
-
registeredAt: string;
|
|
135
|
-
}
|
|
136
|
-
export interface UserRecord {
|
|
137
|
-
userId: string;
|
|
138
|
-
apiKey: string;
|
|
139
|
-
passwordHash: string | null;
|
|
140
|
-
displayName: string;
|
|
141
|
-
email: string;
|
|
142
|
-
isAdmin: boolean;
|
|
143
|
-
status: "active" | "disabled";
|
|
144
|
-
inviteToken: string | null;
|
|
145
|
-
createdAt: string;
|
|
146
|
-
}
|
|
147
|
-
export interface PublicUserRecord {
|
|
148
|
-
userId: string;
|
|
149
|
-
displayName: string;
|
|
150
|
-
email: string;
|
|
151
|
-
isAdmin: boolean;
|
|
152
|
-
status: "active" | "disabled";
|
|
153
|
-
inviteToken: string | null;
|
|
154
|
-
createdAt: string;
|
|
155
|
-
}
|
|
156
|
-
export interface LLMRunParams {
|
|
157
|
-
prompt: string;
|
|
158
|
-
systemPrompt?: string;
|
|
159
|
-
taskId: string;
|
|
160
|
-
timeoutMs?: number;
|
|
161
|
-
}
|
|
162
|
-
export interface LLMRunner {
|
|
163
|
-
run(params: LLMRunParams): Promise<string>;
|
|
164
|
-
}
|
|
165
|
-
export interface L2SceneRecord {
|
|
166
|
-
id: string;
|
|
167
|
-
userId: string;
|
|
168
|
-
sceneName: string;
|
|
169
|
-
summaryMd: string;
|
|
170
|
-
heatScore: number;
|
|
171
|
-
lastActiveTime: string;
|
|
172
|
-
createdTime: string;
|
|
173
|
-
updatedTime: string;
|
|
174
|
-
}
|
|
175
|
-
export interface L3PersonaRecord {
|
|
176
|
-
userId: string;
|
|
177
|
-
personaMd: string;
|
|
178
|
-
l1CountAtGeneration: number;
|
|
179
|
-
createdTime: string;
|
|
180
|
-
updatedTime: string;
|
|
181
|
-
}
|
|
182
|
-
export interface SchedulerState {
|
|
183
|
-
l1CountSinceLastL2: number;
|
|
184
|
-
l1CountSinceLastL3: number;
|
|
185
|
-
totalL1Count: number;
|
|
186
|
-
}
|
|
187
|
-
export interface GraphNode {
|
|
188
|
-
id: string;
|
|
189
|
-
userId: string;
|
|
190
|
-
entity: string;
|
|
191
|
-
entityType: string;
|
|
192
|
-
skillTag: string;
|
|
193
|
-
confidence: number;
|
|
194
|
-
sourceRecordId: string;
|
|
195
|
-
createdTime: string;
|
|
196
|
-
}
|
|
197
|
-
export interface GraphEdge {
|
|
198
|
-
id: string;
|
|
199
|
-
userId: string;
|
|
200
|
-
fromNodeId: string;
|
|
201
|
-
toNodeId: string;
|
|
202
|
-
relation: string;
|
|
203
|
-
skillTag: string;
|
|
204
|
-
confidence: number;
|
|
205
|
-
sourceRecordId: string;
|
|
206
|
-
createdTime: string;
|
|
207
|
-
}
|