@mnemoai/core 1.1.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.
Files changed (49) hide show
  1. package/index.ts +3395 -0
  2. package/openclaw.plugin.json +815 -0
  3. package/package.json +59 -0
  4. package/src/access-tracker.ts +341 -0
  5. package/src/adapters/README.md +78 -0
  6. package/src/adapters/chroma.ts +206 -0
  7. package/src/adapters/lancedb.ts +237 -0
  8. package/src/adapters/pgvector.ts +218 -0
  9. package/src/adapters/qdrant.ts +191 -0
  10. package/src/adaptive-retrieval.ts +90 -0
  11. package/src/audit-log.ts +238 -0
  12. package/src/chunker.ts +254 -0
  13. package/src/config.ts +271 -0
  14. package/src/decay-engine.ts +238 -0
  15. package/src/embedder.ts +735 -0
  16. package/src/extraction-prompts.ts +339 -0
  17. package/src/license.ts +258 -0
  18. package/src/llm-client.ts +125 -0
  19. package/src/mcp-server.ts +415 -0
  20. package/src/memory-categories.ts +71 -0
  21. package/src/memory-upgrader.ts +388 -0
  22. package/src/migrate.ts +364 -0
  23. package/src/mnemo.ts +142 -0
  24. package/src/noise-filter.ts +97 -0
  25. package/src/noise-prototypes.ts +164 -0
  26. package/src/observability.ts +81 -0
  27. package/src/query-tracker.ts +57 -0
  28. package/src/reflection-event-store.ts +98 -0
  29. package/src/reflection-item-store.ts +112 -0
  30. package/src/reflection-mapped-metadata.ts +84 -0
  31. package/src/reflection-metadata.ts +23 -0
  32. package/src/reflection-ranking.ts +33 -0
  33. package/src/reflection-retry.ts +181 -0
  34. package/src/reflection-slices.ts +265 -0
  35. package/src/reflection-store.ts +602 -0
  36. package/src/resonance-state.ts +85 -0
  37. package/src/retriever.ts +1510 -0
  38. package/src/scopes.ts +375 -0
  39. package/src/self-improvement-files.ts +143 -0
  40. package/src/semantic-gate.ts +121 -0
  41. package/src/session-recovery.ts +138 -0
  42. package/src/smart-extractor.ts +923 -0
  43. package/src/smart-metadata.ts +561 -0
  44. package/src/storage-adapter.ts +153 -0
  45. package/src/store.ts +1330 -0
  46. package/src/tier-manager.ts +189 -0
  47. package/src/tools.ts +1292 -0
  48. package/src/wal-recovery.ts +172 -0
  49. package/test/core.test.mjs +301 -0
