@kinqs/brainrouter-mcp-server 0.3.5 → 0.3.7

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.
Files changed (63) hide show
  1. package/.env.example +121 -71
  2. package/README.md +1 -1
  3. package/dist/__tests__/cognitive-extractor.test.js +112 -0
  4. package/dist/__tests__/crypto.test.js +8 -1
  5. package/dist/__tests__/working-memory.test.js +67 -0
  6. package/dist/api/auth/crypto.js +8 -3
  7. package/dist/index.js +1 -1
  8. package/dist/memory/engine.js +21 -1
  9. package/dist/memory/pipeline/cognitive-extractor.js +19 -1
  10. package/dist/memory/recall.d.ts +3 -1
  11. package/dist/memory/recall.js +48 -3
  12. package/dist/memory/store/relevance-judge.d.ts +51 -0
  13. package/dist/memory/store/relevance-judge.js +196 -0
  14. package/dist/memory/working/canvas.js +11 -0
  15. package/docs/specs/0.3.7-terminal-ui-redesign.md +259 -0
  16. package/package.json +2 -2
  17. package/dist/memory/config.d.ts +0 -2
  18. package/dist/memory/config.js +0 -3
  19. package/dist/memory/pipeline/l1-contradiction.d.ts +0 -7
  20. package/dist/memory/pipeline/l1-contradiction.js +0 -66
  21. package/dist/memory/pipeline/l1-dedup.d.ts +0 -23
  22. package/dist/memory/pipeline/l1-dedup.js +0 -39
  23. package/dist/memory/pipeline/l1-extractor.d.ts +0 -21
  24. package/dist/memory/pipeline/l1-extractor.js +0 -180
  25. package/dist/memory/pipeline/l2-direction-shift.d.ts +0 -10
  26. package/dist/memory/pipeline/l2-direction-shift.js +0 -27
  27. package/dist/memory/pipeline/l2-scene.d.ts +0 -15
  28. package/dist/memory/pipeline/l2-scene.js +0 -140
  29. package/dist/memory/pipeline/l3-distiller.d.ts +0 -15
  30. package/dist/memory/pipeline/l3-distiller.js +0 -40
  31. package/dist/memory/pipeline/task-queue.d.ts +0 -54
  32. package/dist/memory/pipeline/task-queue.js +0 -117
  33. package/dist/memory/prompts/graph-extraction-batch.d.ts +0 -14
  34. package/dist/memory/prompts/graph-extraction-batch.js +0 -54
  35. package/dist/memory/prompts/l1-contradiction-batch.d.ts +0 -16
  36. package/dist/memory/prompts/l1-contradiction-batch.js +0 -47
  37. package/dist/memory/prompts/l1-contradiction.d.ts +0 -1
  38. package/dist/memory/prompts/l1-contradiction.js +0 -25
  39. package/dist/memory/prompts/l1-extraction.d.ts +0 -10
  40. package/dist/memory/prompts/l1-extraction.js +0 -114
  41. package/dist/memory/prompts/l2-direction-shift.d.ts +0 -5
  42. package/dist/memory/prompts/l2-direction-shift.js +0 -32
  43. package/dist/memory/prompts/l2-scene-cluster.d.ts +0 -2
  44. package/dist/memory/prompts/l2-scene-cluster.js +0 -33
  45. package/dist/memory/prompts/l2-scene.d.ts +0 -7
  46. package/dist/memory/prompts/l2-scene.js +0 -40
  47. package/dist/memory/prompts/l3-persona.d.ts +0 -6
  48. package/dist/memory/prompts/l3-persona.js +0 -60
  49. package/dist/memory/store/types.d.ts +0 -101
  50. package/dist/memory/store/types.js +0 -1
  51. package/dist/memory/types.d.ts +0 -207
  52. package/dist/memory/types.js +0 -7
  53. package/dist/memory/validation.d.ts +0 -441
  54. package/dist/memory/validation.js +0 -129
  55. package/dist/tools/agent_memory_tools.d.ts +0 -485
  56. package/dist/tools/agent_memory_tools.js +0 -793
  57. package/dist/tools/get_doc.d.ts +0 -21
  58. package/dist/tools/get_doc.js +0 -24
  59. package/dist/tools/list_docs.d.ts +0 -15
  60. package/dist/tools/list_docs.js +0 -16
  61. package/dist/tools/update_doc.d.ts +0 -24
  62. package/dist/tools/update_doc.js +0 -35
  63. /package/dist/__tests__/{agent_mode.test.d.ts → cognitive-extractor.test.d.ts} +0 -0
