mnemocyte 0.0.1 → 0.0.3
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/README.md +16 -0
- package/dist/index.d.mts +119 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +579 -4
- package/dist/index.mjs.map +1 -1
- package/migrations/0000_initial.sql +46 -0
- package/package.json +17 -10
- package/README +0 -6
package/README.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
> Warning
|
|
2
|
+
>
|
|
3
|
+
> Mnemocyte is in active early development.
|
|
4
|
+
> APIs may change significantly before v1.0.
|
|
5
|
+
>
|
|
6
|
+
> The package is currently being built in public.
|
|
7
|
+
|
|
8
|
+
## Current status
|
|
9
|
+
|
|
10
|
+
Mnemocyte is currently a stub package. The public entry point exports
|
|
11
|
+
`createMnemocyte()`, which throws until the first usable implementation is built.
|
|
12
|
+
|
|
13
|
+
The package is ESM-only. Use `import` rather than CommonJS `require`.
|
|
14
|
+
|
|
15
|
+
See `ARCHITECTURE.md` for the planned production architecture and
|
|
16
|
+
`ARCHITECTURE_REVIEW.md` for the implementation review and roadmap.
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,121 @@
|
|
|
1
|
-
//#region src/
|
|
2
|
-
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
type MemoryType = "fact" | "preference" | "instruction" | "backstory" | "episode" | "session";
|
|
3
|
+
type ImportanceLevel = "low" | "normal" | "high" | "critical";
|
|
4
|
+
type ContextFormat = "markdown" | "plain" | "xml";
|
|
5
|
+
interface MnemocyteConfig {
|
|
6
|
+
databaseUrl?: string;
|
|
7
|
+
embedder: Embedder;
|
|
8
|
+
defaults?: {
|
|
9
|
+
limit?: number;
|
|
10
|
+
minScore?: number;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
interface Embedder {
|
|
14
|
+
readonly model: string;
|
|
15
|
+
readonly dimensions: number;
|
|
16
|
+
embed(texts: readonly string[]): Promise<number[][]>;
|
|
17
|
+
}
|
|
18
|
+
interface Memory {
|
|
19
|
+
id: string;
|
|
20
|
+
entityId: string;
|
|
21
|
+
content: string;
|
|
22
|
+
type: MemoryType;
|
|
23
|
+
importance: ImportanceLevel;
|
|
24
|
+
tags: string[];
|
|
25
|
+
source: string | null;
|
|
26
|
+
metadata: Record<string, unknown>;
|
|
27
|
+
confidence: number;
|
|
28
|
+
embeddingModel: string;
|
|
29
|
+
embeddingDimensions: number;
|
|
30
|
+
supersededBy: string | null;
|
|
31
|
+
expiresAt: Date | null;
|
|
32
|
+
lastAccessedAt: Date | null;
|
|
33
|
+
accessCount: number;
|
|
34
|
+
createdAt: Date;
|
|
35
|
+
updatedAt: Date;
|
|
36
|
+
}
|
|
37
|
+
interface RetrievalScores {
|
|
38
|
+
vector: number;
|
|
39
|
+
lexical: number;
|
|
40
|
+
recency: number;
|
|
41
|
+
}
|
|
42
|
+
interface RetrievalExplanation {
|
|
43
|
+
vectorScore: number;
|
|
44
|
+
lexicalScore: number;
|
|
45
|
+
recencyScore: number;
|
|
46
|
+
importanceBoost: number;
|
|
47
|
+
finalScore: number;
|
|
48
|
+
}
|
|
49
|
+
interface MemoryWithScore extends Memory {
|
|
50
|
+
score: number;
|
|
51
|
+
scores: RetrievalScores;
|
|
52
|
+
explanation: RetrievalExplanation | null;
|
|
53
|
+
}
|
|
54
|
+
interface RememberInput {
|
|
55
|
+
entityId: string;
|
|
56
|
+
content: string;
|
|
57
|
+
type?: MemoryType;
|
|
58
|
+
importance?: ImportanceLevel;
|
|
59
|
+
tags?: string[];
|
|
60
|
+
source?: string;
|
|
61
|
+
metadata?: Record<string, unknown>;
|
|
62
|
+
confidence?: number;
|
|
63
|
+
expiresAt?: Date;
|
|
64
|
+
}
|
|
65
|
+
interface RecallInput {
|
|
66
|
+
entityId: string;
|
|
67
|
+
query: string;
|
|
68
|
+
limit?: number;
|
|
69
|
+
minScore?: number;
|
|
70
|
+
types?: MemoryType[];
|
|
71
|
+
tags?: string[];
|
|
72
|
+
before?: Date;
|
|
73
|
+
after?: Date;
|
|
74
|
+
includeSuperseded?: boolean;
|
|
75
|
+
includeExpired?: boolean;
|
|
76
|
+
explain?: boolean;
|
|
77
|
+
}
|
|
78
|
+
interface EntityStats {
|
|
79
|
+
entityId: string;
|
|
80
|
+
memoryCount: number;
|
|
81
|
+
activeMemoryCount: number;
|
|
82
|
+
expiredMemoryCount: number;
|
|
83
|
+
supersededMemoryCount: number;
|
|
84
|
+
}
|
|
85
|
+
interface GlobalStats {
|
|
86
|
+
entityCount: number;
|
|
87
|
+
memoryCount: number;
|
|
88
|
+
activeMemoryCount: number;
|
|
89
|
+
expiredMemoryCount: number;
|
|
90
|
+
supersededMemoryCount: number;
|
|
91
|
+
}
|
|
92
|
+
interface MnemocyteClient {
|
|
93
|
+
remember(input: RememberInput): Promise<Memory>;
|
|
94
|
+
rememberMany(inputs: readonly RememberInput[]): Promise<Memory[]>;
|
|
95
|
+
recall(input: RecallInput): Promise<MemoryWithScore[]>;
|
|
96
|
+
forget(input: {
|
|
97
|
+
entityId: string;
|
|
98
|
+
memoryId: string;
|
|
99
|
+
}): Promise<void>;
|
|
100
|
+
forgetAll(input: {
|
|
101
|
+
entityId: string;
|
|
102
|
+
}): Promise<void>;
|
|
103
|
+
stats(input?: {
|
|
104
|
+
entityId?: string;
|
|
105
|
+
}): Promise<EntityStats | GlobalStats>;
|
|
106
|
+
close(): Promise<void>;
|
|
107
|
+
}
|
|
3
108
|
//#endregion
|
|
4
|
-
|
|
109
|
+
//#region src/client.d.ts
|
|
110
|
+
declare function createMnemocyte(config: MnemocyteConfig): MnemocyteClient;
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region src/errors.d.ts
|
|
113
|
+
type MnemocyteErrorCode = "CONFIG" | "VALIDATION" | "DB" | "EMBEDDING" | "NOT_FOUND" | "MIGRATION";
|
|
114
|
+
declare class MnemocyteError extends Error {
|
|
115
|
+
readonly code: MnemocyteErrorCode;
|
|
116
|
+
readonly cause?: unknown;
|
|
117
|
+
constructor(message: string, code: MnemocyteErrorCode, cause?: unknown);
|
|
118
|
+
}
|
|
119
|
+
//#endregion
|
|
120
|
+
export { type ContextFormat, type Embedder, type EntityStats, type GlobalStats, type ImportanceLevel, type Memory, type MemoryType, type MemoryWithScore, type MnemocyteClient, type MnemocyteConfig, MnemocyteError, type MnemocyteErrorCode, type RecallInput, type RememberInput, type RetrievalExplanation, type RetrievalScores, createMnemocyte };
|
|
5
121
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/client.ts","../src/errors.ts"],"mappings":";KAAY,UAAA;AAAA,KAQA,eAAA;AAAA,KAEA,aAAA;AAAA,UAEK,eAAA;EAChB,WAAA;EACA,QAAA,EAAU,QAAA;EACV,QAAA;IACC,KAAA;IACA,QAAA;EAAA;AAAA;AAAA,UAIe,QAAA;EAAA,SACP,KAAA;EAAA,SACA,UAAA;EACT,KAAA,CAAM,KAAA,sBAA2B,OAAA;AAAA;AAAA,UAGjB,MAAA;EAChB,EAAA;EACA,QAAA;EACA,OAAA;EACA,IAAA,EAAM,UAAA;EACN,UAAA,EAAY,eAAA;EACZ,IAAA;EACA,MAAA;EACA,QAAA,EAAU,MAAA;EACV,UAAA;EACA,cAAA;EACA,mBAAA;EACA,YAAA;EACA,SAAA,EAAW,IAAA;EACX,cAAA,EAAgB,IAAA;EAChB,WAAA;EACA,SAAA,EAAW,IAAA;EACX,SAAA,EAAW,IAAA;AAAA;AAAA,UAGK,eAAA;EAChB,MAAA;EACA,OAAA;EACA,OAAA;AAAA;AAAA,UAGgB,oBAAA;EAChB,WAAA;EACA,YAAA;EACA,YAAA;EACA,eAAA;EACA,UAAA;AAAA;AAAA,UAGgB,eAAA,SAAwB,MAAA;EACxC,KAAA;EACA,MAAA,EAAQ,eAAA;EACR,WAAA,EAAa,oBAAA;AAAA;AAAA,UAGG,aAAA;EAChB,QAAA;EACA,OAAA;EACA,IAAA,GAAO,UAAA;EACP,UAAA,GAAa,eAAA;EACb,IAAA;EACA,MAAA;EACA,QAAA,GAAW,MAAA;EACX,UAAA;EACA,SAAA,GAAY,IAAA;AAAA;AAAA,UAGI,WAAA;EAChB,QAAA;EACA,KAAA;EACA,KAAA;EACA,QAAA;EACA,KAAA,GAAQ,UAAA;EACR,IAAA;EACA,MAAA,GAAS,IAAA;EACT,KAAA,GAAQ,IAAA;EACR,iBAAA;EACA,cAAA;EACA,OAAA;AAAA;AAAA,UAGgB,WAAA;EAChB,QAAA;EACA,WAAA;EACA,iBAAA;EACA,kBAAA;EACA,qBAAA;AAAA;AAAA,UAGgB,WAAA;EAChB,WAAA;EACA,WAAA;EACA,iBAAA;EACA,kBAAA;EACA,qBAAA;AAAA;AAAA,UAGgB,eAAA;EAChB,QAAA,CAAS,KAAA,EAAO,aAAA,GAAgB,OAAA,CAAQ,MAAA;EACxC,YAAA,CAAa,MAAA,WAAiB,aAAA,KAAkB,OAAA,CAAQ,MAAA;EACxD,MAAA,CAAO,KAAA,EAAO,WAAA,GAAc,OAAA,CAAQ,eAAA;EACpC,MAAA,CAAO,KAAA;IAAS,QAAA;IAAkB,QAAA;EAAA,IAAqB,OAAA;EACvD,SAAA,CAAU,KAAA;IAAS,QAAA;EAAA,IAAqB,OAAA;EACxC,KAAA,CAAM,KAAA;IAAU,QAAA;EAAA,IAAsB,OAAA,CAAQ,WAAA,GAAc,WAAA;EAC5D,KAAA,IAAS,OAAA;AAAA;;;iBC9GM,eAAA,CAAgB,MAAA,EAAQ,eAAA,GAAkB,eAAA;;;KCN9C,kBAAA;AAAA,cAQC,cAAA,SAAuB,KAAA;EAAA,SAC1B,IAAA,EAAM,kBAAA;EAAA,SACG,KAAA;cAEN,OAAA,UAAiB,IAAA,EAAM,kBAAA,EAAoB,KAAA;AAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,583 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { drizzle } from "drizzle-orm/postgres-js";
|
|
2
|
+
import postgres from "postgres";
|
|
3
|
+
import { and, eq, gt, inArray, isNull, lt, sql } from "drizzle-orm";
|
|
4
|
+
import { customType, index, integer, jsonb, pgTable, real, text, timestamp } from "drizzle-orm/pg-core";
|
|
5
|
+
//#region \0rolldown/runtime.js
|
|
6
|
+
var __defProp = Object.defineProperty;
|
|
7
|
+
var __exportAll = (all, no_symbols) => {
|
|
8
|
+
let target = {};
|
|
9
|
+
for (var name in all) __defProp(target, name, {
|
|
10
|
+
get: all[name],
|
|
11
|
+
enumerable: true
|
|
12
|
+
});
|
|
13
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
14
|
+
return target;
|
|
15
|
+
};
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region src/db/schema.ts
|
|
18
|
+
var schema_exports = /* @__PURE__ */ __exportAll({
|
|
19
|
+
eventsTable: () => eventsTable,
|
|
20
|
+
memoriesTable: () => memoriesTable
|
|
21
|
+
});
|
|
22
|
+
const vector = customType({
|
|
23
|
+
dataType(config) {
|
|
24
|
+
return `vector(${typeof config === "number" ? config : 1536})`;
|
|
25
|
+
},
|
|
26
|
+
toDriver(value) {
|
|
27
|
+
return `[${value.join(",")}]`;
|
|
28
|
+
},
|
|
29
|
+
fromDriver(value) {
|
|
30
|
+
return value.slice(1, -1).split(",").filter(Boolean).map(Number);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
const memoriesTable = pgTable("mnemocyte_memories", {
|
|
34
|
+
id: text("id").primaryKey(),
|
|
35
|
+
entityId: text("entity_id").notNull(),
|
|
36
|
+
content: text("content").notNull(),
|
|
37
|
+
type: text("type").notNull().default("fact"),
|
|
38
|
+
importance: text("importance").notNull().default("normal"),
|
|
39
|
+
tags: text("tags").array().notNull().default(sql`'{}'::text[]`),
|
|
40
|
+
source: text("source"),
|
|
41
|
+
metadata: jsonb("metadata").notNull().default({}),
|
|
42
|
+
confidence: real("confidence").notNull().default(1),
|
|
43
|
+
embedding: vector("embedding", { dimensions: 1536 }),
|
|
44
|
+
embeddingModel: text("embedding_model").notNull(),
|
|
45
|
+
embeddingDimensions: integer("embedding_dimensions").notNull(),
|
|
46
|
+
supersededBy: text("superseded_by"),
|
|
47
|
+
supersededAt: timestamp("superseded_at", { withTimezone: true }),
|
|
48
|
+
expiresAt: timestamp("expires_at", { withTimezone: true }),
|
|
49
|
+
lastAccessedAt: timestamp("last_accessed_at", { withTimezone: true }),
|
|
50
|
+
accessCount: integer("access_count").notNull().default(0),
|
|
51
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
52
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
53
|
+
}, (table) => [index("mnemocyte_memories_entity_idx").on(table.entityId), index("mnemocyte_memories_entity_type_idx").on(table.entityId, table.type)]);
|
|
54
|
+
const eventsTable = pgTable("mnemocyte_events", {
|
|
55
|
+
id: text("id").primaryKey(),
|
|
56
|
+
entityId: text("entity_id").notNull(),
|
|
57
|
+
description: text("description").notNull(),
|
|
58
|
+
metadata: jsonb("metadata").notNull().default({}),
|
|
59
|
+
timestamp: timestamp("timestamp", { withTimezone: true }).notNull().defaultNow()
|
|
60
|
+
}, (table) => [index("mnemocyte_events_entity_time_idx").on(table.entityId, table.timestamp)]);
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/db/index.ts
|
|
63
|
+
function createDatabase(databaseUrl) {
|
|
64
|
+
const client = postgres(databaseUrl, {
|
|
65
|
+
max: 10,
|
|
66
|
+
idle_timeout: 20,
|
|
67
|
+
connect_timeout: 10
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
db: drizzle(client, { schema: schema_exports }),
|
|
71
|
+
async close() {
|
|
72
|
+
await client.end();
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/errors.ts
|
|
78
|
+
var MnemocyteError = class extends Error {
|
|
79
|
+
code;
|
|
80
|
+
cause;
|
|
81
|
+
constructor(message, code, cause) {
|
|
82
|
+
super(message);
|
|
83
|
+
this.name = "MnemocyteError";
|
|
84
|
+
this.code = code;
|
|
85
|
+
this.cause = cause;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
function assertNonEmptyString(value, field) {
|
|
89
|
+
if (value.trim().length === 0) throw new MnemocyteError(`${field} must be a non-empty string.`, "VALIDATION");
|
|
90
|
+
}
|
|
91
|
+
function assertLimit(value) {
|
|
92
|
+
if (!Number.isInteger(value) || value < 1) throw new MnemocyteError("limit must be a positive integer.", "VALIDATION");
|
|
93
|
+
}
|
|
94
|
+
function assertMinScore(value) {
|
|
95
|
+
if (!Number.isFinite(value) || value < 0 || value > 1) throw new MnemocyteError("minScore must be a number between 0 and 1.", "VALIDATION");
|
|
96
|
+
}
|
|
97
|
+
function assertEmbedder(embedder) {
|
|
98
|
+
if (!embedder || typeof embedder.embed !== "function") throw new MnemocyteError("embedder with an embed function is required.", "CONFIG");
|
|
99
|
+
assertNonEmptyString(embedder.model, "embedder.model");
|
|
100
|
+
if (!Number.isInteger(embedder.dimensions) || embedder.dimensions < 1) throw new MnemocyteError("embedder.dimensions must be a positive integer.", "CONFIG");
|
|
101
|
+
}
|
|
102
|
+
function normalizeTags(tags) {
|
|
103
|
+
if (!tags) return [];
|
|
104
|
+
return [...new Set(tags.map((tag) => tag.trim()).filter(Boolean))];
|
|
105
|
+
}
|
|
106
|
+
function createId() {
|
|
107
|
+
return `mem_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
108
|
+
}
|
|
109
|
+
function isExpired(memory, now) {
|
|
110
|
+
return memory.expiresAt !== null && memory.expiresAt.getTime() <= now.getTime();
|
|
111
|
+
}
|
|
112
|
+
function cloneMemory(memory) {
|
|
113
|
+
return {
|
|
114
|
+
...memory,
|
|
115
|
+
tags: [...memory.tags],
|
|
116
|
+
metadata: { ...memory.metadata },
|
|
117
|
+
expiresAt: memory.expiresAt ? new Date(memory.expiresAt) : null,
|
|
118
|
+
lastAccessedAt: memory.lastAccessedAt ? new Date(memory.lastAccessedAt) : null,
|
|
119
|
+
createdAt: new Date(memory.createdAt),
|
|
120
|
+
updatedAt: new Date(memory.updatedAt)
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function rowToMemory(row) {
|
|
124
|
+
return {
|
|
125
|
+
id: row.id,
|
|
126
|
+
entityId: row.entityId,
|
|
127
|
+
content: row.content,
|
|
128
|
+
type: row.type,
|
|
129
|
+
importance: row.importance,
|
|
130
|
+
tags: row.tags,
|
|
131
|
+
source: row.source,
|
|
132
|
+
metadata: row.metadata,
|
|
133
|
+
confidence: row.confidence,
|
|
134
|
+
embeddingModel: row.embeddingModel,
|
|
135
|
+
embeddingDimensions: row.embeddingDimensions,
|
|
136
|
+
supersededBy: row.supersededBy,
|
|
137
|
+
expiresAt: row.expiresAt,
|
|
138
|
+
lastAccessedAt: row.lastAccessedAt,
|
|
139
|
+
accessCount: row.accessCount,
|
|
140
|
+
createdAt: row.createdAt,
|
|
141
|
+
updatedAt: row.updatedAt
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
async function embedOne(embedder, text) {
|
|
145
|
+
let embeddings;
|
|
146
|
+
try {
|
|
147
|
+
embeddings = await embedder.embed([text]);
|
|
148
|
+
} catch (error) {
|
|
149
|
+
throw new MnemocyteError("Failed to embed text.", "EMBEDDING", error);
|
|
150
|
+
}
|
|
151
|
+
const embedding = embeddings[0];
|
|
152
|
+
if (!embedding) throw new MnemocyteError("Embedder returned no embedding.", "EMBEDDING");
|
|
153
|
+
if (embedding.length !== embedder.dimensions) throw new MnemocyteError("Embedder returned an embedding with unexpected dimensions.", "EMBEDDING");
|
|
154
|
+
return embedding;
|
|
155
|
+
}
|
|
156
|
+
function validateRememberInput(input) {
|
|
157
|
+
assertNonEmptyString(input.entityId, "entityId");
|
|
158
|
+
assertNonEmptyString(input.content, "content");
|
|
159
|
+
if (input.confidence !== void 0 && (!Number.isFinite(input.confidence) || input.confidence < 0 || input.confidence > 1)) throw new MnemocyteError("confidence must be a number between 0 and 1.", "VALIDATION");
|
|
160
|
+
}
|
|
161
|
+
function validateRecallInput(input) {
|
|
162
|
+
assertNonEmptyString(input.entityId, "entityId");
|
|
163
|
+
assertNonEmptyString(input.query, "query");
|
|
164
|
+
if (input.limit !== void 0) assertLimit(input.limit);
|
|
165
|
+
if (input.minScore !== void 0) assertMinScore(input.minScore);
|
|
166
|
+
}
|
|
167
|
+
function matchesRecallFilter(memory, input, now) {
|
|
168
|
+
if (memory.entityId !== input.entityId) return false;
|
|
169
|
+
if (!input.includeSuperseded && memory.supersededBy !== null) return false;
|
|
170
|
+
if (!input.includeExpired && isExpired(memory, now)) return false;
|
|
171
|
+
if (input.types && !input.types.includes(memory.type)) return false;
|
|
172
|
+
if (input.tags && !input.tags.every((tag) => memory.tags.includes(tag))) return false;
|
|
173
|
+
if (input.before && memory.createdAt >= input.before) return false;
|
|
174
|
+
if (input.after && memory.createdAt <= input.after) return false;
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
//#endregion
|
|
178
|
+
//#region src/retrieval/scorer.ts
|
|
179
|
+
const IMPORTANCE_BOOSTS = {
|
|
180
|
+
low: -.05,
|
|
181
|
+
normal: 0,
|
|
182
|
+
high: .08,
|
|
183
|
+
critical: .15
|
|
184
|
+
};
|
|
185
|
+
function cosineSimilarity(a, b) {
|
|
186
|
+
let dot = 0;
|
|
187
|
+
let aMagnitude = 0;
|
|
188
|
+
let bMagnitude = 0;
|
|
189
|
+
for (let i = 0; i < a.length; i += 1) {
|
|
190
|
+
const av = a[i] ?? 0;
|
|
191
|
+
const bv = b[i] ?? 0;
|
|
192
|
+
dot += av * bv;
|
|
193
|
+
aMagnitude += av * av;
|
|
194
|
+
bMagnitude += bv * bv;
|
|
195
|
+
}
|
|
196
|
+
if (aMagnitude === 0 || bMagnitude === 0) return 0;
|
|
197
|
+
return dot / (Math.sqrt(aMagnitude) * Math.sqrt(bMagnitude));
|
|
198
|
+
}
|
|
199
|
+
function lexicalScore(content, query) {
|
|
200
|
+
const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
201
|
+
if (terms.length === 0) return 0;
|
|
202
|
+
const lowerContent = content.toLowerCase();
|
|
203
|
+
return terms.filter((term) => lowerContent.includes(term)).length / terms.length;
|
|
204
|
+
}
|
|
205
|
+
function recencyScore(createdAt) {
|
|
206
|
+
const ageMs = Date.now() - createdAt.getTime();
|
|
207
|
+
const ageDays = Math.max(0, ageMs / 864e5);
|
|
208
|
+
return Math.exp(-Math.LN2 * ageDays / 90);
|
|
209
|
+
}
|
|
210
|
+
function toScoredMemory(memory, vector, lexical, input) {
|
|
211
|
+
const recency = recencyScore(memory.createdAt);
|
|
212
|
+
const importanceBoost = IMPORTANCE_BOOSTS[memory.importance];
|
|
213
|
+
const score = Math.max(0, Math.min(1, vector * .7 + lexical * .2 + recency * .1 + importanceBoost));
|
|
214
|
+
return {
|
|
215
|
+
...cloneMemory(memory),
|
|
216
|
+
score,
|
|
217
|
+
scores: {
|
|
218
|
+
vector,
|
|
219
|
+
lexical,
|
|
220
|
+
recency
|
|
221
|
+
},
|
|
222
|
+
explanation: input.explain ? {
|
|
223
|
+
vectorScore: vector,
|
|
224
|
+
lexicalScore: lexical,
|
|
225
|
+
recencyScore: recency,
|
|
226
|
+
importanceBoost,
|
|
227
|
+
finalScore: score
|
|
228
|
+
} : null
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
//#endregion
|
|
232
|
+
//#region src/memory/in-memory.ts
|
|
233
|
+
function createInMemoryClient(config) {
|
|
234
|
+
const memories = /* @__PURE__ */ new Map();
|
|
235
|
+
let closed = false;
|
|
236
|
+
function assertOpen() {
|
|
237
|
+
if (closed) throw new MnemocyteError("Mnemocyte client is closed.", "DB");
|
|
238
|
+
}
|
|
239
|
+
async function remember(input) {
|
|
240
|
+
assertOpen();
|
|
241
|
+
validateRememberInput(input);
|
|
242
|
+
const embedding = await embedOne(config.embedder, input.content);
|
|
243
|
+
const now = /* @__PURE__ */ new Date();
|
|
244
|
+
const memory = {
|
|
245
|
+
id: createId(),
|
|
246
|
+
entityId: input.entityId,
|
|
247
|
+
content: input.content,
|
|
248
|
+
type: input.type ?? "fact",
|
|
249
|
+
importance: input.importance ?? "normal",
|
|
250
|
+
tags: normalizeTags(input.tags),
|
|
251
|
+
source: input.source ?? null,
|
|
252
|
+
metadata: input.metadata ?? {},
|
|
253
|
+
confidence: input.confidence ?? 1,
|
|
254
|
+
embeddingModel: config.embedder.model,
|
|
255
|
+
embeddingDimensions: config.embedder.dimensions,
|
|
256
|
+
supersededBy: null,
|
|
257
|
+
expiresAt: input.expiresAt ?? null,
|
|
258
|
+
lastAccessedAt: null,
|
|
259
|
+
accessCount: 0,
|
|
260
|
+
createdAt: now,
|
|
261
|
+
updatedAt: now,
|
|
262
|
+
embedding
|
|
263
|
+
};
|
|
264
|
+
memories.set(memory.id, memory);
|
|
265
|
+
return cloneMemory(memory);
|
|
266
|
+
}
|
|
267
|
+
return {
|
|
268
|
+
remember,
|
|
269
|
+
async rememberMany(inputs) {
|
|
270
|
+
assertOpen();
|
|
271
|
+
const result = [];
|
|
272
|
+
for (const input of inputs) result.push(await remember(input));
|
|
273
|
+
return result;
|
|
274
|
+
},
|
|
275
|
+
async recall(input) {
|
|
276
|
+
assertOpen();
|
|
277
|
+
validateRecallInput(input);
|
|
278
|
+
const limit = input.limit ?? config.defaults?.limit ?? 10;
|
|
279
|
+
const minScore = input.minScore ?? config.defaults?.minScore ?? 0;
|
|
280
|
+
assertLimit(limit);
|
|
281
|
+
assertMinScore(minScore);
|
|
282
|
+
const queryEmbedding = await embedOne(config.embedder, input.query);
|
|
283
|
+
const now = /* @__PURE__ */ new Date();
|
|
284
|
+
const scored = Array.from(memories.values()).filter((memory) => matchesRecallFilter(memory, input, now)).map((memory) => toScoredMemory(memory, Math.max(0, cosineSimilarity(memory.embedding, queryEmbedding)), lexicalScore(memory.content, input.query), input)).filter((memory) => memory.score >= minScore).sort((a, b) => b.score - a.score).slice(0, limit);
|
|
285
|
+
for (const memory of scored) {
|
|
286
|
+
const stored = memories.get(memory.id);
|
|
287
|
+
if (stored) {
|
|
288
|
+
stored.lastAccessedAt = now;
|
|
289
|
+
stored.accessCount += 1;
|
|
290
|
+
stored.updatedAt = now;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return scored;
|
|
294
|
+
},
|
|
295
|
+
async forget(input) {
|
|
296
|
+
assertOpen();
|
|
297
|
+
assertNonEmptyString(input.entityId, "entityId");
|
|
298
|
+
assertNonEmptyString(input.memoryId, "memoryId");
|
|
299
|
+
const memory = memories.get(input.memoryId);
|
|
300
|
+
if (!memory || memory.entityId !== input.entityId) throw new MnemocyteError("Memory was not found.", "NOT_FOUND");
|
|
301
|
+
memories.delete(input.memoryId);
|
|
302
|
+
},
|
|
303
|
+
async forgetAll(input) {
|
|
304
|
+
assertOpen();
|
|
305
|
+
assertNonEmptyString(input.entityId, "entityId");
|
|
306
|
+
for (const [id, memory] of memories) if (memory.entityId === input.entityId) memories.delete(id);
|
|
307
|
+
},
|
|
308
|
+
async stats(input) {
|
|
309
|
+
assertOpen();
|
|
310
|
+
const now = /* @__PURE__ */ new Date();
|
|
311
|
+
const allMemories = Array.from(memories.values());
|
|
312
|
+
const selected = input?.entityId ? allMemories.filter((memory) => memory.entityId === input.entityId) : allMemories;
|
|
313
|
+
const activeMemoryCount = selected.filter((memory) => !isExpired(memory, now) && memory.supersededBy === null).length;
|
|
314
|
+
const expiredMemoryCount = selected.filter((memory) => isExpired(memory, now)).length;
|
|
315
|
+
const supersededMemoryCount = selected.filter((memory) => memory.supersededBy !== null).length;
|
|
316
|
+
if (input?.entityId) return {
|
|
317
|
+
entityId: input.entityId,
|
|
318
|
+
memoryCount: selected.length,
|
|
319
|
+
activeMemoryCount,
|
|
320
|
+
expiredMemoryCount,
|
|
321
|
+
supersededMemoryCount
|
|
322
|
+
};
|
|
323
|
+
return {
|
|
324
|
+
entityCount: new Set(allMemories.map((memory) => memory.entityId)).size,
|
|
325
|
+
memoryCount: selected.length,
|
|
326
|
+
activeMemoryCount,
|
|
327
|
+
expiredMemoryCount,
|
|
328
|
+
supersededMemoryCount
|
|
329
|
+
};
|
|
330
|
+
},
|
|
331
|
+
async close() {
|
|
332
|
+
closed = true;
|
|
333
|
+
memories.clear();
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
//#endregion
|
|
338
|
+
//#region src/db/queries/events.ts
|
|
339
|
+
async function deleteEventsForEntity(db, entityId) {
|
|
340
|
+
return (await db.delete(eventsTable).where(eq(eventsTable.entityId, entityId)).returning({ id: eventsTable.id })).length;
|
|
341
|
+
}
|
|
342
|
+
//#endregion
|
|
343
|
+
//#region src/db/queries/memories.ts
|
|
344
|
+
function vectorLiteral(embedding) {
|
|
345
|
+
return `[${embedding.join(",")}]`;
|
|
346
|
+
}
|
|
347
|
+
function filterConditions(filter) {
|
|
348
|
+
return and(eq(memoriesTable.entityId, filter.entityId), filter.includeSuperseded ? void 0 : isNull(memoriesTable.supersededBy), filter.includeExpired ? void 0 : sql`(${memoriesTable.expiresAt} IS NULL OR ${memoriesTable.expiresAt} > now())`, filter.types && filter.types.length > 0 ? inArray(memoriesTable.type, [...filter.types]) : void 0, filter.tags && filter.tags.length > 0 ? sql`${memoriesTable.tags} && ${filter.tags}` : void 0, filter.before ? lt(memoriesTable.createdAt, filter.before) : void 0, filter.after ? gt(memoriesTable.createdAt, filter.after) : void 0);
|
|
349
|
+
}
|
|
350
|
+
async function insertMemory(db, row) {
|
|
351
|
+
const inserted = (await db.insert(memoriesTable).values(row).returning())[0];
|
|
352
|
+
if (!inserted) throw new Error("Memory insert returned no rows.");
|
|
353
|
+
return inserted;
|
|
354
|
+
}
|
|
355
|
+
async function listMemories(db, filter) {
|
|
356
|
+
return db.select().from(memoriesTable).where(filterConditions(filter));
|
|
357
|
+
}
|
|
358
|
+
async function deleteMemory(db, entityId, memoryId) {
|
|
359
|
+
return (await db.delete(memoriesTable).where(and(eq(memoriesTable.entityId, entityId), eq(memoriesTable.id, memoryId))).returning({ id: memoriesTable.id })).length > 0;
|
|
360
|
+
}
|
|
361
|
+
async function deleteMemoriesForEntity(db, entityId) {
|
|
362
|
+
return (await db.delete(memoriesTable).where(eq(memoriesTable.entityId, entityId)).returning({ id: memoriesTable.id })).length;
|
|
363
|
+
}
|
|
364
|
+
async function markMemoryAccessed(db, memoryIds) {
|
|
365
|
+
if (memoryIds.length === 0) return;
|
|
366
|
+
await db.update(memoriesTable).set({
|
|
367
|
+
lastAccessedAt: /* @__PURE__ */ new Date(),
|
|
368
|
+
accessCount: sql`${memoriesTable.accessCount} + 1`,
|
|
369
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
370
|
+
}).where(inArray(memoriesTable.id, [...memoryIds]));
|
|
371
|
+
}
|
|
372
|
+
async function vectorSearch(db, input) {
|
|
373
|
+
const embedding = vectorLiteral(input.embedding);
|
|
374
|
+
const minScore = input.minScore ?? 0;
|
|
375
|
+
return await db.execute(sql`
|
|
376
|
+
SELECT
|
|
377
|
+
id,
|
|
378
|
+
entity_id AS "entityId",
|
|
379
|
+
content,
|
|
380
|
+
type,
|
|
381
|
+
importance,
|
|
382
|
+
tags,
|
|
383
|
+
source,
|
|
384
|
+
metadata,
|
|
385
|
+
confidence,
|
|
386
|
+
embedding,
|
|
387
|
+
embedding_model AS "embeddingModel",
|
|
388
|
+
embedding_dimensions AS "embeddingDimensions",
|
|
389
|
+
superseded_by AS "supersededBy",
|
|
390
|
+
superseded_at AS "supersededAt",
|
|
391
|
+
expires_at AS "expiresAt",
|
|
392
|
+
last_accessed_at AS "lastAccessedAt",
|
|
393
|
+
access_count AS "accessCount",
|
|
394
|
+
created_at AS "createdAt",
|
|
395
|
+
updated_at AS "updatedAt",
|
|
396
|
+
1 - (embedding <=> ${embedding}::vector) AS "vectorScore"
|
|
397
|
+
FROM mnemocyte_memories
|
|
398
|
+
WHERE
|
|
399
|
+
entity_id = ${input.entityId}
|
|
400
|
+
AND embedding IS NOT NULL
|
|
401
|
+
${input.includeSuperseded ? sql`` : sql`AND superseded_by IS NULL`}
|
|
402
|
+
${input.includeExpired ? sql`` : sql`AND (expires_at IS NULL OR expires_at > now())`}
|
|
403
|
+
${input.types && input.types.length > 0 ? sql`AND type IN (${sql.join(input.types.map((type) => sql`${type}`), sql`, `)})` : sql``}
|
|
404
|
+
${input.tags && input.tags.length > 0 ? sql`AND tags && ${input.tags}` : sql``}
|
|
405
|
+
${input.before ? sql`AND created_at < ${input.before}` : sql``}
|
|
406
|
+
${input.after ? sql`AND created_at > ${input.after}` : sql``}
|
|
407
|
+
AND 1 - (embedding <=> ${embedding}::vector) >= ${minScore}
|
|
408
|
+
ORDER BY embedding <=> ${embedding}::vector
|
|
409
|
+
LIMIT ${input.limit}
|
|
410
|
+
`);
|
|
411
|
+
}
|
|
412
|
+
async function lexicalSearch(db, input) {
|
|
413
|
+
return await db.execute(sql`
|
|
414
|
+
SELECT
|
|
415
|
+
id,
|
|
416
|
+
entity_id AS "entityId",
|
|
417
|
+
content,
|
|
418
|
+
type,
|
|
419
|
+
importance,
|
|
420
|
+
tags,
|
|
421
|
+
source,
|
|
422
|
+
metadata,
|
|
423
|
+
confidence,
|
|
424
|
+
embedding,
|
|
425
|
+
embedding_model AS "embeddingModel",
|
|
426
|
+
embedding_dimensions AS "embeddingDimensions",
|
|
427
|
+
superseded_by AS "supersededBy",
|
|
428
|
+
superseded_at AS "supersededAt",
|
|
429
|
+
expires_at AS "expiresAt",
|
|
430
|
+
last_accessed_at AS "lastAccessedAt",
|
|
431
|
+
access_count AS "accessCount",
|
|
432
|
+
created_at AS "createdAt",
|
|
433
|
+
updated_at AS "updatedAt",
|
|
434
|
+
ts_rank(to_tsvector('english', content), websearch_to_tsquery('english', ${input.query})) AS "lexicalScore"
|
|
435
|
+
FROM mnemocyte_memories
|
|
436
|
+
WHERE
|
|
437
|
+
entity_id = ${input.entityId}
|
|
438
|
+
${input.includeSuperseded ? sql`` : sql`AND superseded_by IS NULL`}
|
|
439
|
+
${input.includeExpired ? sql`` : sql`AND (expires_at IS NULL OR expires_at > now())`}
|
|
440
|
+
${input.types && input.types.length > 0 ? sql`AND type IN (${sql.join(input.types.map((type) => sql`${type}`), sql`, `)})` : sql``}
|
|
441
|
+
${input.tags && input.tags.length > 0 ? sql`AND tags && ${input.tags}` : sql``}
|
|
442
|
+
${input.before ? sql`AND created_at < ${input.before}` : sql``}
|
|
443
|
+
${input.after ? sql`AND created_at > ${input.after}` : sql``}
|
|
444
|
+
AND to_tsvector('english', content) @@ websearch_to_tsquery('english', ${input.query})
|
|
445
|
+
ORDER BY "lexicalScore" DESC
|
|
446
|
+
LIMIT ${input.limit}
|
|
447
|
+
`);
|
|
448
|
+
}
|
|
449
|
+
//#endregion
|
|
450
|
+
//#region src/memory/postgres.ts
|
|
451
|
+
function createPostgresClient(config, handle) {
|
|
452
|
+
let closed = false;
|
|
453
|
+
function assertOpen() {
|
|
454
|
+
if (closed) throw new MnemocyteError("Mnemocyte client is closed.", "DB");
|
|
455
|
+
}
|
|
456
|
+
async function remember(input) {
|
|
457
|
+
assertOpen();
|
|
458
|
+
validateRememberInput(input);
|
|
459
|
+
const embedding = await embedOne(config.embedder, input.content);
|
|
460
|
+
const now = /* @__PURE__ */ new Date();
|
|
461
|
+
return rowToMemory(await insertMemory(handle.db, {
|
|
462
|
+
id: createId(),
|
|
463
|
+
entityId: input.entityId,
|
|
464
|
+
content: input.content,
|
|
465
|
+
type: input.type ?? "fact",
|
|
466
|
+
importance: input.importance ?? "normal",
|
|
467
|
+
tags: normalizeTags(input.tags),
|
|
468
|
+
source: input.source ?? null,
|
|
469
|
+
metadata: input.metadata ?? {},
|
|
470
|
+
confidence: input.confidence ?? 1,
|
|
471
|
+
embedding,
|
|
472
|
+
embeddingModel: config.embedder.model,
|
|
473
|
+
embeddingDimensions: config.embedder.dimensions,
|
|
474
|
+
supersededBy: null,
|
|
475
|
+
supersededAt: null,
|
|
476
|
+
expiresAt: input.expiresAt ?? null,
|
|
477
|
+
lastAccessedAt: null,
|
|
478
|
+
accessCount: 0,
|
|
479
|
+
createdAt: now,
|
|
480
|
+
updatedAt: now
|
|
481
|
+
}));
|
|
482
|
+
}
|
|
483
|
+
return {
|
|
484
|
+
remember,
|
|
485
|
+
async rememberMany(inputs) {
|
|
486
|
+
assertOpen();
|
|
487
|
+
const result = [];
|
|
488
|
+
for (const input of inputs) result.push(await remember(input));
|
|
489
|
+
return result;
|
|
490
|
+
},
|
|
491
|
+
async recall(input) {
|
|
492
|
+
assertOpen();
|
|
493
|
+
validateRecallInput(input);
|
|
494
|
+
const limit = input.limit ?? config.defaults?.limit ?? 10;
|
|
495
|
+
const minScore = input.minScore ?? config.defaults?.minScore ?? 0;
|
|
496
|
+
assertLimit(limit);
|
|
497
|
+
assertMinScore(minScore);
|
|
498
|
+
const queryEmbedding = await embedOne(config.embedder, input.query);
|
|
499
|
+
const [vectorRows, lexicalRows] = await Promise.all([vectorSearch(handle.db, {
|
|
500
|
+
...input,
|
|
501
|
+
embedding: queryEmbedding,
|
|
502
|
+
limit: limit * 2,
|
|
503
|
+
minScore: 0
|
|
504
|
+
}), lexicalSearch(handle.db, {
|
|
505
|
+
...input,
|
|
506
|
+
limit: limit * 2
|
|
507
|
+
})]);
|
|
508
|
+
const merged = /* @__PURE__ */ new Map();
|
|
509
|
+
for (const row of vectorRows) merged.set(row.id, {
|
|
510
|
+
row,
|
|
511
|
+
vectorScore: row.vectorScore,
|
|
512
|
+
lexicalScore: 0
|
|
513
|
+
});
|
|
514
|
+
for (const row of lexicalRows) {
|
|
515
|
+
const existing = merged.get(row.id);
|
|
516
|
+
if (existing) existing.lexicalScore = row.lexicalScore;
|
|
517
|
+
else merged.set(row.id, {
|
|
518
|
+
row,
|
|
519
|
+
vectorScore: 0,
|
|
520
|
+
lexicalScore: row.lexicalScore
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
const scored = Array.from(merged.values()).map((entry) => toScoredMemory(rowToMemory(entry.row), Math.max(0, entry.vectorScore), Math.max(0, entry.lexicalScore), input)).filter((memory) => memory.score >= minScore).sort((a, b) => b.score - a.score).slice(0, limit);
|
|
524
|
+
await markMemoryAccessed(handle.db, scored.map((memory) => memory.id));
|
|
525
|
+
return scored;
|
|
526
|
+
},
|
|
527
|
+
async forget(input) {
|
|
528
|
+
assertOpen();
|
|
529
|
+
assertNonEmptyString(input.entityId, "entityId");
|
|
530
|
+
assertNonEmptyString(input.memoryId, "memoryId");
|
|
531
|
+
if (!await deleteMemory(handle.db, input.entityId, input.memoryId)) throw new MnemocyteError("Memory was not found.", "NOT_FOUND");
|
|
532
|
+
},
|
|
533
|
+
async forgetAll(input) {
|
|
534
|
+
assertOpen();
|
|
535
|
+
assertNonEmptyString(input.entityId, "entityId");
|
|
536
|
+
await Promise.all([deleteMemoriesForEntity(handle.db, input.entityId), deleteEventsForEntity(handle.db, input.entityId)]);
|
|
537
|
+
},
|
|
538
|
+
async stats(input) {
|
|
539
|
+
assertOpen();
|
|
540
|
+
const now = /* @__PURE__ */ new Date();
|
|
541
|
+
if (input?.entityId) {
|
|
542
|
+
const selected = await listMemories(handle.db, {
|
|
543
|
+
entityId: input.entityId,
|
|
544
|
+
includeExpired: true,
|
|
545
|
+
includeSuperseded: true
|
|
546
|
+
});
|
|
547
|
+
return {
|
|
548
|
+
entityId: input.entityId,
|
|
549
|
+
memoryCount: selected.length,
|
|
550
|
+
activeMemoryCount: selected.filter((memory) => !isExpired(rowToMemory(memory), now) && memory.supersededBy === null).length,
|
|
551
|
+
expiredMemoryCount: selected.filter((memory) => isExpired(rowToMemory(memory), now)).length,
|
|
552
|
+
supersededMemoryCount: selected.filter((memory) => memory.supersededBy !== null).length
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
const allMemories = await handle.db.select().from(memoriesTable);
|
|
556
|
+
return {
|
|
557
|
+
entityCount: new Set(allMemories.map((memory) => memory.entityId)).size,
|
|
558
|
+
memoryCount: allMemories.length,
|
|
559
|
+
activeMemoryCount: allMemories.filter((memory) => !isExpired(rowToMemory(memory), now) && memory.supersededBy === null).length,
|
|
560
|
+
expiredMemoryCount: allMemories.filter((memory) => isExpired(rowToMemory(memory), now)).length,
|
|
561
|
+
supersededMemoryCount: allMemories.filter((memory) => memory.supersededBy !== null).length
|
|
562
|
+
};
|
|
563
|
+
},
|
|
564
|
+
async close() {
|
|
565
|
+
closed = true;
|
|
566
|
+
await handle.close();
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
//#endregion
|
|
571
|
+
//#region src/client.ts
|
|
572
|
+
function createMnemocyte(config) {
|
|
573
|
+
assertEmbedder(config.embedder);
|
|
574
|
+
if (config.databaseUrl) {
|
|
575
|
+
assertNonEmptyString(config.databaseUrl, "databaseUrl");
|
|
576
|
+
return createPostgresClient(config, createDatabase(config.databaseUrl));
|
|
577
|
+
}
|
|
578
|
+
return createInMemoryClient(config);
|
|
4
579
|
}
|
|
5
580
|
//#endregion
|
|
6
|
-
export { createMnemocyte };
|
|
581
|
+
export { MnemocyteError, createMnemocyte };
|
|
7
582
|
|
|
8
583
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["export function createMnemocyte() {\n\tthrow new Error(\"Mnemocyte is currently in early development.\");\n}\n"],"mappings":";AAAA,SAAgB,kBAAkB;CACjC,MAAM,IAAI,MAAM,+CAA+C"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/db/schema.ts","../src/db/index.ts","../src/errors.ts","../src/memory/shared.ts","../src/retrieval/scorer.ts","../src/memory/in-memory.ts","../src/db/queries/events.ts","../src/db/queries/memories.ts","../src/memory/postgres.ts","../src/client.ts"],"sourcesContent":["import { sql } from \"drizzle-orm\";\nimport {\n\tcustomType,\n\tindex,\n\tinteger,\n\tjsonb,\n\tpgTable,\n\treal,\n\ttext,\n\ttimestamp,\n} from \"drizzle-orm/pg-core\";\n\nconst vector = customType<{ data: number[]; driverData: string }>({\n\tdataType(config) {\n\t\tconst dimensions = typeof config === \"number\" ? config : 1536;\n\t\treturn `vector(${dimensions})`;\n\t},\n\ttoDriver(value) {\n\t\treturn `[${value.join(\",\")}]`;\n\t},\n\tfromDriver(value) {\n\t\treturn value.slice(1, -1).split(\",\").filter(Boolean).map(Number);\n\t},\n});\n\nexport const memoriesTable = pgTable(\n\t\"mnemocyte_memories\",\n\t{\n\t\tid: text(\"id\").primaryKey(),\n\t\tentityId: text(\"entity_id\").notNull(),\n\t\tcontent: text(\"content\").notNull(),\n\t\ttype: text(\"type\").notNull().default(\"fact\"),\n\t\timportance: text(\"importance\").notNull().default(\"normal\"),\n\t\ttags: text(\"tags\").array().notNull().default(sql`'{}'::text[]`),\n\t\tsource: text(\"source\"),\n\t\tmetadata: jsonb(\"metadata\").notNull().default({}),\n\t\tconfidence: real(\"confidence\").notNull().default(1),\n\t\tembedding: vector(\"embedding\", { dimensions: 1536 }),\n\t\tembeddingModel: text(\"embedding_model\").notNull(),\n\t\tembeddingDimensions: integer(\"embedding_dimensions\").notNull(),\n\t\tsupersededBy: text(\"superseded_by\"),\n\t\tsupersededAt: timestamp(\"superseded_at\", { withTimezone: true }),\n\t\texpiresAt: timestamp(\"expires_at\", { withTimezone: true }),\n\t\tlastAccessedAt: timestamp(\"last_accessed_at\", { withTimezone: true }),\n\t\taccessCount: integer(\"access_count\").notNull().default(0),\n\t\tcreatedAt: timestamp(\"created_at\", { withTimezone: true })\n\t\t\t.notNull()\n\t\t\t.defaultNow(),\n\t\tupdatedAt: timestamp(\"updated_at\", { withTimezone: true })\n\t\t\t.notNull()\n\t\t\t.defaultNow(),\n\t},\n\t(table) => [\n\t\tindex(\"mnemocyte_memories_entity_idx\").on(table.entityId),\n\t\tindex(\"mnemocyte_memories_entity_type_idx\").on(table.entityId, table.type),\n\t],\n);\n\nexport const eventsTable = pgTable(\n\t\"mnemocyte_events\",\n\t{\n\t\tid: text(\"id\").primaryKey(),\n\t\tentityId: text(\"entity_id\").notNull(),\n\t\tdescription: text(\"description\").notNull(),\n\t\tmetadata: jsonb(\"metadata\").notNull().default({}),\n\t\ttimestamp: timestamp(\"timestamp\", { withTimezone: true })\n\t\t\t.notNull()\n\t\t\t.defaultNow(),\n\t},\n\t(table) => [\n\t\tindex(\"mnemocyte_events_entity_time_idx\").on(\n\t\t\ttable.entityId,\n\t\t\ttable.timestamp,\n\t\t),\n\t],\n);\n\nexport type MemoryRow = typeof memoriesTable.$inferSelect;\nexport type NewMemoryRow = typeof memoriesTable.$inferInsert;\nexport type EventRow = typeof eventsTable.$inferSelect;\nexport type NewEventRow = typeof eventsTable.$inferInsert;\n","import { drizzle, type PostgresJsDatabase } from \"drizzle-orm/postgres-js\";\nimport postgres, { type Sql } from \"postgres\";\nimport * as schema from \"./schema.js\";\n\nexport type MnemocyteDatabase = PostgresJsDatabase<typeof schema>;\n\nexport interface DatabaseHandle {\n\tdb: MnemocyteDatabase;\n\tclose(): Promise<void>;\n}\n\nexport function createDatabase(databaseUrl: string): DatabaseHandle {\n\tconst client = postgres(databaseUrl, {\n\t\tmax: 10,\n\t\tidle_timeout: 20,\n\t\tconnect_timeout: 10,\n\t});\n\n\treturn {\n\t\tdb: drizzle(client, { schema }),\n\t\tasync close() {\n\t\t\tawait client.end();\n\t\t},\n\t};\n}\n\nexport function useDatabase(client: Sql): DatabaseHandle {\n\treturn {\n\t\tdb: drizzle(client, { schema }),\n\t\tasync close() {},\n\t};\n}\n\nexport { schema };\n","export type MnemocyteErrorCode =\n\t| \"CONFIG\"\n\t| \"VALIDATION\"\n\t| \"DB\"\n\t| \"EMBEDDING\"\n\t| \"NOT_FOUND\"\n\t| \"MIGRATION\";\n\nexport class MnemocyteError extends Error {\n\treadonly code: MnemocyteErrorCode;\n\toverride readonly cause?: unknown;\n\n\tconstructor(message: string, code: MnemocyteErrorCode, cause?: unknown) {\n\t\tsuper(message);\n\t\tthis.name = \"MnemocyteError\";\n\t\tthis.code = code;\n\t\tthis.cause = cause;\n\t}\n}\n","import type { MemoryRow } from \"../db/schema.js\";\nimport { MnemocyteError } from \"../errors.js\";\nimport type {\n\tEmbedder,\n\tImportanceLevel,\n\tMemory,\n\tMemoryType,\n\tRecallInput,\n\tRememberInput,\n} from \"../types.js\";\n\nexport const DEFAULT_LIMIT = 10;\nexport const DEFAULT_MIN_SCORE = 0;\nexport const DEFAULT_TYPE: MemoryType = \"fact\";\nexport const DEFAULT_IMPORTANCE: ImportanceLevel = \"normal\";\n\nexport interface StoredMemory extends Memory {\n\tembedding: number[];\n}\n\nexport function assertNonEmptyString(value: string, field: string): void {\n\tif (value.trim().length === 0) {\n\t\tthrow new MnemocyteError(\n\t\t\t`${field} must be a non-empty string.`,\n\t\t\t\"VALIDATION\",\n\t\t);\n\t}\n}\n\nexport function assertLimit(value: number): void {\n\tif (!Number.isInteger(value) || value < 1) {\n\t\tthrow new MnemocyteError(\"limit must be a positive integer.\", \"VALIDATION\");\n\t}\n}\n\nexport function assertMinScore(value: number): void {\n\tif (!Number.isFinite(value) || value < 0 || value > 1) {\n\t\tthrow new MnemocyteError(\n\t\t\t\"minScore must be a number between 0 and 1.\",\n\t\t\t\"VALIDATION\",\n\t\t);\n\t}\n}\n\nexport function assertEmbedder(embedder: Embedder): void {\n\tif (!embedder || typeof embedder.embed !== \"function\") {\n\t\tthrow new MnemocyteError(\n\t\t\t\"embedder with an embed function is required.\",\n\t\t\t\"CONFIG\",\n\t\t);\n\t}\n\tassertNonEmptyString(embedder.model, \"embedder.model\");\n\tif (!Number.isInteger(embedder.dimensions) || embedder.dimensions < 1) {\n\t\tthrow new MnemocyteError(\n\t\t\t\"embedder.dimensions must be a positive integer.\",\n\t\t\t\"CONFIG\",\n\t\t);\n\t}\n}\n\nexport function normalizeTags(tags: readonly string[] | undefined): string[] {\n\tif (!tags) {\n\t\treturn [];\n\t}\n\treturn [...new Set(tags.map((tag) => tag.trim()).filter(Boolean))];\n}\n\nexport function createId(): string {\n\treturn `mem_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;\n}\n\nexport function isExpired(memory: Memory, now: Date): boolean {\n\treturn (\n\t\tmemory.expiresAt !== null && memory.expiresAt.getTime() <= now.getTime()\n\t);\n}\n\nexport function cloneMemory(memory: Memory): Memory {\n\treturn {\n\t\t...memory,\n\t\ttags: [...memory.tags],\n\t\tmetadata: { ...memory.metadata },\n\t\texpiresAt: memory.expiresAt ? new Date(memory.expiresAt) : null,\n\t\tlastAccessedAt: memory.lastAccessedAt\n\t\t\t? new Date(memory.lastAccessedAt)\n\t\t\t: null,\n\t\tcreatedAt: new Date(memory.createdAt),\n\t\tupdatedAt: new Date(memory.updatedAt),\n\t};\n}\n\nexport function rowToMemory(row: MemoryRow): Memory {\n\treturn {\n\t\tid: row.id,\n\t\tentityId: row.entityId,\n\t\tcontent: row.content,\n\t\ttype: row.type as MemoryType,\n\t\timportance: row.importance as ImportanceLevel,\n\t\ttags: row.tags,\n\t\tsource: row.source,\n\t\tmetadata: row.metadata as Record<string, unknown>,\n\t\tconfidence: row.confidence,\n\t\tembeddingModel: row.embeddingModel,\n\t\tembeddingDimensions: row.embeddingDimensions,\n\t\tsupersededBy: row.supersededBy,\n\t\texpiresAt: row.expiresAt,\n\t\tlastAccessedAt: row.lastAccessedAt,\n\t\taccessCount: row.accessCount,\n\t\tcreatedAt: row.createdAt,\n\t\tupdatedAt: row.updatedAt,\n\t};\n}\n\nexport async function embedOne(\n\tembedder: Embedder,\n\ttext: string,\n): Promise<number[]> {\n\tlet embeddings: number[][];\n\ttry {\n\t\tembeddings = await embedder.embed([text]);\n\t} catch (error) {\n\t\tthrow new MnemocyteError(\"Failed to embed text.\", \"EMBEDDING\", error);\n\t}\n\tconst embedding = embeddings[0];\n\tif (!embedding) {\n\t\tthrow new MnemocyteError(\"Embedder returned no embedding.\", \"EMBEDDING\");\n\t}\n\tif (embedding.length !== embedder.dimensions) {\n\t\tthrow new MnemocyteError(\n\t\t\t\"Embedder returned an embedding with unexpected dimensions.\",\n\t\t\t\"EMBEDDING\",\n\t\t);\n\t}\n\treturn embedding;\n}\n\nexport function validateRememberInput(input: RememberInput): void {\n\tassertNonEmptyString(input.entityId, \"entityId\");\n\tassertNonEmptyString(input.content, \"content\");\n\tif (\n\t\tinput.confidence !== undefined &&\n\t\t(!Number.isFinite(input.confidence) ||\n\t\t\tinput.confidence < 0 ||\n\t\t\tinput.confidence > 1)\n\t) {\n\t\tthrow new MnemocyteError(\n\t\t\t\"confidence must be a number between 0 and 1.\",\n\t\t\t\"VALIDATION\",\n\t\t);\n\t}\n}\n\nexport function validateRecallInput(input: RecallInput): void {\n\tassertNonEmptyString(input.entityId, \"entityId\");\n\tassertNonEmptyString(input.query, \"query\");\n\tif (input.limit !== undefined) {\n\t\tassertLimit(input.limit);\n\t}\n\tif (input.minScore !== undefined) {\n\t\tassertMinScore(input.minScore);\n\t}\n}\n\nexport function matchesRecallFilter(\n\tmemory: Memory,\n\tinput: RecallInput,\n\tnow: Date,\n): boolean {\n\tif (memory.entityId !== input.entityId) {\n\t\treturn false;\n\t}\n\tif (!input.includeSuperseded && memory.supersededBy !== null) {\n\t\treturn false;\n\t}\n\tif (!input.includeExpired && isExpired(memory, now)) {\n\t\treturn false;\n\t}\n\tif (input.types && !input.types.includes(memory.type)) {\n\t\treturn false;\n\t}\n\tif (input.tags && !input.tags.every((tag) => memory.tags.includes(tag))) {\n\t\treturn false;\n\t}\n\tif (input.before && memory.createdAt >= input.before) {\n\t\treturn false;\n\t}\n\tif (input.after && memory.createdAt <= input.after) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n","import { cloneMemory } from \"../memory/shared.js\";\nimport type {\n\tImportanceLevel,\n\tMemory,\n\tMemoryWithScore,\n\tRecallInput,\n} from \"../types.js\";\n\nconst IMPORTANCE_BOOSTS: Record<ImportanceLevel, number> = {\n\tlow: -0.05,\n\tnormal: 0,\n\thigh: 0.08,\n\tcritical: 0.15,\n};\n\nexport function cosineSimilarity(\n\ta: readonly number[],\n\tb: readonly number[],\n): number {\n\tlet dot = 0;\n\tlet aMagnitude = 0;\n\tlet bMagnitude = 0;\n\tfor (let i = 0; i < a.length; i += 1) {\n\t\tconst av = a[i] ?? 0;\n\t\tconst bv = b[i] ?? 0;\n\t\tdot += av * bv;\n\t\taMagnitude += av * av;\n\t\tbMagnitude += bv * bv;\n\t}\n\tif (aMagnitude === 0 || bMagnitude === 0) {\n\t\treturn 0;\n\t}\n\treturn dot / (Math.sqrt(aMagnitude) * Math.sqrt(bMagnitude));\n}\n\nexport function lexicalScore(content: string, query: string): number {\n\tconst terms = query.toLowerCase().split(/\\s+/).filter(Boolean);\n\tif (terms.length === 0) {\n\t\treturn 0;\n\t}\n\tconst lowerContent = content.toLowerCase();\n\tconst matches = terms.filter((term) => lowerContent.includes(term)).length;\n\treturn matches / terms.length;\n}\n\nfunction recencyScore(createdAt: Date): number {\n\tconst ageMs = Date.now() - createdAt.getTime();\n\tconst ageDays = Math.max(0, ageMs / 86_400_000);\n\treturn Math.exp((-Math.LN2 * ageDays) / 90);\n}\n\nexport function toScoredMemory(\n\tmemory: Memory,\n\tvector: number,\n\tlexical: number,\n\tinput: RecallInput,\n): MemoryWithScore {\n\tconst recency = recencyScore(memory.createdAt);\n\tconst importanceBoost = IMPORTANCE_BOOSTS[memory.importance];\n\tconst score = Math.max(\n\t\t0,\n\t\tMath.min(1, vector * 0.7 + lexical * 0.2 + recency * 0.1 + importanceBoost),\n\t);\n\treturn {\n\t\t...cloneMemory(memory),\n\t\tscore,\n\t\tscores: { vector, lexical, recency },\n\t\texplanation: input.explain\n\t\t\t? {\n\t\t\t\t\tvectorScore: vector,\n\t\t\t\t\tlexicalScore: lexical,\n\t\t\t\t\trecencyScore: recency,\n\t\t\t\t\timportanceBoost,\n\t\t\t\t\tfinalScore: score,\n\t\t\t\t}\n\t\t\t: null,\n\t};\n}\n","import { MnemocyteError } from \"../errors.js\";\nimport {\n\tcosineSimilarity,\n\tlexicalScore,\n\ttoScoredMemory,\n} from \"../retrieval/scorer.js\";\nimport type {\n\tEntityStats,\n\tGlobalStats,\n\tMemory,\n\tMnemocyteClient,\n\tMnemocyteConfig,\n\tRememberInput,\n} from \"../types.js\";\nimport {\n\tassertLimit,\n\tassertMinScore,\n\tassertNonEmptyString,\n\tcloneMemory,\n\tcreateId,\n\tDEFAULT_IMPORTANCE,\n\tDEFAULT_LIMIT,\n\tDEFAULT_MIN_SCORE,\n\tDEFAULT_TYPE,\n\tembedOne,\n\tisExpired,\n\tmatchesRecallFilter,\n\tnormalizeTags,\n\ttype StoredMemory,\n\tvalidateRecallInput,\n\tvalidateRememberInput,\n} from \"./shared.js\";\n\nexport function createInMemoryClient(config: MnemocyteConfig): MnemocyteClient {\n\tconst memories = new Map<string, StoredMemory>();\n\tlet closed = false;\n\n\tfunction assertOpen(): void {\n\t\tif (closed) {\n\t\t\tthrow new MnemocyteError(\"Mnemocyte client is closed.\", \"DB\");\n\t\t}\n\t}\n\n\tasync function remember(input: RememberInput): Promise<Memory> {\n\t\tassertOpen();\n\t\tvalidateRememberInput(input);\n\t\tconst embedding = await embedOne(config.embedder, input.content);\n\t\tconst now = new Date();\n\t\tconst memory: StoredMemory = {\n\t\t\tid: createId(),\n\t\t\tentityId: input.entityId,\n\t\t\tcontent: input.content,\n\t\t\ttype: input.type ?? DEFAULT_TYPE,\n\t\t\timportance: input.importance ?? DEFAULT_IMPORTANCE,\n\t\t\ttags: normalizeTags(input.tags),\n\t\t\tsource: input.source ?? null,\n\t\t\tmetadata: input.metadata ?? {},\n\t\t\tconfidence: input.confidence ?? 1,\n\t\t\tembeddingModel: config.embedder.model,\n\t\t\tembeddingDimensions: config.embedder.dimensions,\n\t\t\tsupersededBy: null,\n\t\t\texpiresAt: input.expiresAt ?? null,\n\t\t\tlastAccessedAt: null,\n\t\t\taccessCount: 0,\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t\tembedding,\n\t\t};\n\t\tmemories.set(memory.id, memory);\n\t\treturn cloneMemory(memory);\n\t}\n\n\treturn {\n\t\tremember,\n\t\tasync rememberMany(inputs) {\n\t\t\tassertOpen();\n\t\t\tconst result: Memory[] = [];\n\t\t\tfor (const input of inputs) {\n\t\t\t\tresult.push(await remember(input));\n\t\t\t}\n\t\t\treturn result;\n\t\t},\n\t\tasync recall(input) {\n\t\t\tassertOpen();\n\t\t\tvalidateRecallInput(input);\n\t\t\tconst limit = input.limit ?? config.defaults?.limit ?? DEFAULT_LIMIT;\n\t\t\tconst minScore =\n\t\t\t\tinput.minScore ?? config.defaults?.minScore ?? DEFAULT_MIN_SCORE;\n\t\t\tassertLimit(limit);\n\t\t\tassertMinScore(minScore);\n\t\t\tconst queryEmbedding = await embedOne(config.embedder, input.query);\n\t\t\tconst now = new Date();\n\t\t\tconst scored = Array.from(memories.values())\n\t\t\t\t.filter((memory) => matchesRecallFilter(memory, input, now))\n\t\t\t\t.map((memory) =>\n\t\t\t\t\ttoScoredMemory(\n\t\t\t\t\t\tmemory,\n\t\t\t\t\t\tMath.max(0, cosineSimilarity(memory.embedding, queryEmbedding)),\n\t\t\t\t\t\tlexicalScore(memory.content, input.query),\n\t\t\t\t\t\tinput,\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\t.filter((memory) => memory.score >= minScore)\n\t\t\t\t.sort((a, b) => b.score - a.score)\n\t\t\t\t.slice(0, limit);\n\t\t\tfor (const memory of scored) {\n\t\t\t\tconst stored = memories.get(memory.id);\n\t\t\t\tif (stored) {\n\t\t\t\t\tstored.lastAccessedAt = now;\n\t\t\t\t\tstored.accessCount += 1;\n\t\t\t\t\tstored.updatedAt = now;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn scored;\n\t\t},\n\t\tasync forget(input) {\n\t\t\tassertOpen();\n\t\t\tassertNonEmptyString(input.entityId, \"entityId\");\n\t\t\tassertNonEmptyString(input.memoryId, \"memoryId\");\n\t\t\tconst memory = memories.get(input.memoryId);\n\t\t\tif (!memory || memory.entityId !== input.entityId) {\n\t\t\t\tthrow new MnemocyteError(\"Memory was not found.\", \"NOT_FOUND\");\n\t\t\t}\n\t\t\tmemories.delete(input.memoryId);\n\t\t},\n\t\tasync forgetAll(input) {\n\t\t\tassertOpen();\n\t\t\tassertNonEmptyString(input.entityId, \"entityId\");\n\t\t\tfor (const [id, memory] of memories) {\n\t\t\t\tif (memory.entityId === input.entityId) {\n\t\t\t\t\tmemories.delete(id);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tasync stats(input) {\n\t\t\tassertOpen();\n\t\t\tconst now = new Date();\n\t\t\tconst allMemories = Array.from(memories.values());\n\t\t\tconst selected = input?.entityId\n\t\t\t\t? allMemories.filter((memory) => memory.entityId === input.entityId)\n\t\t\t\t: allMemories;\n\t\t\tconst activeMemoryCount = selected.filter(\n\t\t\t\t(memory) => !isExpired(memory, now) && memory.supersededBy === null,\n\t\t\t).length;\n\t\t\tconst expiredMemoryCount = selected.filter((memory) =>\n\t\t\t\tisExpired(memory, now),\n\t\t\t).length;\n\t\t\tconst supersededMemoryCount = selected.filter(\n\t\t\t\t(memory) => memory.supersededBy !== null,\n\t\t\t).length;\n\t\t\tif (input?.entityId) {\n\t\t\t\treturn {\n\t\t\t\t\tentityId: input.entityId,\n\t\t\t\t\tmemoryCount: selected.length,\n\t\t\t\t\tactiveMemoryCount,\n\t\t\t\t\texpiredMemoryCount,\n\t\t\t\t\tsupersededMemoryCount,\n\t\t\t\t} satisfies EntityStats;\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tentityCount: new Set(allMemories.map((memory) => memory.entityId)).size,\n\t\t\t\tmemoryCount: selected.length,\n\t\t\t\tactiveMemoryCount,\n\t\t\t\texpiredMemoryCount,\n\t\t\t\tsupersededMemoryCount,\n\t\t\t} satisfies GlobalStats;\n\t\t},\n\t\tasync close() {\n\t\t\tclosed = true;\n\t\t\tmemories.clear();\n\t\t},\n\t};\n}\n","import { and, desc, eq, gt, lt } from \"drizzle-orm\";\nimport type { MnemocyteDatabase } from \"../index.js\";\nimport { type EventRow, eventsTable, type NewEventRow } from \"../schema.js\";\n\nexport interface EventFilter {\n\tentityId: string;\n\tlimit?: number;\n\tbefore?: Date;\n\tafter?: Date;\n}\n\nexport async function insertEvent(\n\tdb: MnemocyteDatabase,\n\trow: NewEventRow,\n): Promise<EventRow> {\n\tconst result = await db.insert(eventsTable).values(row).returning();\n\tconst inserted = result[0];\n\tif (!inserted) {\n\t\tthrow new Error(\"Event insert returned no rows.\");\n\t}\n\treturn inserted;\n}\n\nexport async function listEvents(\n\tdb: MnemocyteDatabase,\n\tfilter: EventFilter,\n): Promise<EventRow[]> {\n\treturn db\n\t\t.select()\n\t\t.from(eventsTable)\n\t\t.where(\n\t\t\tand(\n\t\t\t\teq(eventsTable.entityId, filter.entityId),\n\t\t\t\tfilter.before ? lt(eventsTable.timestamp, filter.before) : undefined,\n\t\t\t\tfilter.after ? gt(eventsTable.timestamp, filter.after) : undefined,\n\t\t\t),\n\t\t)\n\t\t.orderBy(desc(eventsTable.timestamp))\n\t\t.limit(filter.limit ?? 50);\n}\n\nexport async function deleteEventsForEntity(\n\tdb: MnemocyteDatabase,\n\tentityId: string,\n): Promise<number> {\n\tconst result = await db\n\t\t.delete(eventsTable)\n\t\t.where(eq(eventsTable.entityId, entityId))\n\t\t.returning({ id: eventsTable.id });\n\treturn result.length;\n}\n","import { and, eq, gt, inArray, isNull, lt, sql } from \"drizzle-orm\";\nimport type { MemoryType } from \"../../types.js\";\nimport type { MnemocyteDatabase } from \"../index.js\";\nimport { type MemoryRow, memoriesTable, type NewMemoryRow } from \"../schema.js\";\n\nexport interface MemoryFilter {\n\tentityId: string;\n\ttypes?: readonly MemoryType[];\n\ttags?: readonly string[];\n\tbefore?: Date;\n\tafter?: Date;\n\tincludeSuperseded?: boolean;\n\tincludeExpired?: boolean;\n}\n\nexport interface VectorSearchInput extends MemoryFilter {\n\tembedding: readonly number[];\n\tlimit: number;\n\tminScore?: number;\n}\n\nexport interface LexicalSearchInput extends MemoryFilter {\n\tquery: string;\n\tlimit: number;\n}\n\nfunction vectorLiteral(embedding: readonly number[]): string {\n\treturn `[${embedding.join(\",\")}]`;\n}\n\nfunction filterConditions(filter: MemoryFilter) {\n\treturn and(\n\t\teq(memoriesTable.entityId, filter.entityId),\n\t\tfilter.includeSuperseded ? undefined : isNull(memoriesTable.supersededBy),\n\t\tfilter.includeExpired\n\t\t\t? undefined\n\t\t\t: sql`(${memoriesTable.expiresAt} IS NULL OR ${memoriesTable.expiresAt} > now())`,\n\t\tfilter.types && filter.types.length > 0\n\t\t\t? inArray(memoriesTable.type, [...filter.types])\n\t\t\t: undefined,\n\t\tfilter.tags && filter.tags.length > 0\n\t\t\t? sql`${memoriesTable.tags} && ${filter.tags}`\n\t\t\t: undefined,\n\t\tfilter.before ? lt(memoriesTable.createdAt, filter.before) : undefined,\n\t\tfilter.after ? gt(memoriesTable.createdAt, filter.after) : undefined,\n\t);\n}\n\nexport async function insertMemory(\n\tdb: MnemocyteDatabase,\n\trow: NewMemoryRow,\n): Promise<MemoryRow> {\n\tconst result = await db.insert(memoriesTable).values(row).returning();\n\tconst inserted = result[0];\n\tif (!inserted) {\n\t\tthrow new Error(\"Memory insert returned no rows.\");\n\t}\n\treturn inserted;\n}\n\nexport async function getMemoryById(\n\tdb: MnemocyteDatabase,\n\tentityId: string,\n\tmemoryId: string,\n): Promise<MemoryRow | null> {\n\tconst result = await db\n\t\t.select()\n\t\t.from(memoriesTable)\n\t\t.where(\n\t\t\tand(eq(memoriesTable.entityId, entityId), eq(memoriesTable.id, memoryId)),\n\t\t)\n\t\t.limit(1);\n\treturn result[0] ?? null;\n}\n\nexport async function listMemories(\n\tdb: MnemocyteDatabase,\n\tfilter: MemoryFilter,\n): Promise<MemoryRow[]> {\n\treturn db.select().from(memoriesTable).where(filterConditions(filter));\n}\n\nexport async function deleteMemory(\n\tdb: MnemocyteDatabase,\n\tentityId: string,\n\tmemoryId: string,\n): Promise<boolean> {\n\tconst result = await db\n\t\t.delete(memoriesTable)\n\t\t.where(\n\t\t\tand(eq(memoriesTable.entityId, entityId), eq(memoriesTable.id, memoryId)),\n\t\t)\n\t\t.returning({ id: memoriesTable.id });\n\treturn result.length > 0;\n}\n\nexport async function deleteMemoriesForEntity(\n\tdb: MnemocyteDatabase,\n\tentityId: string,\n): Promise<number> {\n\tconst result = await db\n\t\t.delete(memoriesTable)\n\t\t.where(eq(memoriesTable.entityId, entityId))\n\t\t.returning({ id: memoriesTable.id });\n\treturn result.length;\n}\n\nexport async function markMemoryAccessed(\n\tdb: MnemocyteDatabase,\n\tmemoryIds: readonly string[],\n): Promise<void> {\n\tif (memoryIds.length === 0) {\n\t\treturn;\n\t}\n\tawait db\n\t\t.update(memoriesTable)\n\t\t.set({\n\t\t\tlastAccessedAt: new Date(),\n\t\t\taccessCount: sql`${memoriesTable.accessCount} + 1`,\n\t\t\tupdatedAt: new Date(),\n\t\t})\n\t\t.where(inArray(memoriesTable.id, [...memoryIds]));\n}\n\nexport async function vectorSearch(\n\tdb: MnemocyteDatabase,\n\tinput: VectorSearchInput,\n): Promise<Array<MemoryRow & { vectorScore: number }>> {\n\tconst embedding = vectorLiteral(input.embedding);\n\tconst minScore = input.minScore ?? 0;\n\tconst rows = await db.execute(sql`\n\t\tSELECT\n\t\t\tid,\n\t\t\tentity_id AS \"entityId\",\n\t\t\tcontent,\n\t\t\ttype,\n\t\t\timportance,\n\t\t\ttags,\n\t\t\tsource,\n\t\t\tmetadata,\n\t\t\tconfidence,\n\t\t\tembedding,\n\t\t\tembedding_model AS \"embeddingModel\",\n\t\t\tembedding_dimensions AS \"embeddingDimensions\",\n\t\t\tsuperseded_by AS \"supersededBy\",\n\t\t\tsuperseded_at AS \"supersededAt\",\n\t\t\texpires_at AS \"expiresAt\",\n\t\t\tlast_accessed_at AS \"lastAccessedAt\",\n\t\t\taccess_count AS \"accessCount\",\n\t\t\tcreated_at AS \"createdAt\",\n\t\t\tupdated_at AS \"updatedAt\",\n\t\t\t1 - (embedding <=> ${embedding}::vector) AS \"vectorScore\"\n\t\tFROM mnemocyte_memories\n\t\tWHERE\n\t\t\tentity_id = ${input.entityId}\n\t\t\tAND embedding IS NOT NULL\n\t\t\t${input.includeSuperseded ? sql`` : sql`AND superseded_by IS NULL`}\n\t\t\t${input.includeExpired ? sql`` : sql`AND (expires_at IS NULL OR expires_at > now())`}\n\t\t\t${\n\t\t\t\tinput.types && input.types.length > 0\n\t\t\t\t\t? sql`AND type IN (${sql.join(\n\t\t\t\t\t\t\tinput.types.map((type) => sql`${type}`),\n\t\t\t\t\t\t\tsql`, `,\n\t\t\t\t\t\t)})`\n\t\t\t\t\t: sql``\n\t\t\t}\n\t\t\t${input.tags && input.tags.length > 0 ? sql`AND tags && ${input.tags}` : sql``}\n\t\t\t${input.before ? sql`AND created_at < ${input.before}` : sql``}\n\t\t\t${input.after ? sql`AND created_at > ${input.after}` : sql``}\n\t\t\tAND 1 - (embedding <=> ${embedding}::vector) >= ${minScore}\n\t\tORDER BY embedding <=> ${embedding}::vector\n\t\tLIMIT ${input.limit}\n\t`);\n\treturn rows as unknown as Array<MemoryRow & { vectorScore: number }>;\n}\n\nexport async function lexicalSearch(\n\tdb: MnemocyteDatabase,\n\tinput: LexicalSearchInput,\n): Promise<Array<MemoryRow & { lexicalScore: number }>> {\n\tconst rows = await db.execute(sql`\n\t\tSELECT\n\t\t\tid,\n\t\t\tentity_id AS \"entityId\",\n\t\t\tcontent,\n\t\t\ttype,\n\t\t\timportance,\n\t\t\ttags,\n\t\t\tsource,\n\t\t\tmetadata,\n\t\t\tconfidence,\n\t\t\tembedding,\n\t\t\tembedding_model AS \"embeddingModel\",\n\t\t\tembedding_dimensions AS \"embeddingDimensions\",\n\t\t\tsuperseded_by AS \"supersededBy\",\n\t\t\tsuperseded_at AS \"supersededAt\",\n\t\t\texpires_at AS \"expiresAt\",\n\t\t\tlast_accessed_at AS \"lastAccessedAt\",\n\t\t\taccess_count AS \"accessCount\",\n\t\t\tcreated_at AS \"createdAt\",\n\t\t\tupdated_at AS \"updatedAt\",\n\t\t\tts_rank(to_tsvector('english', content), websearch_to_tsquery('english', ${input.query})) AS \"lexicalScore\"\n\t\tFROM mnemocyte_memories\n\t\tWHERE\n\t\t\tentity_id = ${input.entityId}\n\t\t\t${input.includeSuperseded ? sql`` : sql`AND superseded_by IS NULL`}\n\t\t\t${input.includeExpired ? sql`` : sql`AND (expires_at IS NULL OR expires_at > now())`}\n\t\t\t${\n\t\t\t\tinput.types && input.types.length > 0\n\t\t\t\t\t? sql`AND type IN (${sql.join(\n\t\t\t\t\t\t\tinput.types.map((type) => sql`${type}`),\n\t\t\t\t\t\t\tsql`, `,\n\t\t\t\t\t\t)})`\n\t\t\t\t\t: sql``\n\t\t\t}\n\t\t\t${input.tags && input.tags.length > 0 ? sql`AND tags && ${input.tags}` : sql``}\n\t\t\t${input.before ? sql`AND created_at < ${input.before}` : sql``}\n\t\t\t${input.after ? sql`AND created_at > ${input.after}` : sql``}\n\t\t\tAND to_tsvector('english', content) @@ websearch_to_tsquery('english', ${input.query})\n\t\tORDER BY \"lexicalScore\" DESC\n\t\tLIMIT ${input.limit}\n\t`);\n\treturn rows as unknown as Array<MemoryRow & { lexicalScore: number }>;\n}\n","import type { DatabaseHandle } from \"../db/index.js\";\nimport { deleteEventsForEntity } from \"../db/queries/events.js\";\nimport {\n\tdeleteMemoriesForEntity,\n\tdeleteMemory,\n\tinsertMemory,\n\tlexicalSearch,\n\tlistMemories,\n\tmarkMemoryAccessed,\n\tvectorSearch,\n} from \"../db/queries/memories.js\";\nimport { type MemoryRow, memoriesTable } from \"../db/schema.js\";\nimport { MnemocyteError } from \"../errors.js\";\nimport { toScoredMemory } from \"../retrieval/scorer.js\";\nimport type {\n\tEntityStats,\n\tGlobalStats,\n\tMemory,\n\tMnemocyteClient,\n\tMnemocyteConfig,\n\tRememberInput,\n} from \"../types.js\";\nimport {\n\tassertLimit,\n\tassertMinScore,\n\tassertNonEmptyString,\n\tcreateId,\n\tDEFAULT_IMPORTANCE,\n\tDEFAULT_LIMIT,\n\tDEFAULT_MIN_SCORE,\n\tDEFAULT_TYPE,\n\tembedOne,\n\tisExpired,\n\tnormalizeTags,\n\trowToMemory,\n\tvalidateRecallInput,\n\tvalidateRememberInput,\n} from \"./shared.js\";\n\ninterface ScoredRow {\n\trow: MemoryRow;\n\tvectorScore: number;\n\tlexicalScore: number;\n}\n\nexport function createPostgresClient(\n\tconfig: MnemocyteConfig,\n\thandle: DatabaseHandle,\n): MnemocyteClient {\n\tlet closed = false;\n\n\tfunction assertOpen(): void {\n\t\tif (closed) {\n\t\t\tthrow new MnemocyteError(\"Mnemocyte client is closed.\", \"DB\");\n\t\t}\n\t}\n\n\tasync function remember(input: RememberInput): Promise<Memory> {\n\t\tassertOpen();\n\t\tvalidateRememberInput(input);\n\t\tconst embedding = await embedOne(config.embedder, input.content);\n\t\tconst now = new Date();\n\t\tconst row = await insertMemory(handle.db, {\n\t\t\tid: createId(),\n\t\t\tentityId: input.entityId,\n\t\t\tcontent: input.content,\n\t\t\ttype: input.type ?? DEFAULT_TYPE,\n\t\t\timportance: input.importance ?? DEFAULT_IMPORTANCE,\n\t\t\ttags: normalizeTags(input.tags),\n\t\t\tsource: input.source ?? null,\n\t\t\tmetadata: input.metadata ?? {},\n\t\t\tconfidence: input.confidence ?? 1,\n\t\t\tembedding,\n\t\t\tembeddingModel: config.embedder.model,\n\t\t\tembeddingDimensions: config.embedder.dimensions,\n\t\t\tsupersededBy: null,\n\t\t\tsupersededAt: null,\n\t\t\texpiresAt: input.expiresAt ?? null,\n\t\t\tlastAccessedAt: null,\n\t\t\taccessCount: 0,\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t});\n\t\treturn rowToMemory(row);\n\t}\n\n\treturn {\n\t\tremember,\n\t\tasync rememberMany(inputs) {\n\t\t\tassertOpen();\n\t\t\tconst result: Memory[] = [];\n\t\t\tfor (const input of inputs) {\n\t\t\t\tresult.push(await remember(input));\n\t\t\t}\n\t\t\treturn result;\n\t\t},\n\t\tasync recall(input) {\n\t\t\tassertOpen();\n\t\t\tvalidateRecallInput(input);\n\t\t\tconst limit = input.limit ?? config.defaults?.limit ?? DEFAULT_LIMIT;\n\t\t\tconst minScore =\n\t\t\t\tinput.minScore ?? config.defaults?.minScore ?? DEFAULT_MIN_SCORE;\n\t\t\tassertLimit(limit);\n\t\t\tassertMinScore(minScore);\n\t\t\tconst queryEmbedding = await embedOne(config.embedder, input.query);\n\t\t\tconst [vectorRows, lexicalRows] = await Promise.all([\n\t\t\t\tvectorSearch(handle.db, {\n\t\t\t\t\t...input,\n\t\t\t\t\tembedding: queryEmbedding,\n\t\t\t\t\tlimit: limit * 2,\n\t\t\t\t\tminScore: 0,\n\t\t\t\t}),\n\t\t\t\tlexicalSearch(handle.db, { ...input, limit: limit * 2 }),\n\t\t\t]);\n\t\t\tconst merged = new Map<string, ScoredRow>();\n\t\t\tfor (const row of vectorRows) {\n\t\t\t\tmerged.set(row.id, {\n\t\t\t\t\trow,\n\t\t\t\t\tvectorScore: row.vectorScore,\n\t\t\t\t\tlexicalScore: 0,\n\t\t\t\t});\n\t\t\t}\n\t\t\tfor (const row of lexicalRows) {\n\t\t\t\tconst existing = merged.get(row.id);\n\t\t\t\tif (existing) {\n\t\t\t\t\texisting.lexicalScore = row.lexicalScore;\n\t\t\t\t} else {\n\t\t\t\t\tmerged.set(row.id, {\n\t\t\t\t\t\trow,\n\t\t\t\t\t\tvectorScore: 0,\n\t\t\t\t\t\tlexicalScore: row.lexicalScore,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst scored = Array.from(merged.values())\n\t\t\t\t.map((entry) =>\n\t\t\t\t\ttoScoredMemory(\n\t\t\t\t\t\trowToMemory(entry.row),\n\t\t\t\t\t\tMath.max(0, entry.vectorScore),\n\t\t\t\t\t\tMath.max(0, entry.lexicalScore),\n\t\t\t\t\t\tinput,\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\t.filter((memory) => memory.score >= minScore)\n\t\t\t\t.sort((a, b) => b.score - a.score)\n\t\t\t\t.slice(0, limit);\n\t\t\tawait markMemoryAccessed(\n\t\t\t\thandle.db,\n\t\t\t\tscored.map((memory) => memory.id),\n\t\t\t);\n\t\t\treturn scored;\n\t\t},\n\t\tasync forget(input) {\n\t\t\tassertOpen();\n\t\t\tassertNonEmptyString(input.entityId, \"entityId\");\n\t\t\tassertNonEmptyString(input.memoryId, \"memoryId\");\n\t\t\tconst deleted = await deleteMemory(\n\t\t\t\thandle.db,\n\t\t\t\tinput.entityId,\n\t\t\t\tinput.memoryId,\n\t\t\t);\n\t\t\tif (!deleted) {\n\t\t\t\tthrow new MnemocyteError(\"Memory was not found.\", \"NOT_FOUND\");\n\t\t\t}\n\t\t},\n\t\tasync forgetAll(input) {\n\t\t\tassertOpen();\n\t\t\tassertNonEmptyString(input.entityId, \"entityId\");\n\t\t\tawait Promise.all([\n\t\t\t\tdeleteMemoriesForEntity(handle.db, input.entityId),\n\t\t\t\tdeleteEventsForEntity(handle.db, input.entityId),\n\t\t\t]);\n\t\t},\n\t\tasync stats(input) {\n\t\t\tassertOpen();\n\t\t\tconst now = new Date();\n\t\t\tif (input?.entityId) {\n\t\t\t\tconst selected = await listMemories(handle.db, {\n\t\t\t\t\tentityId: input.entityId,\n\t\t\t\t\tincludeExpired: true,\n\t\t\t\t\tincludeSuperseded: true,\n\t\t\t\t});\n\t\t\t\treturn {\n\t\t\t\t\tentityId: input.entityId,\n\t\t\t\t\tmemoryCount: selected.length,\n\t\t\t\t\tactiveMemoryCount: selected.filter(\n\t\t\t\t\t\t(memory) =>\n\t\t\t\t\t\t\t!isExpired(rowToMemory(memory), now) &&\n\t\t\t\t\t\t\tmemory.supersededBy === null,\n\t\t\t\t\t).length,\n\t\t\t\t\texpiredMemoryCount: selected.filter((memory) =>\n\t\t\t\t\t\tisExpired(rowToMemory(memory), now),\n\t\t\t\t\t).length,\n\t\t\t\t\tsupersededMemoryCount: selected.filter(\n\t\t\t\t\t\t(memory) => memory.supersededBy !== null,\n\t\t\t\t\t).length,\n\t\t\t\t} satisfies EntityStats;\n\t\t\t}\n\t\t\tconst allMemories = await handle.db.select().from(memoriesTable);\n\t\t\treturn {\n\t\t\t\tentityCount: new Set(allMemories.map((memory) => memory.entityId)).size,\n\t\t\t\tmemoryCount: allMemories.length,\n\t\t\t\tactiveMemoryCount: allMemories.filter(\n\t\t\t\t\t(memory) =>\n\t\t\t\t\t\t!isExpired(rowToMemory(memory), now) &&\n\t\t\t\t\t\tmemory.supersededBy === null,\n\t\t\t\t).length,\n\t\t\t\texpiredMemoryCount: allMemories.filter((memory) =>\n\t\t\t\t\tisExpired(rowToMemory(memory), now),\n\t\t\t\t).length,\n\t\t\t\tsupersededMemoryCount: allMemories.filter(\n\t\t\t\t\t(memory) => memory.supersededBy !== null,\n\t\t\t\t).length,\n\t\t\t} satisfies GlobalStats;\n\t\t},\n\t\tasync close() {\n\t\t\tclosed = true;\n\t\t\tawait handle.close();\n\t\t},\n\t};\n}\n","import { createDatabase } from \"./db/index.js\";\nimport { createInMemoryClient } from \"./memory/in-memory.js\";\nimport { createPostgresClient } from \"./memory/postgres.js\";\nimport { assertEmbedder, assertNonEmptyString } from \"./memory/shared.js\";\nimport type { MnemocyteClient, MnemocyteConfig } from \"./types.js\";\n\nexport function createMnemocyte(config: MnemocyteConfig): MnemocyteClient {\n\tassertEmbedder(config.embedder);\n\tif (config.databaseUrl) {\n\t\tassertNonEmptyString(config.databaseUrl, \"databaseUrl\");\n\t\treturn createPostgresClient(config, createDatabase(config.databaseUrl));\n\t}\n\treturn createInMemoryClient(config);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAYA,MAAM,SAAS,WAAmD;CACjE,SAAS,QAAQ;EAEhB,OAAO,UADY,OAAO,WAAW,WAAW,SAAS,KAC7B;;CAE7B,SAAS,OAAO;EACf,OAAO,IAAI,MAAM,KAAK,IAAI,CAAC;;CAE5B,WAAW,OAAO;EACjB,OAAO,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,CAAC,OAAO,QAAQ,CAAC,IAAI,OAAO;;CAEjE,CAAC;AAEF,MAAa,gBAAgB,QAC5B,sBACA;CACC,IAAI,KAAK,KAAK,CAAC,YAAY;CAC3B,UAAU,KAAK,YAAY,CAAC,SAAS;CACrC,SAAS,KAAK,UAAU,CAAC,SAAS;CAClC,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,QAAQ,OAAO;CAC5C,YAAY,KAAK,aAAa,CAAC,SAAS,CAAC,QAAQ,SAAS;CAC1D,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,GAAG,eAAe;CAC/D,QAAQ,KAAK,SAAS;CACtB,UAAU,MAAM,WAAW,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC;CACjD,YAAY,KAAK,aAAa,CAAC,SAAS,CAAC,QAAQ,EAAE;CACnD,WAAW,OAAO,aAAa,EAAE,YAAY,MAAM,CAAC;CACpD,gBAAgB,KAAK,kBAAkB,CAAC,SAAS;CACjD,qBAAqB,QAAQ,uBAAuB,CAAC,SAAS;CAC9D,cAAc,KAAK,gBAAgB;CACnC,cAAc,UAAU,iBAAiB,EAAE,cAAc,MAAM,CAAC;CAChE,WAAW,UAAU,cAAc,EAAE,cAAc,MAAM,CAAC;CAC1D,gBAAgB,UAAU,oBAAoB,EAAE,cAAc,MAAM,CAAC;CACrE,aAAa,QAAQ,eAAe,CAAC,SAAS,CAAC,QAAQ,EAAE;CACzD,WAAW,UAAU,cAAc,EAAE,cAAc,MAAM,CAAC,CACxD,SAAS,CACT,YAAY;CACd,WAAW,UAAU,cAAc,EAAE,cAAc,MAAM,CAAC,CACxD,SAAS,CACT,YAAY;CACd,GACA,UAAU,CACV,MAAM,gCAAgC,CAAC,GAAG,MAAM,SAAS,EACzD,MAAM,qCAAqC,CAAC,GAAG,MAAM,UAAU,MAAM,KAAK,CAC1E,CACD;AAED,MAAa,cAAc,QAC1B,oBACA;CACC,IAAI,KAAK,KAAK,CAAC,YAAY;CAC3B,UAAU,KAAK,YAAY,CAAC,SAAS;CACrC,aAAa,KAAK,cAAc,CAAC,SAAS;CAC1C,UAAU,MAAM,WAAW,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC;CACjD,WAAW,UAAU,aAAa,EAAE,cAAc,MAAM,CAAC,CACvD,SAAS,CACT,YAAY;CACd,GACA,UAAU,CACV,MAAM,mCAAmC,CAAC,GACzC,MAAM,UACN,MAAM,UACN,CACD,CACD;;;AChED,SAAgB,eAAe,aAAqC;CACnE,MAAM,SAAS,SAAS,aAAa;EACpC,KAAK;EACL,cAAc;EACd,iBAAiB;EACjB,CAAC;CAEF,OAAO;EACN,IAAI,QAAQ,QAAQ,EAAE,QAAA,gBAAQ,CAAC;EAC/B,MAAM,QAAQ;GACb,MAAM,OAAO,KAAK;;EAEnB;;;;ACfF,IAAa,iBAAb,cAAoC,MAAM;CACzC;CACA;CAEA,YAAY,SAAiB,MAA0B,OAAiB;EACvE,MAAM,QAAQ;EACd,KAAK,OAAO;EACZ,KAAK,OAAO;EACZ,KAAK,QAAQ;;;ACIf,SAAgB,qBAAqB,OAAe,OAAqB;CACxE,IAAI,MAAM,MAAM,CAAC,WAAW,GAC3B,MAAM,IAAI,eACT,GAAG,MAAM,+BACT,aACA;;AAIH,SAAgB,YAAY,OAAqB;CAChD,IAAI,CAAC,OAAO,UAAU,MAAM,IAAI,QAAQ,GACvC,MAAM,IAAI,eAAe,qCAAqC,aAAa;;AAI7E,SAAgB,eAAe,OAAqB;CACnD,IAAI,CAAC,OAAO,SAAS,MAAM,IAAI,QAAQ,KAAK,QAAQ,GACnD,MAAM,IAAI,eACT,8CACA,aACA;;AAIH,SAAgB,eAAe,UAA0B;CACxD,IAAI,CAAC,YAAY,OAAO,SAAS,UAAU,YAC1C,MAAM,IAAI,eACT,gDACA,SACA;CAEF,qBAAqB,SAAS,OAAO,iBAAiB;CACtD,IAAI,CAAC,OAAO,UAAU,SAAS,WAAW,IAAI,SAAS,aAAa,GACnE,MAAM,IAAI,eACT,mDACA,SACA;;AAIH,SAAgB,cAAc,MAA+C;CAC5E,IAAI,CAAC,MACJ,OAAO,EAAE;CAEV,OAAO,CAAC,GAAG,IAAI,IAAI,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,CAAC,OAAO,QAAQ,CAAC,CAAC;;AAGnE,SAAgB,WAAmB;CAClC,OAAO,OAAO,KAAK,KAAK,CAAC,SAAS,GAAG,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,GAAG,GAAG;;AAGjF,SAAgB,UAAU,QAAgB,KAAoB;CAC7D,OACC,OAAO,cAAc,QAAQ,OAAO,UAAU,SAAS,IAAI,IAAI,SAAS;;AAI1E,SAAgB,YAAY,QAAwB;CACnD,OAAO;EACN,GAAG;EACH,MAAM,CAAC,GAAG,OAAO,KAAK;EACtB,UAAU,EAAE,GAAG,OAAO,UAAU;EAChC,WAAW,OAAO,YAAY,IAAI,KAAK,OAAO,UAAU,GAAG;EAC3D,gBAAgB,OAAO,iBACpB,IAAI,KAAK,OAAO,eAAe,GAC/B;EACH,WAAW,IAAI,KAAK,OAAO,UAAU;EACrC,WAAW,IAAI,KAAK,OAAO,UAAU;EACrC;;AAGF,SAAgB,YAAY,KAAwB;CACnD,OAAO;EACN,IAAI,IAAI;EACR,UAAU,IAAI;EACd,SAAS,IAAI;EACb,MAAM,IAAI;EACV,YAAY,IAAI;EAChB,MAAM,IAAI;EACV,QAAQ,IAAI;EACZ,UAAU,IAAI;EACd,YAAY,IAAI;EAChB,gBAAgB,IAAI;EACpB,qBAAqB,IAAI;EACzB,cAAc,IAAI;EAClB,WAAW,IAAI;EACf,gBAAgB,IAAI;EACpB,aAAa,IAAI;EACjB,WAAW,IAAI;EACf,WAAW,IAAI;EACf;;AAGF,eAAsB,SACrB,UACA,MACoB;CACpB,IAAI;CACJ,IAAI;EACH,aAAa,MAAM,SAAS,MAAM,CAAC,KAAK,CAAC;UACjC,OAAO;EACf,MAAM,IAAI,eAAe,yBAAyB,aAAa,MAAM;;CAEtE,MAAM,YAAY,WAAW;CAC7B,IAAI,CAAC,WACJ,MAAM,IAAI,eAAe,mCAAmC,YAAY;CAEzE,IAAI,UAAU,WAAW,SAAS,YACjC,MAAM,IAAI,eACT,8DACA,YACA;CAEF,OAAO;;AAGR,SAAgB,sBAAsB,OAA4B;CACjE,qBAAqB,MAAM,UAAU,WAAW;CAChD,qBAAqB,MAAM,SAAS,UAAU;CAC9C,IACC,MAAM,eAAe,KAAA,MACpB,CAAC,OAAO,SAAS,MAAM,WAAW,IAClC,MAAM,aAAa,KACnB,MAAM,aAAa,IAEpB,MAAM,IAAI,eACT,gDACA,aACA;;AAIH,SAAgB,oBAAoB,OAA0B;CAC7D,qBAAqB,MAAM,UAAU,WAAW;CAChD,qBAAqB,MAAM,OAAO,QAAQ;CAC1C,IAAI,MAAM,UAAU,KAAA,GACnB,YAAY,MAAM,MAAM;CAEzB,IAAI,MAAM,aAAa,KAAA,GACtB,eAAe,MAAM,SAAS;;AAIhC,SAAgB,oBACf,QACA,OACA,KACU;CACV,IAAI,OAAO,aAAa,MAAM,UAC7B,OAAO;CAER,IAAI,CAAC,MAAM,qBAAqB,OAAO,iBAAiB,MACvD,OAAO;CAER,IAAI,CAAC,MAAM,kBAAkB,UAAU,QAAQ,IAAI,EAClD,OAAO;CAER,IAAI,MAAM,SAAS,CAAC,MAAM,MAAM,SAAS,OAAO,KAAK,EACpD,OAAO;CAER,IAAI,MAAM,QAAQ,CAAC,MAAM,KAAK,OAAO,QAAQ,OAAO,KAAK,SAAS,IAAI,CAAC,EACtE,OAAO;CAER,IAAI,MAAM,UAAU,OAAO,aAAa,MAAM,QAC7C,OAAO;CAER,IAAI,MAAM,SAAS,OAAO,aAAa,MAAM,OAC5C,OAAO;CAER,OAAO;;;;ACrLR,MAAM,oBAAqD;CAC1D,KAAK;CACL,QAAQ;CACR,MAAM;CACN,UAAU;CACV;AAED,SAAgB,iBACf,GACA,GACS;CACT,IAAI,MAAM;CACV,IAAI,aAAa;CACjB,IAAI,aAAa;CACjB,KAAK,IAAI,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK,GAAG;EACrC,MAAM,KAAK,EAAE,MAAM;EACnB,MAAM,KAAK,EAAE,MAAM;EACnB,OAAO,KAAK;EACZ,cAAc,KAAK;EACnB,cAAc,KAAK;;CAEpB,IAAI,eAAe,KAAK,eAAe,GACtC,OAAO;CAER,OAAO,OAAO,KAAK,KAAK,WAAW,GAAG,KAAK,KAAK,WAAW;;AAG5D,SAAgB,aAAa,SAAiB,OAAuB;CACpE,MAAM,QAAQ,MAAM,aAAa,CAAC,MAAM,MAAM,CAAC,OAAO,QAAQ;CAC9D,IAAI,MAAM,WAAW,GACpB,OAAO;CAER,MAAM,eAAe,QAAQ,aAAa;CAE1C,OADgB,MAAM,QAAQ,SAAS,aAAa,SAAS,KAAK,CAAC,CAAC,SACnD,MAAM;;AAGxB,SAAS,aAAa,WAAyB;CAC9C,MAAM,QAAQ,KAAK,KAAK,GAAG,UAAU,SAAS;CAC9C,MAAM,UAAU,KAAK,IAAI,GAAG,QAAQ,MAAW;CAC/C,OAAO,KAAK,IAAK,CAAC,KAAK,MAAM,UAAW,GAAG;;AAG5C,SAAgB,eACf,QACA,QACA,SACA,OACkB;CAClB,MAAM,UAAU,aAAa,OAAO,UAAU;CAC9C,MAAM,kBAAkB,kBAAkB,OAAO;CACjD,MAAM,QAAQ,KAAK,IAClB,GACA,KAAK,IAAI,GAAG,SAAS,KAAM,UAAU,KAAM,UAAU,KAAM,gBAAgB,CAC3E;CACD,OAAO;EACN,GAAG,YAAY,OAAO;EACtB;EACA,QAAQ;GAAE;GAAQ;GAAS;GAAS;EACpC,aAAa,MAAM,UAChB;GACA,aAAa;GACb,cAAc;GACd,cAAc;GACd;GACA,YAAY;GACZ,GACA;EACH;;;;AC3CF,SAAgB,qBAAqB,QAA0C;CAC9E,MAAM,2BAAW,IAAI,KAA2B;CAChD,IAAI,SAAS;CAEb,SAAS,aAAmB;EAC3B,IAAI,QACH,MAAM,IAAI,eAAe,+BAA+B,KAAK;;CAI/D,eAAe,SAAS,OAAuC;EAC9D,YAAY;EACZ,sBAAsB,MAAM;EAC5B,MAAM,YAAY,MAAM,SAAS,OAAO,UAAU,MAAM,QAAQ;EAChE,MAAM,sBAAM,IAAI,MAAM;EACtB,MAAM,SAAuB;GAC5B,IAAI,UAAU;GACd,UAAU,MAAM;GAChB,SAAS,MAAM;GACf,MAAM,MAAM,QAAA;GACZ,YAAY,MAAM,cAAA;GAClB,MAAM,cAAc,MAAM,KAAK;GAC/B,QAAQ,MAAM,UAAU;GACxB,UAAU,MAAM,YAAY,EAAE;GAC9B,YAAY,MAAM,cAAc;GAChC,gBAAgB,OAAO,SAAS;GAChC,qBAAqB,OAAO,SAAS;GACrC,cAAc;GACd,WAAW,MAAM,aAAa;GAC9B,gBAAgB;GAChB,aAAa;GACb,WAAW;GACX,WAAW;GACX;GACA;EACD,SAAS,IAAI,OAAO,IAAI,OAAO;EAC/B,OAAO,YAAY,OAAO;;CAG3B,OAAO;EACN;EACA,MAAM,aAAa,QAAQ;GAC1B,YAAY;GACZ,MAAM,SAAmB,EAAE;GAC3B,KAAK,MAAM,SAAS,QACnB,OAAO,KAAK,MAAM,SAAS,MAAM,CAAC;GAEnC,OAAO;;EAER,MAAM,OAAO,OAAO;GACnB,YAAY;GACZ,oBAAoB,MAAM;GAC1B,MAAM,QAAQ,MAAM,SAAS,OAAO,UAAU,SAAA;GAC9C,MAAM,WACL,MAAM,YAAY,OAAO,UAAU,YAAA;GACpC,YAAY,MAAM;GAClB,eAAe,SAAS;GACxB,MAAM,iBAAiB,MAAM,SAAS,OAAO,UAAU,MAAM,MAAM;GACnE,MAAM,sBAAM,IAAI,MAAM;GACtB,MAAM,SAAS,MAAM,KAAK,SAAS,QAAQ,CAAC,CAC1C,QAAQ,WAAW,oBAAoB,QAAQ,OAAO,IAAI,CAAC,CAC3D,KAAK,WACL,eACC,QACA,KAAK,IAAI,GAAG,iBAAiB,OAAO,WAAW,eAAe,CAAC,EAC/D,aAAa,OAAO,SAAS,MAAM,MAAM,EACzC,MACA,CACD,CACA,QAAQ,WAAW,OAAO,SAAS,SAAS,CAC5C,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM,CACjC,MAAM,GAAG,MAAM;GACjB,KAAK,MAAM,UAAU,QAAQ;IAC5B,MAAM,SAAS,SAAS,IAAI,OAAO,GAAG;IACtC,IAAI,QAAQ;KACX,OAAO,iBAAiB;KACxB,OAAO,eAAe;KACtB,OAAO,YAAY;;;GAGrB,OAAO;;EAER,MAAM,OAAO,OAAO;GACnB,YAAY;GACZ,qBAAqB,MAAM,UAAU,WAAW;GAChD,qBAAqB,MAAM,UAAU,WAAW;GAChD,MAAM,SAAS,SAAS,IAAI,MAAM,SAAS;GAC3C,IAAI,CAAC,UAAU,OAAO,aAAa,MAAM,UACxC,MAAM,IAAI,eAAe,yBAAyB,YAAY;GAE/D,SAAS,OAAO,MAAM,SAAS;;EAEhC,MAAM,UAAU,OAAO;GACtB,YAAY;GACZ,qBAAqB,MAAM,UAAU,WAAW;GAChD,KAAK,MAAM,CAAC,IAAI,WAAW,UAC1B,IAAI,OAAO,aAAa,MAAM,UAC7B,SAAS,OAAO,GAAG;;EAItB,MAAM,MAAM,OAAO;GAClB,YAAY;GACZ,MAAM,sBAAM,IAAI,MAAM;GACtB,MAAM,cAAc,MAAM,KAAK,SAAS,QAAQ,CAAC;GACjD,MAAM,WAAW,OAAO,WACrB,YAAY,QAAQ,WAAW,OAAO,aAAa,MAAM,SAAS,GAClE;GACH,MAAM,oBAAoB,SAAS,QACjC,WAAW,CAAC,UAAU,QAAQ,IAAI,IAAI,OAAO,iBAAiB,KAC/D,CAAC;GACF,MAAM,qBAAqB,SAAS,QAAQ,WAC3C,UAAU,QAAQ,IAAI,CACtB,CAAC;GACF,MAAM,wBAAwB,SAAS,QACrC,WAAW,OAAO,iBAAiB,KACpC,CAAC;GACF,IAAI,OAAO,UACV,OAAO;IACN,UAAU,MAAM;IAChB,aAAa,SAAS;IACtB;IACA;IACA;IACA;GAEF,OAAO;IACN,aAAa,IAAI,IAAI,YAAY,KAAK,WAAW,OAAO,SAAS,CAAC,CAAC;IACnE,aAAa,SAAS;IACtB;IACA;IACA;IACA;;EAEF,MAAM,QAAQ;GACb,SAAS;GACT,SAAS,OAAO;;EAEjB;;;;AClIF,eAAsB,sBACrB,IACA,UACkB;CAKlB,QAAO,MAJc,GACnB,OAAO,YAAY,CACnB,MAAM,GAAG,YAAY,UAAU,SAAS,CAAC,CACzC,UAAU,EAAE,IAAI,YAAY,IAAI,CAAC,EACrB;;;;ACvBf,SAAS,cAAc,WAAsC;CAC5D,OAAO,IAAI,UAAU,KAAK,IAAI,CAAC;;AAGhC,SAAS,iBAAiB,QAAsB;CAC/C,OAAO,IACN,GAAG,cAAc,UAAU,OAAO,SAAS,EAC3C,OAAO,oBAAoB,KAAA,IAAY,OAAO,cAAc,aAAa,EACzE,OAAO,iBACJ,KAAA,IACA,GAAG,IAAI,cAAc,UAAU,cAAc,cAAc,UAAU,YACxE,OAAO,SAAS,OAAO,MAAM,SAAS,IACnC,QAAQ,cAAc,MAAM,CAAC,GAAG,OAAO,MAAM,CAAC,GAC9C,KAAA,GACH,OAAO,QAAQ,OAAO,KAAK,SAAS,IACjC,GAAG,GAAG,cAAc,KAAK,MAAM,OAAO,SACtC,KAAA,GACH,OAAO,SAAS,GAAG,cAAc,WAAW,OAAO,OAAO,GAAG,KAAA,GAC7D,OAAO,QAAQ,GAAG,cAAc,WAAW,OAAO,MAAM,GAAG,KAAA,EAC3D;;AAGF,eAAsB,aACrB,IACA,KACqB;CAErB,MAAM,YAAW,MADI,GAAG,OAAO,cAAc,CAAC,OAAO,IAAI,CAAC,WAAW,EAC7C;CACxB,IAAI,CAAC,UACJ,MAAM,IAAI,MAAM,kCAAkC;CAEnD,OAAO;;AAkBR,eAAsB,aACrB,IACA,QACuB;CACvB,OAAO,GAAG,QAAQ,CAAC,KAAK,cAAc,CAAC,MAAM,iBAAiB,OAAO,CAAC;;AAGvE,eAAsB,aACrB,IACA,UACA,UACmB;CAOnB,QAAO,MANc,GACnB,OAAO,cAAc,CACrB,MACA,IAAI,GAAG,cAAc,UAAU,SAAS,EAAE,GAAG,cAAc,IAAI,SAAS,CAAC,CACzE,CACA,UAAU,EAAE,IAAI,cAAc,IAAI,CAAC,EACvB,SAAS;;AAGxB,eAAsB,wBACrB,IACA,UACkB;CAKlB,QAAO,MAJc,GACnB,OAAO,cAAc,CACrB,MAAM,GAAG,cAAc,UAAU,SAAS,CAAC,CAC3C,UAAU,EAAE,IAAI,cAAc,IAAI,CAAC,EACvB;;AAGf,eAAsB,mBACrB,IACA,WACgB;CAChB,IAAI,UAAU,WAAW,GACxB;CAED,MAAM,GACJ,OAAO,cAAc,CACrB,IAAI;EACJ,gCAAgB,IAAI,MAAM;EAC1B,aAAa,GAAG,GAAG,cAAc,YAAY;EAC7C,2BAAW,IAAI,MAAM;EACrB,CAAC,CACD,MAAM,QAAQ,cAAc,IAAI,CAAC,GAAG,UAAU,CAAC,CAAC;;AAGnD,eAAsB,aACrB,IACA,OACsD;CACtD,MAAM,YAAY,cAAc,MAAM,UAAU;CAChD,MAAM,WAAW,MAAM,YAAY;CA4CnC,OAAO,MA3CY,GAAG,QAAQ,GAAG;;;;;;;;;;;;;;;;;;;;;wBAqBV,UAAU;;;iBAGjB,MAAM,SAAS;;KAE3B,MAAM,oBAAoB,GAAG,KAAK,GAAG,4BAA4B;KACjE,MAAM,iBAAiB,GAAG,KAAK,GAAG,iDAAiD;KAEpF,MAAM,SAAS,MAAM,MAAM,SAAS,IACjC,GAAG,gBAAgB,IAAI,KACvB,MAAM,MAAM,KAAK,SAAS,GAAG,GAAG,OAAO,EACvC,GAAG,KACH,CAAC,KACD,GAAG,GACN;KACC,MAAM,QAAQ,MAAM,KAAK,SAAS,IAAI,GAAG,eAAe,MAAM,SAAS,GAAG,GAAG;KAC7E,MAAM,SAAS,GAAG,oBAAoB,MAAM,WAAW,GAAG,GAAG;KAC7D,MAAM,QAAQ,GAAG,oBAAoB,MAAM,UAAU,GAAG,GAAG;4BACpC,UAAU,eAAe,SAAS;2BACnC,UAAU;UAC3B,MAAM,MAAM;GACnB;;AAIH,eAAsB,cACrB,IACA,OACuD;CA2CvD,OAAO,MA1CY,GAAG,QAAQ,GAAG;;;;;;;;;;;;;;;;;;;;;8EAqB4C,MAAM,MAAM;;;iBAGzE,MAAM,SAAS;KAC3B,MAAM,oBAAoB,GAAG,KAAK,GAAG,4BAA4B;KACjE,MAAM,iBAAiB,GAAG,KAAK,GAAG,iDAAiD;KAEpF,MAAM,SAAS,MAAM,MAAM,SAAS,IACjC,GAAG,gBAAgB,IAAI,KACvB,MAAM,MAAM,KAAK,SAAS,GAAG,GAAG,OAAO,EACvC,GAAG,KACH,CAAC,KACD,GAAG,GACN;KACC,MAAM,QAAQ,MAAM,KAAK,SAAS,IAAI,GAAG,eAAe,MAAM,SAAS,GAAG,GAAG;KAC7E,MAAM,SAAS,GAAG,oBAAoB,MAAM,WAAW,GAAG,GAAG;KAC7D,MAAM,QAAQ,GAAG,oBAAoB,MAAM,UAAU,GAAG,GAAG;4EACY,MAAM,MAAM;;UAE9E,MAAM,MAAM;GACnB;;;;AChLH,SAAgB,qBACf,QACA,QACkB;CAClB,IAAI,SAAS;CAEb,SAAS,aAAmB;EAC3B,IAAI,QACH,MAAM,IAAI,eAAe,+BAA+B,KAAK;;CAI/D,eAAe,SAAS,OAAuC;EAC9D,YAAY;EACZ,sBAAsB,MAAM;EAC5B,MAAM,YAAY,MAAM,SAAS,OAAO,UAAU,MAAM,QAAQ;EAChE,MAAM,sBAAM,IAAI,MAAM;EAsBtB,OAAO,YAAY,MArBD,aAAa,OAAO,IAAI;GACzC,IAAI,UAAU;GACd,UAAU,MAAM;GAChB,SAAS,MAAM;GACf,MAAM,MAAM,QAAA;GACZ,YAAY,MAAM,cAAA;GAClB,MAAM,cAAc,MAAM,KAAK;GAC/B,QAAQ,MAAM,UAAU;GACxB,UAAU,MAAM,YAAY,EAAE;GAC9B,YAAY,MAAM,cAAc;GAChC;GACA,gBAAgB,OAAO,SAAS;GAChC,qBAAqB,OAAO,SAAS;GACrC,cAAc;GACd,cAAc;GACd,WAAW,MAAM,aAAa;GAC9B,gBAAgB;GAChB,aAAa;GACb,WAAW;GACX,WAAW;GACX,CAAC,CACqB;;CAGxB,OAAO;EACN;EACA,MAAM,aAAa,QAAQ;GAC1B,YAAY;GACZ,MAAM,SAAmB,EAAE;GAC3B,KAAK,MAAM,SAAS,QACnB,OAAO,KAAK,MAAM,SAAS,MAAM,CAAC;GAEnC,OAAO;;EAER,MAAM,OAAO,OAAO;GACnB,YAAY;GACZ,oBAAoB,MAAM;GAC1B,MAAM,QAAQ,MAAM,SAAS,OAAO,UAAU,SAAA;GAC9C,MAAM,WACL,MAAM,YAAY,OAAO,UAAU,YAAA;GACpC,YAAY,MAAM;GAClB,eAAe,SAAS;GACxB,MAAM,iBAAiB,MAAM,SAAS,OAAO,UAAU,MAAM,MAAM;GACnE,MAAM,CAAC,YAAY,eAAe,MAAM,QAAQ,IAAI,CACnD,aAAa,OAAO,IAAI;IACvB,GAAG;IACH,WAAW;IACX,OAAO,QAAQ;IACf,UAAU;IACV,CAAC,EACF,cAAc,OAAO,IAAI;IAAE,GAAG;IAAO,OAAO,QAAQ;IAAG,CAAC,CACxD,CAAC;GACF,MAAM,yBAAS,IAAI,KAAwB;GAC3C,KAAK,MAAM,OAAO,YACjB,OAAO,IAAI,IAAI,IAAI;IAClB;IACA,aAAa,IAAI;IACjB,cAAc;IACd,CAAC;GAEH,KAAK,MAAM,OAAO,aAAa;IAC9B,MAAM,WAAW,OAAO,IAAI,IAAI,GAAG;IACnC,IAAI,UACH,SAAS,eAAe,IAAI;SAE5B,OAAO,IAAI,IAAI,IAAI;KAClB;KACA,aAAa;KACb,cAAc,IAAI;KAClB,CAAC;;GAGJ,MAAM,SAAS,MAAM,KAAK,OAAO,QAAQ,CAAC,CACxC,KAAK,UACL,eACC,YAAY,MAAM,IAAI,EACtB,KAAK,IAAI,GAAG,MAAM,YAAY,EAC9B,KAAK,IAAI,GAAG,MAAM,aAAa,EAC/B,MACA,CACD,CACA,QAAQ,WAAW,OAAO,SAAS,SAAS,CAC5C,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM,CACjC,MAAM,GAAG,MAAM;GACjB,MAAM,mBACL,OAAO,IACP,OAAO,KAAK,WAAW,OAAO,GAAG,CACjC;GACD,OAAO;;EAER,MAAM,OAAO,OAAO;GACnB,YAAY;GACZ,qBAAqB,MAAM,UAAU,WAAW;GAChD,qBAAqB,MAAM,UAAU,WAAW;GAMhD,IAAI,CAAC,MALiB,aACrB,OAAO,IACP,MAAM,UACN,MAAM,SACN,EAEA,MAAM,IAAI,eAAe,yBAAyB,YAAY;;EAGhE,MAAM,UAAU,OAAO;GACtB,YAAY;GACZ,qBAAqB,MAAM,UAAU,WAAW;GAChD,MAAM,QAAQ,IAAI,CACjB,wBAAwB,OAAO,IAAI,MAAM,SAAS,EAClD,sBAAsB,OAAO,IAAI,MAAM,SAAS,CAChD,CAAC;;EAEH,MAAM,MAAM,OAAO;GAClB,YAAY;GACZ,MAAM,sBAAM,IAAI,MAAM;GACtB,IAAI,OAAO,UAAU;IACpB,MAAM,WAAW,MAAM,aAAa,OAAO,IAAI;KAC9C,UAAU,MAAM;KAChB,gBAAgB;KAChB,mBAAmB;KACnB,CAAC;IACF,OAAO;KACN,UAAU,MAAM;KAChB,aAAa,SAAS;KACtB,mBAAmB,SAAS,QAC1B,WACA,CAAC,UAAU,YAAY,OAAO,EAAE,IAAI,IACpC,OAAO,iBAAiB,KACzB,CAAC;KACF,oBAAoB,SAAS,QAAQ,WACpC,UAAU,YAAY,OAAO,EAAE,IAAI,CACnC,CAAC;KACF,uBAAuB,SAAS,QAC9B,WAAW,OAAO,iBAAiB,KACpC,CAAC;KACF;;GAEF,MAAM,cAAc,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,cAAc;GAChE,OAAO;IACN,aAAa,IAAI,IAAI,YAAY,KAAK,WAAW,OAAO,SAAS,CAAC,CAAC;IACnE,aAAa,YAAY;IACzB,mBAAmB,YAAY,QAC7B,WACA,CAAC,UAAU,YAAY,OAAO,EAAE,IAAI,IACpC,OAAO,iBAAiB,KACzB,CAAC;IACF,oBAAoB,YAAY,QAAQ,WACvC,UAAU,YAAY,OAAO,EAAE,IAAI,CACnC,CAAC;IACF,uBAAuB,YAAY,QACjC,WAAW,OAAO,iBAAiB,KACpC,CAAC;IACF;;EAEF,MAAM,QAAQ;GACb,SAAS;GACT,MAAM,OAAO,OAAO;;EAErB;;;;ACrNF,SAAgB,gBAAgB,QAA0C;CACzE,eAAe,OAAO,SAAS;CAC/B,IAAI,OAAO,aAAa;EACvB,qBAAqB,OAAO,aAAa,cAAc;EACvD,OAAO,qBAAqB,QAAQ,eAAe,OAAO,YAAY,CAAC;;CAExE,OAAO,qBAAqB,OAAO"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
CREATE EXTENSION IF NOT EXISTS vector;
|
|
2
|
+
|
|
3
|
+
CREATE TABLE IF NOT EXISTS mnemocyte_memories (
|
|
4
|
+
id text PRIMARY KEY,
|
|
5
|
+
entity_id text NOT NULL,
|
|
6
|
+
content text NOT NULL,
|
|
7
|
+
type text NOT NULL DEFAULT 'fact',
|
|
8
|
+
importance text NOT NULL DEFAULT 'normal',
|
|
9
|
+
tags text[] NOT NULL DEFAULT '{}',
|
|
10
|
+
source text,
|
|
11
|
+
metadata jsonb NOT NULL DEFAULT '{}',
|
|
12
|
+
confidence real NOT NULL DEFAULT 1.0,
|
|
13
|
+
embedding vector(1536),
|
|
14
|
+
embedding_model text NOT NULL,
|
|
15
|
+
embedding_dimensions integer NOT NULL,
|
|
16
|
+
superseded_by text REFERENCES mnemocyte_memories(id),
|
|
17
|
+
superseded_at timestamptz,
|
|
18
|
+
expires_at timestamptz,
|
|
19
|
+
last_accessed_at timestamptz,
|
|
20
|
+
access_count integer NOT NULL DEFAULT 0,
|
|
21
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
22
|
+
updated_at timestamptz NOT NULL DEFAULT now()
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
CREATE TABLE IF NOT EXISTS mnemocyte_events (
|
|
26
|
+
id text PRIMARY KEY,
|
|
27
|
+
entity_id text NOT NULL,
|
|
28
|
+
description text NOT NULL,
|
|
29
|
+
metadata jsonb NOT NULL DEFAULT '{}',
|
|
30
|
+
timestamp timestamptz NOT NULL DEFAULT now()
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
CREATE INDEX IF NOT EXISTS mnemocyte_memories_entity_idx
|
|
34
|
+
ON mnemocyte_memories (entity_id);
|
|
35
|
+
|
|
36
|
+
CREATE INDEX IF NOT EXISTS mnemocyte_memories_entity_type_idx
|
|
37
|
+
ON mnemocyte_memories (entity_id, type);
|
|
38
|
+
|
|
39
|
+
CREATE INDEX IF NOT EXISTS mnemocyte_events_entity_time_idx
|
|
40
|
+
ON mnemocyte_events (entity_id, timestamp DESC);
|
|
41
|
+
|
|
42
|
+
CREATE INDEX IF NOT EXISTS mnemocyte_memories_fts_idx
|
|
43
|
+
ON mnemocyte_memories USING gin (to_tsvector('english', content));
|
|
44
|
+
|
|
45
|
+
CREATE INDEX IF NOT EXISTS mnemocyte_memories_embedding_hnsw_idx
|
|
46
|
+
ON mnemocyte_memories USING hnsw (embedding vector_cosine_ops);
|
package/package.json
CHANGED
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mnemocyte",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Persistent memory for TypeScript AI apps.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"files": [
|
|
8
|
-
"dist"
|
|
8
|
+
"dist",
|
|
9
|
+
"migrations"
|
|
9
10
|
],
|
|
10
11
|
"exports": {
|
|
11
12
|
".": {
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
"default": "./dist/index.js"
|
|
15
|
-
}
|
|
13
|
+
"types": "./dist/index.d.mts",
|
|
14
|
+
"import": "./dist/index.mjs"
|
|
16
15
|
}
|
|
17
16
|
},
|
|
18
|
-
"types": "./dist/index.d.
|
|
19
|
-
"main": "./dist/index.js",
|
|
17
|
+
"types": "./dist/index.d.mts",
|
|
20
18
|
"repository": {
|
|
21
19
|
"type": "git",
|
|
22
20
|
"url": "https://github.com/Meenic/mnemocyte.git"
|
|
@@ -42,7 +40,7 @@
|
|
|
42
40
|
"provenance": true
|
|
43
41
|
},
|
|
44
42
|
"engines": {
|
|
45
|
-
"node": ">=
|
|
43
|
+
"node": ">=18"
|
|
46
44
|
},
|
|
47
45
|
"devEngines": {
|
|
48
46
|
"packageManager": {
|
|
@@ -51,9 +49,14 @@
|
|
|
51
49
|
"onFail": "download"
|
|
52
50
|
}
|
|
53
51
|
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"drizzle-orm": "^0.45.2",
|
|
54
|
+
"postgres": "^3.4.7"
|
|
55
|
+
},
|
|
54
56
|
"devDependencies": {
|
|
55
57
|
"@biomejs/biome": "2.4.15",
|
|
56
58
|
"@types/node": "^25.7.0",
|
|
59
|
+
"drizzle-kit": "^0.31.0",
|
|
57
60
|
"tsdown": "^0.22.0",
|
|
58
61
|
"typescript": "^6.0.3"
|
|
59
62
|
},
|
|
@@ -61,7 +64,11 @@
|
|
|
61
64
|
"build": "tsdown src/index.ts",
|
|
62
65
|
"dev": "tsdown src/index.ts --watch",
|
|
63
66
|
"checktypes": "tsc --noEmit",
|
|
67
|
+
"test:integration": "pnpm build && node test/integration/postgres.test.mjs",
|
|
64
68
|
"lint": "biome lint --write",
|
|
65
|
-
"format": "biome format --write"
|
|
69
|
+
"format": "biome format --write",
|
|
70
|
+
"db:generate": "drizzle-kit generate",
|
|
71
|
+
"db:migrate": "drizzle-kit migrate",
|
|
72
|
+
"pack:dry": "pnpm pack --dry-run"
|
|
66
73
|
}
|
|
67
74
|
}
|