@@ -0,0 +1,181 @@
1
+ // SPDX-License-Identifier: LicenseRef-Mnemo-Pro
2
+ type RetryClassifierInput = {
3
+ inReflectionScope: boolean;
4
+ retryCount: number;
5
+ usefulOutputChars: number;
6
+ error: unknown;
7
+ };
8
+
9
+ type RetryClassifierResult = {
10
+ retryable: boolean;
11
+ reason:
12
+ | "not_reflection_scope"
13
+ | "retry_already_used"
14
+ | "useful_output_present"
15
+ | "non_retry_error"
16
+ | "non_transient_error"
17
+ | "transient_upstream_failure";
18
+ normalizedError: string;
19
+ };
20
+
21
+ type RetryState = { count: number };
22
+
23
+ type RetryRunnerParams<T> = {
24
+ scope: "reflection" | "distiller";
25
+ runner: "embedded" | "cli";
26
+ retryState: RetryState;
27
+ execute: () => Promise<T>;
28
+ onLog?: (level: "info" | "warn", message: string) => void;
29
+ random?: () => number;
30
+ sleep?: (ms: number) => Promise<void>;
31
+ };
32
+
33
+ const REFLECTION_TRANSIENT_PATTERNS: RegExp[] = [
34
+ /unexpected eof/i,
35
+ /\beconnreset\b/i,
36
+ /\beconnaborted\b/i,
37
+ /\betimedout\b/i,
38
+ /\bepipe\b/i,
39
+ /connection reset/i,
40
+ /socket hang up/i,
41
+ /socket (?:closed|disconnected)/i,
42
+ /connection (?:closed|aborted|dropped)/i,
43
+ /early close/i,
44
+ /stream (?:ended|closed) unexpectedly/i,
45
+ /temporar(?:y|ily).*unavailable/i,
46
+ /upstream.*unavailable/i,
47
+ /service unavailable/i,
48
+ /bad gateway/i,
49
+ /gateway timeout/i,
50
+ /\b(?:http|status)\s*(?:502|503|504)\b/i,
51
+ /\btimed out\b/i,
52
+ /\btimeout\b/i,
53
+ /\bund_err_(?:socket|headers_timeout|body_timeout)\b/i,
54
+ /network error/i,
55
+ /fetch failed/i,
56
+ ];
57
+
58
+ const REFLECTION_NON_RETRY_PATTERNS: RegExp[] = [
59
+ /\b401\b/i,
60
+ /\bunauthorized\b/i,
61
+ /invalid api key/i,
62
+ /invalid[_ -]?token/i,
63
+ /\bauth(?:entication)?_?unavailable\b/i,
64
+ /insufficient (?:credit|credits|balance)/i,
65
+ /\bbilling\b/i,
66
+ /\bquota exceeded\b/i,
67
+ /payment required/i,
68
+ /model .*not found/i,
69
+ /no such model/i,
70
+ /unknown model/i,
71
+ /context length/i,
72
+ /context window/i,
73
+ /request too large/i,
74
+ /payload too large/i,
75
+ /too many tokens/i,
76
+ /token limit/i,
77
+ /prompt too long/i,
78
+ /session expired/i,
79
+ /invalid session/i,
80
+ /refusal/i,
81
+ /content policy/i,
82
+ /safety policy/i,
83
+ /content filter/i,
84
+ /disallowed/i,
85
+ ];
86
+
87
+ const DEFAULT_SLEEP = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));
88
+
89
+ function toErrorMessage(error: unknown): string {
90
+ if (error instanceof Error) {
91
+ const msg = `${error.name}: ${error.message}`.trim();
92
+ return msg || "Error";
93
+ }
94
+ if (typeof error === "string") return error;
95
+ try {
96
+ return JSON.stringify(error);
97
+ } catch {
98
+ return String(error);
99
+ }
100
+ }
101
+
102
+ function clipSingleLine(text: string, maxLen = 260): string {
103
+ const oneLine = text.replace(/\s+/g, " ").trim();
104
+ if (oneLine.length <= maxLen) return oneLine;
105
+ return `${oneLine.slice(0, maxLen - 3)}...`;
106
+ }
107
+
108
+ export function isTransientReflectionUpstreamError(error: unknown): boolean {
109
+ const msg = toErrorMessage(error);
110
+ return REFLECTION_TRANSIENT_PATTERNS.some((pattern) => pattern.test(msg));
111
+ }
112
+
113
+ export function isReflectionNonRetryError(error: unknown): boolean {
114
+ const msg = toErrorMessage(error);
115
+ return REFLECTION_NON_RETRY_PATTERNS.some((pattern) => pattern.test(msg));
116
+ }
117
+
118
+ export function classifyReflectionRetry(input: RetryClassifierInput): RetryClassifierResult {
119
+ const normalizedError = clipSingleLine(toErrorMessage(input.error), 260);
120
+
121
+ if (!input.inReflectionScope) {
122
+ return { retryable: false, reason: "not_reflection_scope", normalizedError };
123
+ }
124
+ if (input.retryCount > 0) {
125
+ return { retryable: false, reason: "retry_already_used", normalizedError };
126
+ }
127
+ if (input.usefulOutputChars > 0) {
128
+ return { retryable: false, reason: "useful_output_present", normalizedError };
129
+ }
130
+ if (isReflectionNonRetryError(input.error)) {
131
+ return { retryable: false, reason: "non_retry_error", normalizedError };
132
+ }
133
+ if (isTransientReflectionUpstreamError(input.error)) {
134
+ return { retryable: true, reason: "transient_upstream_failure", normalizedError };
135
+ }
136
+ return { retryable: false, reason: "non_transient_error", normalizedError };
137
+ }
138
+
139
+ export function computeReflectionRetryDelayMs(random: () => number = Math.random): number {
140
+ const raw = random();
141
+ const clamped = Number.isFinite(raw) ? Math.min(1, Math.max(0, raw)) : 0;
142
+ return 1000 + Math.floor(clamped * 2000);
143
+ }
144
+
145
+ export async function runWithReflectionTransientRetryOnce<T>(
146
+ params: RetryRunnerParams<T>
147
+ ): Promise<T> {
148
+ try {
149
+ return await params.execute();
150
+ } catch (error) {
151
+ const decision = classifyReflectionRetry({
152
+ inReflectionScope: params.scope === "reflection" || params.scope === "distiller",
153
+ retryCount: params.retryState.count,
154
+ usefulOutputChars: 0,
155
+ error,
156
+ });
157
+ if (!decision.retryable) throw error;
158
+
159
+ const delayMs = computeReflectionRetryDelayMs(params.random);
160
+ params.retryState.count += 1;
161
+ params.onLog?.(
162
+ "warn",
163
+ `memory-${params.scope}: transient upstream failure detected (${params.runner}); ` +
164
+ `retrying once in ${delayMs}ms (${decision.reason}). error=${decision.normalizedError}`
165
+ );
166
+ await (params.sleep ?? DEFAULT_SLEEP)(delayMs);
167
+
168
+ try {
169
+ const result = await params.execute();
170
+ params.onLog?.("info", `memory-${params.scope}: retry succeeded (${params.runner})`);
171
+ return result;
172
+ } catch (retryError) {
173
+ params.onLog?.(
174
+ "warn",
175
+ `memory-${params.scope}: retry exhausted (${params.runner}). ` +
176
+ `error=${clipSingleLine(toErrorMessage(retryError), 260)}`
177
+ );
178
+ throw retryError;
179
+ }
180
+ }
181
+ }
@@ -0,0 +1,265 @@
1
+ // SPDX-License-Identifier: LicenseRef-Mnemo-Pro
2
+ export interface ReflectionSlices {
3
+ invariants: string[];
4
+ derived: string[];
5
+ }
6
+
7
+ export interface ReflectionMappedMemory {
8
+ text: string;
9
+ category: "preference" | "fact" | "decision";
10
+ heading: string;
11
+ }
12
+
13
+ export type ReflectionMappedKind = "user-model" | "agent-model" | "lesson" | "decision";
14
+
15
+ export interface ReflectionMappedMemoryItem extends ReflectionMappedMemory {
16
+ mappedKind: ReflectionMappedKind;
17
+ ordinal: number;
18
+ groupSize: number;
19
+ }
20
+
21
+ export interface ReflectionSliceItem {
22
+ text: string;
23
+ itemKind: "invariant" | "derived";
24
+ section: "Invariants" | "Derived";
25
+ ordinal: number;
26
+ groupSize: number;
27
+ }
28
+
29
+ export interface ReflectionGovernanceEntry {
30
+ priority?: string;
31
+ status?: string;
32
+ area?: string;
33
+ summary: string;
34
+ details?: string;
35
+ suggestedAction?: string;
36
+ }
37
+
38
+ export function extractSectionMarkdown(markdown: string, heading: string): string {
39
+ const lines = markdown.split(/\r?\n/);
40
+ const headingNeedle = `## ${heading}`.toLowerCase();
41
+ let inSection = false;
42
+ const collected: string[] = [];
43
+ for (const raw of lines) {
44
+ const line = raw.trim();
45
+ const lower = line.toLowerCase();
46
+ if (lower.startsWith("## ")) {
47
+ if (inSection && lower !== headingNeedle) break;
48
+ inSection = lower === headingNeedle;
49
+ continue;
50
+ }
51
+ if (!inSection) continue;
52
+ collected.push(raw);
53
+ }
54
+ return collected.join("\n").trim();
55
+ }
56
+
57
+ export function parseSectionBullets(markdown: string, heading: string): string[] {
58
+ const lines = extractSectionMarkdown(markdown, heading).split(/\r?\n/);
59
+ const collected: string[] = [];
60
+ for (const raw of lines) {
61
+ const line = raw.trim();
62
+ if (line.startsWith("- ") || line.startsWith("* ")) {
63
+ const normalized = line.slice(2).trim();
64
+ if (normalized) collected.push(normalized);
65
+ }
66
+ }
67
+ return collected;
68
+ }
69
+
70
+ export function isPlaceholderReflectionSliceLine(line: string): boolean {
71
+ const normalized = line.replace(/\*\*/g, "").trim();
72
+ if (!normalized) return true;
73
+ if (/^\(none( captured)?\)$/i.test(normalized)) return true;
74
+ if (/^(invariants?|reflections?|derived)[::]$/i.test(normalized)) return true;
75
+ if (/apply this session'?s deltas next run/i.test(normalized)) return true;
76
+ if (/apply this session'?s distilled changes next run/i.test(normalized)) return true;
77
+ if (/investigate why embedded reflection generation failed/i.test(normalized)) return true;
78
+ return false;
79
+ }
80
+
81
+ export function normalizeReflectionSliceLine(line: string): string {
82
+ return line
83
+ .replace(/\*\*/g, "")
84
+ .replace(/^(invariants?|reflections?|derived)[::]\s*/i, "")
85
+ .trim();
86
+ }
87
+
88
+ export function sanitizeReflectionSliceLines(lines: string[]): string[] {
89
+ return lines
90
+ .map(normalizeReflectionSliceLine)
91
+ .filter((line) => !isPlaceholderReflectionSliceLine(line));
92
+ }
93
+
94
+ function isInvariantRuleLike(line: string): boolean {
95
+ return /^(always|never|when\b|if\b|before\b|after\b|prefer\b|avoid\b|require\b|only\b|do not\b|must\b|should\b)/i.test(line) ||
96
+ /\b(must|should|never|always|prefer|avoid|required?)\b/i.test(line);
97
+ }
98
+
99
+ function isDerivedDeltaLike(line: string): boolean {
100
+ return /^(this run|next run|going forward|follow-up|re-check|retest|verify|confirm|avoid repeating|adjust|change|update|retry|keep|watch)\b/i.test(line) ||
101
+ /\b(this run|next run|delta|change|adjust|retry|re-check|retest|verify|confirm|avoid repeating|follow-up)\b/i.test(line);
102
+ }
103
+
104
+ function isOpenLoopAction(line: string): boolean {
105
+ return /^(investigate|verify|confirm|re-check|retest|update|add|remove|fix|avoid|keep|watch|document)\b/i.test(line);
106
+ }
107
+
108
+ export function extractReflectionLessons(reflectionText: string): string[] {
109
+ return sanitizeReflectionSliceLines(parseSectionBullets(reflectionText, "Lessons & pitfalls (symptom / cause / fix / prevention)"));
110
+ }
111
+
112
+ export function extractReflectionLearningGovernanceCandidates(reflectionText: string): ReflectionGovernanceEntry[] {
113
+ const section = extractSectionMarkdown(reflectionText, "Learning governance candidates (.learnings / promotion / skill extraction)");
114
+ if (!section) return [];
115
+
116
+ const entryBlocks = section
117
+ .split(/(?=^###\s+Entry\b)/gim)
118
+ .map((block) => block.trim())
119
+ .filter(Boolean);
120
+
121
+ const parsed = entryBlocks
122
+ .map(parseReflectionGovernanceEntry)
123
+ .filter((entry): entry is ReflectionGovernanceEntry => entry !== null);
124
+
125
+ if (parsed.length > 0) return parsed;
126
+
127
+ const fallbackBullets = sanitizeReflectionSliceLines(
128
+ parseSectionBullets(reflectionText, "Learning governance candidates (.learnings / promotion / skill extraction)")
129
+ );
130
+ if (fallbackBullets.length === 0) return [];
131
+
132
+ return [{
133
+ priority: "medium",
134
+ status: "pending",
135
+ area: "config",
136
+ summary: "Reflection learning governance candidates",
137
+ details: fallbackBullets.map((line) => `- ${line}`).join("\n"),
138
+ suggestedAction: "Review the governance candidates, promote durable rules to AGENTS.md / SOUL.md / TOOLS.md when stable, and extract a skill if the pattern becomes reusable.",
139
+ }];
140
+ }
141
+
142
+ function parseReflectionGovernanceEntry(block: string): ReflectionGovernanceEntry | null {
143
+ const body = block.replace(/^###\s+Entry\b[^\n]*\n?/i, "").trim();
144
+ if (!body) return null;
145
+
146
+ const readField = (label: string): string | undefined => {
147
+ const match = body.match(new RegExp(`^\\*\\*${label}\\*\\*:\\s*(.+)$`, "im"));
148
+ const value = match?.[1]?.trim();
149
+ return value ? value : undefined;
150
+ };
151
+
152
+ const readSection = (label: string): string | undefined => {
153
+ const escaped = label.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
154
+ const match = body.match(new RegExp(`^###\\s+${escaped}\\s*\\n([\\s\\S]*?)(?=^###\\s+|$)`, "im"));
155
+ const value = match?.[1]?.trim();
156
+ return value ? value : undefined;
157
+ };
158
+
159
+ const summary = readSection("Summary");
160
+ if (!summary) return null;
161
+
162
+ return {
163
+ priority: readField("Priority"),
164
+ status: readField("Status"),
165
+ area: readField("Area"),
166
+ summary,
167
+ details: readSection("Details"),
168
+ suggestedAction: readSection("Suggested Action"),
169
+ };
170
+ }
171
+
172
+ export function extractReflectionMappedMemories(reflectionText: string): ReflectionMappedMemory[] {
173
+ return extractReflectionMappedMemoryItems(reflectionText).map(({ text, category, heading }) => ({ text, category, heading }));
174
+ }
175
+
176
+ export function extractReflectionMappedMemoryItems(reflectionText: string): ReflectionMappedMemoryItem[] {
177
+ const mappedSections: Array<{
178
+ heading: string;
179
+ category: "preference" | "fact" | "decision";
180
+ mappedKind: ReflectionMappedKind;
181
+ }> = [
182
+ {
183
+ heading: "User model deltas (about the human)",
184
+ category: "preference",
185
+ mappedKind: "user-model",
186
+ },
187
+ {
188
+ heading: "Agent model deltas (about the assistant/system)",
189
+ category: "preference",
190
+ mappedKind: "agent-model",
191
+ },
192
+ {
193
+ heading: "Lessons & pitfalls (symptom / cause / fix / prevention)",
194
+ category: "fact",
195
+ mappedKind: "lesson",
196
+ },
197
+ {
198
+ heading: "Decisions (durable)",
199
+ category: "decision",
200
+ mappedKind: "decision",
201
+ },
202
+ ];
203
+
204
+ return mappedSections.flatMap(({ heading, category, mappedKind }) => {
205
+ const lines = sanitizeReflectionSliceLines(parseSectionBullets(reflectionText, heading));
206
+ const groupSize = lines.length;
207
+ return lines.map((text, ordinal) => ({ text, category, heading, mappedKind, ordinal, groupSize }));
208
+ });
209
+ }
210
+
211
+ export function extractReflectionSlices(reflectionText: string): ReflectionSlices {
212
+ const invariantSection = parseSectionBullets(reflectionText, "Invariants");
213
+ const derivedSection = parseSectionBullets(reflectionText, "Derived");
214
+ const mergedSection = parseSectionBullets(reflectionText, "Invariants & Reflections");
215
+
216
+ const invariantsPrimary = sanitizeReflectionSliceLines(invariantSection).filter(isInvariantRuleLike);
217
+ const derivedPrimary = sanitizeReflectionSliceLines(derivedSection).filter(isDerivedDeltaLike);
218
+
219
+ const invariantLinesLegacy = sanitizeReflectionSliceLines(
220
+ mergedSection.filter((line) => /invariant|stable|policy|rule/i.test(line))
221
+ ).filter(isInvariantRuleLike);
222
+ const reflectionLinesLegacy = sanitizeReflectionSliceLines(
223
+ mergedSection.filter((line) => /reflect|inherit|derive|change|apply/i.test(line))
224
+ ).filter(isDerivedDeltaLike);
225
+ const openLoopLines = sanitizeReflectionSliceLines(parseSectionBullets(reflectionText, "Open loops / next actions"))
226
+ .filter(isOpenLoopAction)
227
+ .filter(isDerivedDeltaLike);
228
+ const durableDecisionLines = sanitizeReflectionSliceLines(parseSectionBullets(reflectionText, "Decisions (durable)"))
229
+ .filter(isInvariantRuleLike);
230
+
231
+ const invariants = invariantsPrimary.length > 0
232
+ ? invariantsPrimary
233
+ : (invariantLinesLegacy.length > 0 ? invariantLinesLegacy : durableDecisionLines);
234
+ const derived = derivedPrimary.length > 0
235
+ ? derivedPrimary
236
+ : [...reflectionLinesLegacy, ...openLoopLines];
237
+
238
+ return {
239
+ invariants: invariants.slice(0, 8),
240
+ derived: derived.slice(0, 10),
241
+ };
242
+ }
243
+
244
+ export function extractReflectionSliceItems(reflectionText: string): ReflectionSliceItem[] {
245
+ const slices = extractReflectionSlices(reflectionText);
246
+ const invariantGroupSize = slices.invariants.length;
247
+ const derivedGroupSize = slices.derived.length;
248
+
249
+ const invariantItems = slices.invariants.map((text, ordinal) => ({
250
+ text,
251
+ itemKind: "invariant" as const,
252
+ section: "Invariants" as const,
253
+ ordinal,
254
+ groupSize: invariantGroupSize,
255
+ }));
256
+ const derivedItems = slices.derived.map((text, ordinal) => ({
257
+ text,
258
+ itemKind: "derived" as const,
259
+ section: "Derived" as const,
260
+ ordinal,
261
+ groupSize: derivedGroupSize,
262
+ }));
263
+
264
+ return [...invariantItems, ...derivedItems];
265
+ }