@@ -1,117 +0,0 @@
1
- /**
2
- * BrainRouter — Background Task Queue (Optimization C)
3
- *
4
- * Decouples L2 scene distillation and L3 persona distillation from the hot
5
- * path of capture(). These are the most expensive, non-latency-critical steps.
6
- *
7
- * Graph extraction (Opt B) fires immediately as fire-and-forget — it is NOT queued.
8
- * Contradiction detection (Opt A) also fires immediately as fire-and-forget.
9
- *
10
- * Flush triggers:
11
- * 1. Lazy — called by recall() when the user is idle (lastCapture > idleThresholdMs)
12
- * 2. Explicit — engine.flush(userId) called directly
13
- * 3. Disabled — BRAINROUTER_ASYNC_PIPELINE=false keeps old fire-and-forget behaviour
14
- */
15
- import { distillScenes } from "./l2-scene.js";
16
- import { distillPersona } from "./l3-distiller.js";
17
- // ─── Config ───────────────────────────────────────────────────────────────────
18
- /** Flush pending tasks before recall if last capture was more than this many ms ago. */
19
- const IDLE_THRESHOLD_MS = parseInt(process.env.BRAINROUTER_FLUSH_IDLE_MS ?? String(30_000), // default: 30s idle
20
- 10);
21
- /** Is the async pipeline enabled? Default: true. Set to "false" to use old fire-and-forget. */
22
- export function isAsyncPipelineEnabled() {
23
- return process.env.BRAINROUTER_ASYNC_PIPELINE !== "false";
24
- }
25
- // ─── Enqueue ──────────────────────────────────────────────────────────────────
26
- /**
27
- * Enqueue a task in the persistent SQLite queue instead of firing it immediately.
28
- * Safe to call from the capture hot path — synchronous SQLite insert, no I/O wait.
29
- */
30
- export function enqueueTask(store, task) {
31
- const id = `task_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
32
- store.upsertPendingTask({
33
- id,
34
- userId: task.userId,
35
- taskType: task.taskType,
36
- payload: JSON.stringify(task.payload),
37
- createdAt: new Date().toISOString(),
38
- });
39
- }
40
- // ─── Flush ────────────────────────────────────────────────────────────────────
41
- /** Active flush promises per userId to prevent concurrent flushes */
42
- const flushInProgress = new Map();
43
- /**
44
- * Process all pending L2/L3 tasks for a user in FIFO order.
45
- *
46
- * Re-entrant safe: if a flush is already in progress for this userId, the existing
47
- * promise is returned instead of starting a second flush.
48
- */
49
- export async function flushPendingTasks(userId, store, synthesisRunner) {
50
- const existing = flushInProgress.get(userId);
51
- if (existing)
52
- return existing;
53
- const flushPromise = _doFlush(userId, store, synthesisRunner);
54
- flushInProgress.set(userId, flushPromise);
55
- try {
56
- return await flushPromise;
57
- }
58
- finally {
59
- flushInProgress.delete(userId);
60
- }
61
- }
62
- async function _doFlush(userId, store, synthesisRunner) {
63
- const start = Date.now();
64
- const tasks = store.getPendingTasks(userId);
65
- if (tasks.length === 0) {
66
- return { userId, tasksProcessed: 0, tasksFailed: 0, durationMs: 0 };
67
- }
68
- let processed = 0;
69
- let failed = 0;
70
- // Process l2_scene and l3_persona in FIFO order.
71
- // Deduplicate consecutive same-type tasks (e.g., 10 queued l2_scene → run once).
72
- let lastTaskType = null;
73
- for (const task of tasks) {
74
- try {
75
- if (task.taskType === lastTaskType) {
76
- // Skip duplicate consecutive task of same type — one distillation run is sufficient
77
- store.deletePendingTask(task.id);
78
- continue;
79
- }
80
- switch (task.taskType) {
81
- case "l2_scene":
82
- await distillScenes({ userId, store, llmRunner: synthesisRunner });
83
- break;
84
- case "l3_persona":
85
- await distillPersona({ userId, store, llmRunner: synthesisRunner });
86
- break;
87
- }
88
- lastTaskType = task.taskType;
89
- processed++;
90
- }
91
- catch (err) {
92
- console.error(`[BrainRouter] Flush: task ${task.taskType} failed:`, err.message);
93
- failed++;
94
- }
95
- finally {
96
- store.deletePendingTask(task.id);
97
- }
98
- }
99
- return {
100
- userId,
101
- tasksProcessed: processed,
102
- tasksFailed: failed,
103
- durationMs: Date.now() - start,
104
- };
105
- }
106
- // ─── Idle Check ───────────────────────────────────────────────────────────────
107
- /**
108
- * Returns true if the user has been idle long enough that pre-recall flushing
109
- * won't noticeably delay the response.
110
- */
111
- export function isUserIdle(store, userId) {
112
- const state = store.getSchedulerState(userId);
113
- if (!state.lastCaptureAt)
114
- return true; // never captured = trivially idle
115
- const idleMs = Date.now() - new Date(state.lastCaptureAt).getTime();
116
- return idleMs >= IDLE_THRESHOLD_MS;
117
- }
@@ -1,14 +0,0 @@
1
- /**
2
- * Batch Graph Extraction Prompt
3
- *
4
- * Replaces N individual per-memory graph extraction calls with a single call
5
- * covering all new L1 memories from a capture cycle simultaneously.
6
- *
7
- * Token reduction: N → 1 call per capture cycle.
8
- */
9
- export declare const BATCH_GRAPH_EXTRACTION_SYSTEM_PROMPT = "You are an expert knowledge graph extraction engine.\n\nYou will receive a numbered list of memory statements. Extract ALL key entities (nodes)\nand their relationships (edges) across the entire list.\n\nLIMITS:\n- Extract at most 15 key entities total across all memories.\n- Extract at most 20 key relations total.\n- Focus on the most important technical choices, mandates, roles, and decisions.\n\nEntity Types:\n- \"tool\" / \"technology\" (e.g. pnpm, React, Postgres, Zod)\n- \"project\" (e.g. BrainRouter, Kinqs Radio)\n- \"person\" (e.g. lead engineer, user, junior developers)\n- \"concept\" / \"decision\" (e.g. monorepo migration, dark mode, camelCase policy)\n\nRelation Types (use ONLY these):\n- \"uses\" / \"adopts\"\n- \"owns\" / \"manages\"\n- \"decided\" / \"revised\"\n- \"conflicts_with\" / \"replaces\"\n- \"applies_to\"\n- \"requires\"\n- \"is_a\" / \"is_role\"\n\nFor each entity, also include \"sourceIndex\" matching the memory's index number.\n\nOutput ONLY a valid JSON object (no markdown fences):\n{\n \"entities\": [\n { \"entity\": \"Canonical Name\", \"type\": \"tool|technology|project|person|concept|decision\", \"confidence\": 1.0, \"sourceIndex\": 0 }\n ],\n \"relations\": [\n { \"from\": \"Entity A\", \"to\": \"Entity B\", \"relation\": \"uses\", \"confidence\": 1.0 }\n ]\n}";
10
- export declare function formatBatchGraphExtractionPrompt(memories: Array<{
11
- index: number;
12
- content: string;
13
- recordId: string;
14
- }>): string;
@@ -1,54 +0,0 @@
1
- /**
2
- * Batch Graph Extraction Prompt
3
- *
4
- * Replaces N individual per-memory graph extraction calls with a single call
5
- * covering all new L1 memories from a capture cycle simultaneously.
6
- *
7
- * Token reduction: N → 1 call per capture cycle.
8
- */
9
- export const BATCH_GRAPH_EXTRACTION_SYSTEM_PROMPT = `You are an expert knowledge graph extraction engine.
10
-
11
- You will receive a numbered list of memory statements. Extract ALL key entities (nodes)
12
- and their relationships (edges) across the entire list.
13
-
14
- LIMITS:
15
- - Extract at most 15 key entities total across all memories.
16
- - Extract at most 20 key relations total.
17
- - Focus on the most important technical choices, mandates, roles, and decisions.
18
-
19
- Entity Types:
20
- - "tool" / "technology" (e.g. pnpm, React, Postgres, Zod)
21
- - "project" (e.g. BrainRouter, Kinqs Radio)
22
- - "person" (e.g. lead engineer, user, junior developers)
23
- - "concept" / "decision" (e.g. monorepo migration, dark mode, camelCase policy)
24
-
25
- Relation Types (use ONLY these):
26
- - "uses" / "adopts"
27
- - "owns" / "manages"
28
- - "decided" / "revised"
29
- - "conflicts_with" / "replaces"
30
- - "applies_to"
31
- - "requires"
32
- - "is_a" / "is_role"
33
-
34
- For each entity, also include "sourceIndex" matching the memory's index number.
35
-
36
- Output ONLY a valid JSON object (no markdown fences):
37
- {
38
- "entities": [
39
- { "entity": "Canonical Name", "type": "tool|technology|project|person|concept|decision", "confidence": 1.0, "sourceIndex": 0 }
40
- ],
41
- "relations": [
42
- { "from": "Entity A", "to": "Entity B", "relation": "uses", "confidence": 1.0 }
43
- ]
44
- }`;
45
- export function formatBatchGraphExtractionPrompt(memories) {
46
- const memoryList = memories
47
- .map(m => ` [${m.index}] "${m.content}"`)
48
- .join("\n");
49
- return `Extract entities and relations from ALL of the following memories:
50
-
51
- ${memoryList}
52
-
53
- JSON Output:`;
54
- }
@@ -1,16 +0,0 @@
1
- /**
2
- * Batch Contradiction Detection Prompt
3
- *
4
- * Replaces N×K individual pairwise LLM calls with a single call covering
5
- * all new memories and their top FTS candidates simultaneously.
6
- *
7
- * Token reduction: 25 → 1 call per capture cycle (for 5 new memories × 5 candidates).
8
- */
9
- export declare const BATCH_CONTRADICTION_SYSTEM_PROMPT = "You are the Contradiction & Temporal Update Detector for a hierarchical memory engine.\n\nYou will receive a list of NEWLY EXTRACTED memories and a list of EXISTING memories.\nYour task is to identify ALL pairs that contradict, supersede, or update each other.\n\nRULES:\n1. Only flag pairs with genuine overlap or conflict. Ignore unrelated pairs.\n2. Classify each contradiction into one of two kinds:\n - \"temporal_update\": The new memory deliberately supersedes the old one (e.g. user changed their mind, moved cities, switched tools). The NEW memory wins.\n - \"genuine_conflict\": Logically incompatible absolute claims where neither obviously supersedes the other (e.g. conflicting birth years).\n3. Provide a confidence score (0.0\u20131.0). Only include pairs with confidence > 0.7.\n4. If no contradictions exist, return an empty array.\n\nRespond ONLY with a JSON array (no markdown fences):\n[\n {\n \"newIndex\": number,\n \"existingIndex\": number,\n \"kind\": \"temporal_update\" | \"genuine_conflict\",\n \"reason\": \"Brief explanation\",\n \"confidence\": number\n }\n]";
10
- export declare function formatBatchContradictionPrompt(newMemories: Array<{
11
- index: number;
12
- content: string;
13
- }>, existingMemories: Array<{
14
- index: number;
15
- content: string;
16
- }>): string;
@@ -1,47 +0,0 @@
1
- /**
2
- * Batch Contradiction Detection Prompt
3
- *
4
- * Replaces N×K individual pairwise LLM calls with a single call covering
5
- * all new memories and their top FTS candidates simultaneously.
6
- *
7
- * Token reduction: 25 → 1 call per capture cycle (for 5 new memories × 5 candidates).
8
- */
9
- export const BATCH_CONTRADICTION_SYSTEM_PROMPT = `You are the Contradiction & Temporal Update Detector for a hierarchical memory engine.
10
-
11
- You will receive a list of NEWLY EXTRACTED memories and a list of EXISTING memories.
12
- Your task is to identify ALL pairs that contradict, supersede, or update each other.
13
-
14
- RULES:
15
- 1. Only flag pairs with genuine overlap or conflict. Ignore unrelated pairs.
16
- 2. Classify each contradiction into one of two kinds:
17
- - "temporal_update": The new memory deliberately supersedes the old one (e.g. user changed their mind, moved cities, switched tools). The NEW memory wins.
18
- - "genuine_conflict": Logically incompatible absolute claims where neither obviously supersedes the other (e.g. conflicting birth years).
19
- 3. Provide a confidence score (0.0–1.0). Only include pairs with confidence > 0.7.
20
- 4. If no contradictions exist, return an empty array.
21
-
22
- Respond ONLY with a JSON array (no markdown fences):
23
- [
24
- {
25
- "newIndex": number,
26
- "existingIndex": number,
27
- "kind": "temporal_update" | "genuine_conflict",
28
- "reason": "Brief explanation",
29
- "confidence": number
30
- }
31
- ]`;
32
- export function formatBatchContradictionPrompt(newMemories, existingMemories) {
33
- const newList = newMemories
34
- .map(m => ` [N${m.index}] "${m.content}"`)
35
- .join("\n");
36
- const existingList = existingMemories
37
- .map(m => ` [E${m.index}] "${m.content}"`)
38
- .join("\n");
39
- return `NEWLY EXTRACTED MEMORIES:
40
- ${newList}
41
-
42
- EXISTING MEMORIES (candidates for contradiction):
43
- ${existingList}
44
-
45
- Identify all contradicting pairs between the N (new) and E (existing) sets.
46
- JSON Array Output:`;
47
- }
@@ -1 +0,0 @@
1
- export declare const L1_CONTRADICTION_PROMPT = "\nYou are the Contradiction & Temporal Update Detector for a hierarchical memory engine.\nYour task is to compare a NEWLY EXTRACTED memory with an EXISTING RELEVANT memory and determine if they contradict, supersede, or update each other.\n\nNEW MEMORY:\n\"{{newContent}}\"\n\nEXISTING MEMORY:\n\"{{existingContent}}\"\n\nRULES:\n1. Determine if they overlap or conflict. If there is no overlap/conflict, set \"isContradiction\" to false.\n2. If they conflict or override, classify the relationship into one of two kinds:\n - \"temporal_update\": The new memory is a deliberate update, change of mind, transition, or correction to the old memory (e.g. \"User lives in SF\" vs \"User moved to NYC\", or \"User wants pnpm\" vs \"User changed package manager to regular npm\"). This means the new memory actively supersedes the old one.\n - \"genuine_conflict\": The statements are logically incompatible, absolute claims that cannot both be true, and it is NOT a simple update or transition (e.g. \"User was born in 1990\" vs \"User was born in 1995\", or conflicting rules without a clear indication that one was meant to update the other).\n3. Provide a clear reason and a confidence score.\n\nRespond ONLY with a JSON object:\n{\n \"isContradiction\": boolean,\n \"kind\": \"temporal_update\" | \"genuine_conflict\" | null,\n \"reason\": \"Brief explanation if isContradiction is true, else null\",\n \"confidence\": number (0.0 to 1.0)\n}\n";
@@ -1,25 +0,0 @@
1
- export const L1_CONTRADICTION_PROMPT = `
2
- You are the Contradiction & Temporal Update Detector for a hierarchical memory engine.
3
- Your task is to compare a NEWLY EXTRACTED memory with an EXISTING RELEVANT memory and determine if they contradict, supersede, or update each other.
4
-
5
- NEW MEMORY:
6
- "{{newContent}}"
7
-
8
- EXISTING MEMORY:
9
- "{{existingContent}}"
10
-
11
- RULES:
12
- 1. Determine if they overlap or conflict. If there is no overlap/conflict, set "isContradiction" to false.
13
- 2. If they conflict or override, classify the relationship into one of two kinds:
14
- - "temporal_update": The new memory is a deliberate update, change of mind, transition, or correction to the old memory (e.g. "User lives in SF" vs "User moved to NYC", or "User wants pnpm" vs "User changed package manager to regular npm"). This means the new memory actively supersedes the old one.
15
- - "genuine_conflict": The statements are logically incompatible, absolute claims that cannot both be true, and it is NOT a simple update or transition (e.g. "User was born in 1990" vs "User was born in 1995", or conflicting rules without a clear indication that one was meant to update the other).
16
- 3. Provide a clear reason and a confidence score.
17
-
18
- Respond ONLY with a JSON object:
19
- {
20
- "isContradiction": boolean,
21
- "kind": "temporal_update" | "genuine_conflict" | null,
22
- "reason": "Brief explanation if isContradiction is true, else null",
23
- "confidence": number (0.0 to 1.0)
24
- }
25
- `;
@@ -1,10 +0,0 @@
1
- import type { L0Record } from "@brainrouter/types";
2
- export declare const EXTRACT_MEMORIES_SYSTEM_PROMPT = "You are a Skill-Aware Memory Extraction Expert for a software engineering AI assistant.\n\nYour task is to analyze a conversation and extract durable, self-contained memories.\n\n### Scene Segmentation\nDetermine if the topic has changed since the previous scene.\nIf there are existing scenes, PREFER to reuse the most relevant existing scene name \u2014 only create a new name if the topic is genuinely different from ALL existing scenes.\nIf reusing, use the EXACT existing name (no paraphrasing).\nIf creating new, format: \"AI helping [user role] with [goal activity]\" (unique, max 50 chars).\n\n### Memory Types\n\n1. **persona** \u2014 Stable user traits, preferences, identity\n - Format: \"User [prefers/is/always/never] ...\"\n - Priority: 80-100 (core identity) / 50-70 (preferences) / skip if <50\n\n2. **episodic** \u2014 Objective events, decisions, results with timestamps\n - Format: \"User [did X] at [time] resulting in [Y]\"\n - Priority: 80-100 (major decisions) / 60-79 (significant events) / skip if <60\n\n3. **instruction** \u2014 Long-term rules the user gave the AI\n - Format: \"User requires AI to always/never ...\"\n - Priority: 90-100 (hard rules) / 70-89 (preferences) / skip if <70\n - Instructions NEVER decay.\n\n4. **skill_context** \u2014 Observations about how the user runs THIS SKILL specifically\n - Format: \"When running [skill], user tends to ...\"\n - Only extract if a genuine behavioral pattern is visible, not a one-off.\n\n5. **tool_preference** \u2014 Stable preferences about tools, workflows, or command usage.\n6. **codebase_fact / api_contract / data_model** \u2014 Verified facts about code, public APIs, schemas, or storage models.\n7. **dependency_constraint / environment_constraint** \u2014 Version, runtime, platform, sandbox, or deployment constraints.\n8. **architecture_decision / implementation_decision / design_constraint** \u2014 Durable decisions and constraints that future agents must preserve.\n9. **security_policy / performance_baseline** \u2014 Security rules or measured performance facts. Extract only with direct evidence.\n10. **bug_finding / debug_trace / fix_summary / verification_result / failed_attempt / regression_risk** \u2014 Debugging history, fixes, checks, and risk notes.\n11. **task_state / handover_note / blocked_reason / review_comment / release_note** \u2014 Planning, review, release, and continuation state.\n12. **source_evidence / artifact_reference / file_history / command_knowledge** \u2014 Evidence-backed references to files, artifacts, file evolution, or command behavior.\n\nAllowed type values:\npersona, episodic, instruction, skill_context, tool_preference, codebase_fact, api_contract,\ndata_model, dependency_constraint, environment_constraint, architecture_decision,\nimplementation_decision, design_constraint, security_policy, performance_baseline,\nbug_finding, debug_trace, fix_summary, verification_result, failed_attempt, regression_risk,\ntask_state, handover_note, blocked_reason, review_comment, release_note, source_evidence,\nartifact_reference, file_history, command_knowledge.\n\n### Quality Rules\n- Nothingness > Bad memory. Prefer empty over wrong.\n- Memory must stand alone without the conversation.\n- Merge causally-linked facts into one memory.\n- Do not extract AI outputs \u2014 only user behavior and statements.\n- Filter out: tool calls, one-time requests, casual greetings.\n- Evidence-gated types (api_contract, data_model, security_policy, performance_baseline) require a cited file path, command, test result, or explicit user statement.\n- Use confidence from 0.0 to 1.0. Use lower confidence for model inference, higher confidence for direct source or command output.\n- Classify sourceKind as user_instruction, source_file, command_output, test_result, model_inference, or prior_memory.\n- Extract filePaths, repoPaths, and commands when explicitly mentioned; otherwise use empty arrays.\n\n### Output\nReturn ONLY a valid JSON array matching this format exactly:\n[\n {\n \"scene_name\": \"current or inherited scene name\",\n \"message_ids\": [\"id1\"],\n \"memories\": [\n {\n \"type\": \"one allowed type value\",\n \"content\": \"self-contained memory statement\",\n \"priority\": 85,\n \"skill_tag\": \"the active skill\",\n \"source_message_ids\": [\"id1\"],\n \"confidence\": 0.75,\n \"sourceKind\": \"user_instruction|source_file|command_output|test_result|model_inference|prior_memory\",\n \"verificationStatus\": \"verified|unverified|stale\",\n \"repoPaths\": [],\n \"filePaths\": [],\n \"commands\": [],\n \"metadata\": {}\n }\n ]\n }\n]\n";
3
- export declare function formatExtractionPrompt(params: {
4
- newMessages: L0Record[];
5
- backgroundMessages?: L0Record[];
6
- previousSceneName?: string;
7
- existingSceneNames?: string[];
8
- activeSkill?: string;
9
- skillHints?: string;
10
- }): string;
@@ -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
- }