@elizaos/plugin-sql 1.0.0-alpha.1
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/LICENSE +21 -0
- package/README.md +178 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +2177 -0
- package/dist/index.js.map +1 -0
- package/package.json +46 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2177 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import {
|
|
3
|
+
logger as logger6
|
|
4
|
+
} from "@elizaos/core";
|
|
5
|
+
|
|
6
|
+
// src/pg-lite/adapter.ts
|
|
7
|
+
import { logger as logger2 } from "@elizaos/core";
|
|
8
|
+
import { drizzle } from "drizzle-orm/pglite";
|
|
9
|
+
|
|
10
|
+
// src/base.ts
|
|
11
|
+
import {
|
|
12
|
+
DatabaseAdapter,
|
|
13
|
+
logger
|
|
14
|
+
} from "@elizaos/core";
|
|
15
|
+
import {
|
|
16
|
+
and,
|
|
17
|
+
cosineDistance,
|
|
18
|
+
count,
|
|
19
|
+
desc,
|
|
20
|
+
eq,
|
|
21
|
+
gte,
|
|
22
|
+
inArray,
|
|
23
|
+
lte,
|
|
24
|
+
or,
|
|
25
|
+
sql as sql13
|
|
26
|
+
} from "drizzle-orm";
|
|
27
|
+
import { v4 } from "uuid";
|
|
28
|
+
|
|
29
|
+
// src/schema/embedding.ts
|
|
30
|
+
import {
|
|
31
|
+
pgTable as pgTable6,
|
|
32
|
+
uuid as uuid6,
|
|
33
|
+
vector as vector2,
|
|
34
|
+
index as index2,
|
|
35
|
+
foreignKey as foreignKey2,
|
|
36
|
+
check as check2
|
|
37
|
+
} from "drizzle-orm/pg-core";
|
|
38
|
+
import { sql as sql6 } from "drizzle-orm";
|
|
39
|
+
|
|
40
|
+
// src/schema/types.ts
|
|
41
|
+
import { customType } from "drizzle-orm/pg-core";
|
|
42
|
+
var stringJsonb = customType({
|
|
43
|
+
dataType() {
|
|
44
|
+
return "jsonb";
|
|
45
|
+
},
|
|
46
|
+
toDriver(value) {
|
|
47
|
+
return JSON.stringify(value);
|
|
48
|
+
},
|
|
49
|
+
fromDriver(value) {
|
|
50
|
+
return JSON.stringify(value);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
var numberTimestamp = customType(
|
|
54
|
+
{
|
|
55
|
+
dataType() {
|
|
56
|
+
return "timestamptz";
|
|
57
|
+
},
|
|
58
|
+
toDriver(value) {
|
|
59
|
+
return new Date(value).toISOString();
|
|
60
|
+
},
|
|
61
|
+
fromDriver(value) {
|
|
62
|
+
return new Date(value).getTime();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
// src/schema/memory.ts
|
|
68
|
+
import {
|
|
69
|
+
pgTable as pgTable5,
|
|
70
|
+
uuid as uuid5,
|
|
71
|
+
text as text5,
|
|
72
|
+
jsonb as jsonb5,
|
|
73
|
+
index,
|
|
74
|
+
boolean as boolean2,
|
|
75
|
+
foreignKey,
|
|
76
|
+
check
|
|
77
|
+
} from "drizzle-orm/pg-core";
|
|
78
|
+
import { relations, sql as sql5 } from "drizzle-orm";
|
|
79
|
+
|
|
80
|
+
// src/schema/entity.ts
|
|
81
|
+
import { sql as sql2 } from "drizzle-orm";
|
|
82
|
+
import { jsonb as jsonb2, pgTable as pgTable2, text as text2, unique as unique2, uuid as uuid2 } from "drizzle-orm/pg-core";
|
|
83
|
+
|
|
84
|
+
// src/schema/agent.ts
|
|
85
|
+
import {
|
|
86
|
+
boolean,
|
|
87
|
+
jsonb,
|
|
88
|
+
pgTable,
|
|
89
|
+
text,
|
|
90
|
+
uuid,
|
|
91
|
+
unique
|
|
92
|
+
} from "drizzle-orm/pg-core";
|
|
93
|
+
import { sql } from "drizzle-orm";
|
|
94
|
+
var agentTable = pgTable(
|
|
95
|
+
"agents",
|
|
96
|
+
{
|
|
97
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
98
|
+
createdAt: numberTimestamp("createdAt").default(sql`now()`).notNull(),
|
|
99
|
+
updatedAt: numberTimestamp("updatedAt").default(sql`now()`).notNull(),
|
|
100
|
+
enabled: boolean("enabled").default(true).notNull(),
|
|
101
|
+
// Character
|
|
102
|
+
name: text("name"),
|
|
103
|
+
username: text("username"),
|
|
104
|
+
system: text("system"),
|
|
105
|
+
bio: jsonb("bio").$type().notNull(),
|
|
106
|
+
messageExamples: jsonb("message_examples").$type().default(sql`'[]'::jsonb`),
|
|
107
|
+
postExamples: jsonb("post_examples").$type().default(sql`'[]'::jsonb`),
|
|
108
|
+
topics: jsonb("topics").$type().default(sql`'[]'::jsonb`),
|
|
109
|
+
adjectives: jsonb("adjectives").$type().default(sql`'[]'::jsonb`),
|
|
110
|
+
knowledge: jsonb("knowledge").$type().default(sql`'[]'::jsonb`),
|
|
111
|
+
plugins: jsonb("plugins").$type().default(sql`'[]'::jsonb`),
|
|
112
|
+
settings: jsonb("settings").$type().default(sql`'{}'::jsonb`),
|
|
113
|
+
style: jsonb("style").$type().default(sql`'{}'::jsonb`)
|
|
114
|
+
},
|
|
115
|
+
(table) => {
|
|
116
|
+
return {
|
|
117
|
+
nameUnique: unique("name_unique").on(table.name)
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
// src/schema/entity.ts
|
|
123
|
+
var entityTable = pgTable2(
|
|
124
|
+
"entities",
|
|
125
|
+
{
|
|
126
|
+
id: uuid2("id").notNull().primaryKey(),
|
|
127
|
+
agentId: uuid2("agentId").notNull().references(() => agentTable.id, {
|
|
128
|
+
onDelete: "cascade"
|
|
129
|
+
}),
|
|
130
|
+
createdAt: numberTimestamp("createdAt").default(sql2`now()`).notNull(),
|
|
131
|
+
names: text2("names").array().default(sql2`'{}'::text[]`),
|
|
132
|
+
metadata: jsonb2("metadata").default(sql2`'{}'::jsonb`)
|
|
133
|
+
},
|
|
134
|
+
(table) => {
|
|
135
|
+
return {
|
|
136
|
+
idAgentIdUnique: unique2("id_agent_id_unique").on(table.id, table.agentId)
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
// src/schema/room.ts
|
|
142
|
+
import { sql as sql4 } from "drizzle-orm";
|
|
143
|
+
import { jsonb as jsonb4, pgTable as pgTable4, text as text4, uuid as uuid4 } from "drizzle-orm/pg-core";
|
|
144
|
+
|
|
145
|
+
// src/schema/worldTable.ts
|
|
146
|
+
import { jsonb as jsonb3, pgTable as pgTable3, text as text3, uuid as uuid3 } from "drizzle-orm/pg-core";
|
|
147
|
+
import { sql as sql3 } from "drizzle-orm";
|
|
148
|
+
var worldTable = pgTable3("worlds", {
|
|
149
|
+
id: uuid3("id").notNull().primaryKey().default(sql3`gen_random_uuid()`),
|
|
150
|
+
agentId: uuid3("agentId").notNull().references(() => agentTable.id, { onDelete: "cascade" }),
|
|
151
|
+
name: text3("name").notNull(),
|
|
152
|
+
metadata: jsonb3("metadata"),
|
|
153
|
+
serverId: text3("serverId").notNull(),
|
|
154
|
+
createdAt: numberTimestamp("createdAt").default(sql3`now()`).notNull()
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// src/schema/room.ts
|
|
158
|
+
var roomTable = pgTable4("rooms", {
|
|
159
|
+
id: uuid4("id").notNull().primaryKey().default(sql4`gen_random_uuid()`),
|
|
160
|
+
agentId: uuid4("agentId").references(() => agentTable.id, {
|
|
161
|
+
onDelete: "cascade"
|
|
162
|
+
}),
|
|
163
|
+
source: text4("source").notNull(),
|
|
164
|
+
type: text4("type").notNull(),
|
|
165
|
+
serverId: text4("serverId"),
|
|
166
|
+
worldId: uuid4("worldId").references(() => worldTable.id, {
|
|
167
|
+
onDelete: "cascade"
|
|
168
|
+
}),
|
|
169
|
+
name: text4("name"),
|
|
170
|
+
metadata: jsonb4("metadata"),
|
|
171
|
+
channelId: text4("channelId"),
|
|
172
|
+
createdAt: numberTimestamp("createdAt").default(sql4`now()`).notNull()
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// src/schema/memory.ts
|
|
176
|
+
var memoryTable = pgTable5(
|
|
177
|
+
"memories",
|
|
178
|
+
{
|
|
179
|
+
id: uuid5("id").primaryKey().notNull(),
|
|
180
|
+
type: text5("type").notNull(),
|
|
181
|
+
createdAt: numberTimestamp("createdAt").default(sql5`now()`).notNull(),
|
|
182
|
+
content: jsonb5("content").notNull(),
|
|
183
|
+
entityId: uuid5("entityId").references(() => entityTable.id, {
|
|
184
|
+
onDelete: "cascade"
|
|
185
|
+
}),
|
|
186
|
+
agentId: uuid5("agentId").references(() => agentTable.id, {
|
|
187
|
+
onDelete: "cascade"
|
|
188
|
+
}),
|
|
189
|
+
roomId: uuid5("roomId").references(() => roomTable.id, {
|
|
190
|
+
onDelete: "cascade"
|
|
191
|
+
}),
|
|
192
|
+
unique: boolean2("unique").default(true).notNull(),
|
|
193
|
+
metadata: jsonb5("metadata").default({}).notNull()
|
|
194
|
+
},
|
|
195
|
+
(table) => [
|
|
196
|
+
index("idx_memories_type_room").on(table.type, table.roomId),
|
|
197
|
+
foreignKey({
|
|
198
|
+
name: "fk_room",
|
|
199
|
+
columns: [table.roomId],
|
|
200
|
+
foreignColumns: [roomTable.id]
|
|
201
|
+
}).onDelete("cascade"),
|
|
202
|
+
foreignKey({
|
|
203
|
+
name: "fk_user",
|
|
204
|
+
columns: [table.entityId],
|
|
205
|
+
foreignColumns: [entityTable.id]
|
|
206
|
+
}).onDelete("cascade"),
|
|
207
|
+
foreignKey({
|
|
208
|
+
name: "fk_agent",
|
|
209
|
+
columns: [table.agentId],
|
|
210
|
+
foreignColumns: [entityTable.id]
|
|
211
|
+
}).onDelete("cascade"),
|
|
212
|
+
index("idx_memories_metadata_type").on(sql5`((metadata->>'type'))`),
|
|
213
|
+
index("idx_memories_document_id").on(sql5`((metadata->>'documentId'))`),
|
|
214
|
+
index("idx_fragments_order").on(
|
|
215
|
+
sql5`((metadata->>'documentId'))`,
|
|
216
|
+
sql5`((metadata->>'position'))`
|
|
217
|
+
),
|
|
218
|
+
check(
|
|
219
|
+
"fragment_metadata_check",
|
|
220
|
+
sql5`
|
|
221
|
+
CASE
|
|
222
|
+
WHEN metadata->>'type' = 'fragment' THEN
|
|
223
|
+
metadata ? 'documentId' AND
|
|
224
|
+
metadata ? 'position'
|
|
225
|
+
ELSE true
|
|
226
|
+
END
|
|
227
|
+
`
|
|
228
|
+
),
|
|
229
|
+
check(
|
|
230
|
+
"document_metadata_check",
|
|
231
|
+
sql5`
|
|
232
|
+
CASE
|
|
233
|
+
WHEN metadata->>'type' = 'document' THEN
|
|
234
|
+
metadata ? 'timestamp'
|
|
235
|
+
ELSE true
|
|
236
|
+
END
|
|
237
|
+
`
|
|
238
|
+
)
|
|
239
|
+
]
|
|
240
|
+
);
|
|
241
|
+
var memoryRelations = relations(memoryTable, ({ one }) => ({
|
|
242
|
+
embedding: one(embeddingTable)
|
|
243
|
+
}));
|
|
244
|
+
|
|
245
|
+
// src/schema/embedding.ts
|
|
246
|
+
var VECTOR_DIMS = {
|
|
247
|
+
SMALL: 384,
|
|
248
|
+
MEDIUM: 512,
|
|
249
|
+
LARGE: 768,
|
|
250
|
+
XL: 1024,
|
|
251
|
+
XXL: 1536,
|
|
252
|
+
XXXL: 3072
|
|
253
|
+
};
|
|
254
|
+
var DIMENSION_MAP = {
|
|
255
|
+
[VECTOR_DIMS.SMALL]: "dim384",
|
|
256
|
+
[VECTOR_DIMS.MEDIUM]: "dim512",
|
|
257
|
+
[VECTOR_DIMS.LARGE]: "dim768",
|
|
258
|
+
[VECTOR_DIMS.XL]: "dim1024",
|
|
259
|
+
[VECTOR_DIMS.XXL]: "dim1536",
|
|
260
|
+
[VECTOR_DIMS.XXXL]: "dim3072"
|
|
261
|
+
};
|
|
262
|
+
var embeddingTable = pgTable6(
|
|
263
|
+
"embeddings",
|
|
264
|
+
{
|
|
265
|
+
id: uuid6("id").primaryKey().defaultRandom().notNull(),
|
|
266
|
+
memoryId: uuid6("memory_id").references(() => memoryTable.id),
|
|
267
|
+
createdAt: numberTimestamp("created_at").default(sql6`now()`).notNull(),
|
|
268
|
+
dim384: vector2("dim_384", { dimensions: VECTOR_DIMS.SMALL }),
|
|
269
|
+
dim512: vector2("dim_512", { dimensions: VECTOR_DIMS.MEDIUM }),
|
|
270
|
+
dim768: vector2("dim_768", { dimensions: VECTOR_DIMS.LARGE }),
|
|
271
|
+
dim1024: vector2("dim_1024", { dimensions: VECTOR_DIMS.XL }),
|
|
272
|
+
dim1536: vector2("dim_1536", { dimensions: VECTOR_DIMS.XXL }),
|
|
273
|
+
dim3072: vector2("dim_3072", { dimensions: VECTOR_DIMS.XXXL })
|
|
274
|
+
},
|
|
275
|
+
(table) => [
|
|
276
|
+
check2("embedding_source_check", sql6`"memory_id" IS NOT NULL`),
|
|
277
|
+
index2("idx_embedding_memory").on(table.memoryId),
|
|
278
|
+
foreignKey2({
|
|
279
|
+
name: "fk_embedding_memory",
|
|
280
|
+
columns: [table.memoryId],
|
|
281
|
+
foreignColumns: [memoryTable.id]
|
|
282
|
+
}).onDelete("cascade")
|
|
283
|
+
]
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
// src/schema/cache.ts
|
|
287
|
+
import { sql as sql7 } from "drizzle-orm";
|
|
288
|
+
import { jsonb as jsonb6, pgTable as pgTable7, text as text6, unique as unique4, uuid as uuid7 } from "drizzle-orm/pg-core";
|
|
289
|
+
var cacheTable = pgTable7(
|
|
290
|
+
"cache",
|
|
291
|
+
{
|
|
292
|
+
id: uuid7("id").notNull().primaryKey().default(sql7`gen_random_uuid()`),
|
|
293
|
+
key: text6("key").notNull(),
|
|
294
|
+
agentId: uuid7("agentId").notNull().references(() => agentTable.id, { onDelete: "cascade" }),
|
|
295
|
+
value: jsonb6("value").notNull(),
|
|
296
|
+
createdAt: numberTimestamp("createdAt").default(sql7`now()`).notNull(),
|
|
297
|
+
expiresAt: numberTimestamp("expiresAt")
|
|
298
|
+
},
|
|
299
|
+
(table) => [unique4("cache_key_agent_unique").on(table.key, table.agentId)]
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
// src/schema/component.ts
|
|
303
|
+
import { pgTable as pgTable8, uuid as uuid8, jsonb as jsonb7, text as text7 } from "drizzle-orm/pg-core";
|
|
304
|
+
import { sql as sql8 } from "drizzle-orm";
|
|
305
|
+
var componentTable = pgTable8("components", {
|
|
306
|
+
id: uuid8("id").primaryKey().defaultRandom(),
|
|
307
|
+
entityId: uuid8("entityId").notNull().references(() => entityTable.id, { onDelete: "cascade" }),
|
|
308
|
+
agentId: uuid8("agentId").notNull().references(() => agentTable.id, { onDelete: "cascade" }),
|
|
309
|
+
roomId: uuid8("roomId").notNull().references(() => roomTable.id, { onDelete: "cascade" }),
|
|
310
|
+
worldId: uuid8("worldId").references(() => worldTable.id, {
|
|
311
|
+
onDelete: "cascade"
|
|
312
|
+
}),
|
|
313
|
+
sourceEntityId: uuid8("sourceEntityId").references(() => entityTable.id, {
|
|
314
|
+
onDelete: "cascade"
|
|
315
|
+
}),
|
|
316
|
+
type: text7("type").notNull(),
|
|
317
|
+
data: jsonb7("data").default(sql8`'{}'::jsonb`),
|
|
318
|
+
createdAt: numberTimestamp("createdAt").default(sql8`now()`).notNull()
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// src/schema/goal.ts
|
|
322
|
+
import { pgTable as pgTable9, uuid as uuid9, text as text8, jsonb as jsonb8, foreignKey as foreignKey3 } from "drizzle-orm/pg-core";
|
|
323
|
+
import { sql as sql9 } from "drizzle-orm";
|
|
324
|
+
var goalTable = pgTable9(
|
|
325
|
+
"goals",
|
|
326
|
+
{
|
|
327
|
+
id: uuid9("id").notNull().primaryKey().default(sql9`gen_random_uuid()`),
|
|
328
|
+
createdAt: numberTimestamp("createdAt").default(sql9`now()`).notNull(),
|
|
329
|
+
entityId: uuid9("entityId").references(() => entityTable.id, {
|
|
330
|
+
onDelete: "cascade"
|
|
331
|
+
}),
|
|
332
|
+
agentId: uuid9("agentId").references(() => agentTable.id, {
|
|
333
|
+
onDelete: "cascade"
|
|
334
|
+
}),
|
|
335
|
+
name: text8("name"),
|
|
336
|
+
status: text8("status"),
|
|
337
|
+
description: text8("description"),
|
|
338
|
+
roomId: uuid9("roomId").references(() => roomTable.id, {
|
|
339
|
+
onDelete: "cascade"
|
|
340
|
+
}),
|
|
341
|
+
objectives: jsonb8("objectives").default("[]").notNull()
|
|
342
|
+
},
|
|
343
|
+
(table) => [
|
|
344
|
+
foreignKey3({
|
|
345
|
+
name: "fk_room",
|
|
346
|
+
columns: [table.roomId],
|
|
347
|
+
foreignColumns: [roomTable.id]
|
|
348
|
+
}).onDelete("cascade"),
|
|
349
|
+
foreignKey3({
|
|
350
|
+
name: "fk_user",
|
|
351
|
+
columns: [table.entityId],
|
|
352
|
+
foreignColumns: [entityTable.id]
|
|
353
|
+
}).onDelete("cascade")
|
|
354
|
+
]
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
// src/schema/log.ts
|
|
358
|
+
import { pgTable as pgTable10, uuid as uuid10, text as text9, jsonb as jsonb9, foreignKey as foreignKey4 } from "drizzle-orm/pg-core";
|
|
359
|
+
import { sql as sql10 } from "drizzle-orm";
|
|
360
|
+
var logTable = pgTable10(
|
|
361
|
+
"logs",
|
|
362
|
+
{
|
|
363
|
+
id: uuid10("id").defaultRandom().notNull(),
|
|
364
|
+
createdAt: numberTimestamp("createdAt").default(sql10`now()`).notNull(),
|
|
365
|
+
entityId: uuid10("entityId").notNull().references(() => entityTable.id),
|
|
366
|
+
body: jsonb9("body").notNull(),
|
|
367
|
+
type: text9("type").notNull(),
|
|
368
|
+
roomId: uuid10("roomId").notNull().references(() => roomTable.id)
|
|
369
|
+
},
|
|
370
|
+
(table) => [
|
|
371
|
+
foreignKey4({
|
|
372
|
+
name: "fk_room",
|
|
373
|
+
columns: [table.roomId],
|
|
374
|
+
foreignColumns: [roomTable.id]
|
|
375
|
+
}).onDelete("cascade"),
|
|
376
|
+
foreignKey4({
|
|
377
|
+
name: "fk_user",
|
|
378
|
+
columns: [table.entityId],
|
|
379
|
+
foreignColumns: [entityTable.id]
|
|
380
|
+
}).onDelete("cascade")
|
|
381
|
+
]
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
// src/schema/participant.ts
|
|
385
|
+
import {
|
|
386
|
+
pgTable as pgTable11,
|
|
387
|
+
uuid as uuid11,
|
|
388
|
+
text as text10,
|
|
389
|
+
index as index3,
|
|
390
|
+
foreignKey as foreignKey5
|
|
391
|
+
} from "drizzle-orm/pg-core";
|
|
392
|
+
import { sql as sql11 } from "drizzle-orm";
|
|
393
|
+
var participantTable = pgTable11(
|
|
394
|
+
"participants",
|
|
395
|
+
{
|
|
396
|
+
id: uuid11("id").notNull().primaryKey().default(sql11`gen_random_uuid()`),
|
|
397
|
+
createdAt: numberTimestamp("createdAt").default(sql11`now()`).notNull(),
|
|
398
|
+
entityId: uuid11("entityId").references(() => entityTable.id, {
|
|
399
|
+
onDelete: "cascade"
|
|
400
|
+
}),
|
|
401
|
+
roomId: uuid11("roomId").references(() => roomTable.id, {
|
|
402
|
+
onDelete: "cascade"
|
|
403
|
+
}),
|
|
404
|
+
agentId: uuid11("agentId").references(() => agentTable.id, {
|
|
405
|
+
onDelete: "cascade"
|
|
406
|
+
}),
|
|
407
|
+
roomState: text10("roomState")
|
|
408
|
+
},
|
|
409
|
+
(table) => [
|
|
410
|
+
// unique("participants_user_room_agent_unique").on(table.entityId, table.roomId, table.agentId),
|
|
411
|
+
index3("idx_participants_user").on(table.entityId),
|
|
412
|
+
index3("idx_participants_room").on(table.roomId),
|
|
413
|
+
foreignKey5({
|
|
414
|
+
name: "fk_room",
|
|
415
|
+
columns: [table.roomId],
|
|
416
|
+
foreignColumns: [roomTable.id]
|
|
417
|
+
}).onDelete("cascade"),
|
|
418
|
+
foreignKey5({
|
|
419
|
+
name: "fk_user",
|
|
420
|
+
columns: [table.entityId],
|
|
421
|
+
foreignColumns: [entityTable.id]
|
|
422
|
+
}).onDelete("cascade")
|
|
423
|
+
]
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
// src/schema/relationship.ts
|
|
427
|
+
import { sql as sql12 } from "drizzle-orm";
|
|
428
|
+
import {
|
|
429
|
+
foreignKey as foreignKey6,
|
|
430
|
+
index as index4,
|
|
431
|
+
jsonb as jsonb10,
|
|
432
|
+
pgTable as pgTable12,
|
|
433
|
+
text as text11,
|
|
434
|
+
unique as unique6,
|
|
435
|
+
uuid as uuid12
|
|
436
|
+
} from "drizzle-orm/pg-core";
|
|
437
|
+
var relationshipTable = pgTable12(
|
|
438
|
+
"relationships",
|
|
439
|
+
{
|
|
440
|
+
id: uuid12("id").notNull().primaryKey().default(sql12`gen_random_uuid()`),
|
|
441
|
+
createdAt: numberTimestamp("createdAt").default(sql12`now()`).notNull(),
|
|
442
|
+
sourceEntityId: uuid12("sourceEntityId").notNull().references(() => entityTable.id, { onDelete: "cascade" }),
|
|
443
|
+
targetEntityId: uuid12("targetEntityId").notNull().references(() => entityTable.id, { onDelete: "cascade" }),
|
|
444
|
+
agentId: uuid12("agentId").notNull().references(() => agentTable.id, { onDelete: "cascade" }),
|
|
445
|
+
tags: text11("tags").array(),
|
|
446
|
+
metadata: jsonb10("metadata")
|
|
447
|
+
},
|
|
448
|
+
(table) => [
|
|
449
|
+
index4("idx_relationships_users").on(
|
|
450
|
+
table.sourceEntityId,
|
|
451
|
+
table.targetEntityId
|
|
452
|
+
),
|
|
453
|
+
unique6("unique_relationship").on(
|
|
454
|
+
table.sourceEntityId,
|
|
455
|
+
table.targetEntityId,
|
|
456
|
+
table.agentId
|
|
457
|
+
),
|
|
458
|
+
foreignKey6({
|
|
459
|
+
name: "fk_user_a",
|
|
460
|
+
columns: [table.sourceEntityId],
|
|
461
|
+
foreignColumns: [entityTable.id]
|
|
462
|
+
}).onDelete("cascade"),
|
|
463
|
+
foreignKey6({
|
|
464
|
+
name: "fk_user_b",
|
|
465
|
+
columns: [table.targetEntityId],
|
|
466
|
+
foreignColumns: [entityTable.id]
|
|
467
|
+
}).onDelete("cascade")
|
|
468
|
+
]
|
|
469
|
+
);
|
|
470
|
+
|
|
471
|
+
// src/schema/tasks.ts
|
|
472
|
+
import { jsonb as jsonb11, pgTable as pgTable13, text as text12, timestamp, uuid as uuid13 } from "drizzle-orm/pg-core";
|
|
473
|
+
var taskTable = pgTable13("tasks", {
|
|
474
|
+
id: uuid13("id").primaryKey().defaultRandom(),
|
|
475
|
+
name: text12("name").notNull(),
|
|
476
|
+
description: text12("description").notNull(),
|
|
477
|
+
roomId: uuid13("room_id"),
|
|
478
|
+
worldId: uuid13("world_id"),
|
|
479
|
+
agentId: uuid13("agent_id").notNull(),
|
|
480
|
+
tags: text12("tags").array(),
|
|
481
|
+
metadata: jsonb11("metadata"),
|
|
482
|
+
createdAt: timestamp("created_at").defaultNow(),
|
|
483
|
+
updatedAt: timestamp("updated_at").defaultNow()
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
// src/base.ts
|
|
487
|
+
var BaseDrizzleAdapter = class extends DatabaseAdapter {
|
|
488
|
+
maxRetries = 3;
|
|
489
|
+
baseDelay = 1e3;
|
|
490
|
+
maxDelay = 1e4;
|
|
491
|
+
jitterMax = 1e3;
|
|
492
|
+
embeddingDimension = DIMENSION_MAP[384];
|
|
493
|
+
agentId;
|
|
494
|
+
constructor(agentId) {
|
|
495
|
+
super();
|
|
496
|
+
this.agentId = agentId;
|
|
497
|
+
}
|
|
498
|
+
async withRetry(operation) {
|
|
499
|
+
let lastError = new Error("Unknown error");
|
|
500
|
+
for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
|
|
501
|
+
try {
|
|
502
|
+
return await operation();
|
|
503
|
+
} catch (error) {
|
|
504
|
+
lastError = error;
|
|
505
|
+
if (attempt < this.maxRetries) {
|
|
506
|
+
const backoffDelay = Math.min(
|
|
507
|
+
this.baseDelay * 2 ** (attempt - 1),
|
|
508
|
+
this.maxDelay
|
|
509
|
+
);
|
|
510
|
+
const jitter = Math.random() * this.jitterMax;
|
|
511
|
+
const delay = backoffDelay + jitter;
|
|
512
|
+
logger.warn(
|
|
513
|
+
`Database operation failed (attempt ${attempt}/${this.maxRetries}):`,
|
|
514
|
+
{
|
|
515
|
+
error: error instanceof Error ? error.message : String(error),
|
|
516
|
+
nextRetryIn: `${(delay / 1e3).toFixed(1)}s`
|
|
517
|
+
}
|
|
518
|
+
);
|
|
519
|
+
console.trace("****** Database operation failure source");
|
|
520
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
521
|
+
} else {
|
|
522
|
+
logger.error("Max retry attempts reached:", {
|
|
523
|
+
error: error instanceof Error ? error.message : String(error),
|
|
524
|
+
totalAttempts: attempt
|
|
525
|
+
});
|
|
526
|
+
throw error instanceof Error ? error : new Error(String(error));
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
throw lastError;
|
|
531
|
+
}
|
|
532
|
+
async ensureAgentExists(agent) {
|
|
533
|
+
if (!agent.name) {
|
|
534
|
+
throw new Error("Agent name is required");
|
|
535
|
+
}
|
|
536
|
+
const agents = await this.getAgents();
|
|
537
|
+
const existingAgent = agents.find(
|
|
538
|
+
(a) => a.name === agent.name
|
|
539
|
+
);
|
|
540
|
+
if (!existingAgent) {
|
|
541
|
+
await this.createAgent(agent);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
async ensureEmbeddingDimension(dimension) {
|
|
545
|
+
const existingMemory = await this.db.select({
|
|
546
|
+
embedding: embeddingTable
|
|
547
|
+
}).from(memoryTable).innerJoin(embeddingTable, eq(embeddingTable.memoryId, memoryTable.id)).where(eq(memoryTable.agentId, this.agentId)).limit(1);
|
|
548
|
+
if (existingMemory.length > 0) {
|
|
549
|
+
const usedDimension = Object.entries(DIMENSION_MAP).find(
|
|
550
|
+
([_, colName]) => existingMemory[0].embedding[colName] !== null
|
|
551
|
+
);
|
|
552
|
+
if (usedDimension && usedDimension[1] !== DIMENSION_MAP[dimension]) {
|
|
553
|
+
throw new Error("Cannot change embedding dimension for agent");
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
this.embeddingDimension = DIMENSION_MAP[dimension];
|
|
557
|
+
}
|
|
558
|
+
async getAgent(agentId) {
|
|
559
|
+
return this.withDatabase(async () => {
|
|
560
|
+
const result = await this.db.select().from(agentTable).where(eq(agentTable.id, agentId)).limit(1);
|
|
561
|
+
if (result.length === 0) return null;
|
|
562
|
+
return result[0];
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
async getAgents() {
|
|
566
|
+
return this.withDatabase(async () => {
|
|
567
|
+
const result = await this.db.select().from(agentTable);
|
|
568
|
+
return result;
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
async createAgent(agent) {
|
|
572
|
+
return this.withDatabase(async () => {
|
|
573
|
+
try {
|
|
574
|
+
await this.db.transaction(async (tx) => {
|
|
575
|
+
await tx.insert(agentTable).values({
|
|
576
|
+
...agent
|
|
577
|
+
});
|
|
578
|
+
});
|
|
579
|
+
logger.debug("Agent created successfully:", {
|
|
580
|
+
agentId: agent.id
|
|
581
|
+
});
|
|
582
|
+
return true;
|
|
583
|
+
} catch (error) {
|
|
584
|
+
logger.error("Error creating agent:", {
|
|
585
|
+
error: error instanceof Error ? error.message : String(error),
|
|
586
|
+
agentId: agent.id,
|
|
587
|
+
agent
|
|
588
|
+
});
|
|
589
|
+
return false;
|
|
590
|
+
}
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
async updateAgent(agentId, agent) {
|
|
594
|
+
return this.withDatabase(async () => {
|
|
595
|
+
try {
|
|
596
|
+
if (!agent.id) {
|
|
597
|
+
throw new Error("Agent ID is required for update");
|
|
598
|
+
}
|
|
599
|
+
await this.db.transaction(async (tx) => {
|
|
600
|
+
await tx.update(agentTable).set({
|
|
601
|
+
...agent,
|
|
602
|
+
updatedAt: Date.now()
|
|
603
|
+
}).where(eq(agentTable.id, agentId));
|
|
604
|
+
});
|
|
605
|
+
logger.debug("Agent updated successfully:", {
|
|
606
|
+
agentId
|
|
607
|
+
});
|
|
608
|
+
return true;
|
|
609
|
+
} catch (error) {
|
|
610
|
+
logger.error("Error updating agent:", {
|
|
611
|
+
error: error instanceof Error ? error.message : String(error),
|
|
612
|
+
agentId,
|
|
613
|
+
agent
|
|
614
|
+
});
|
|
615
|
+
return false;
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
async deleteAgent(agentId) {
|
|
620
|
+
return this.withDatabase(async () => {
|
|
621
|
+
await this.db.transaction(async (tx) => {
|
|
622
|
+
await tx.delete(agentTable).where(eq(agentTable.id, agentId));
|
|
623
|
+
});
|
|
624
|
+
return true;
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Count all agents in the database
|
|
629
|
+
* Used primarily for maintenance and cleanup operations
|
|
630
|
+
*/
|
|
631
|
+
async countAgents() {
|
|
632
|
+
return this.withDatabase(async () => {
|
|
633
|
+
try {
|
|
634
|
+
const result = await this.db.select({ count: count() }).from(agentTable);
|
|
635
|
+
return result[0]?.count || 0;
|
|
636
|
+
} catch (error) {
|
|
637
|
+
logger.error("Error counting agents:", {
|
|
638
|
+
error: error instanceof Error ? error.message : String(error)
|
|
639
|
+
});
|
|
640
|
+
return 0;
|
|
641
|
+
}
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Clean up the agents table by removing all agents
|
|
646
|
+
* This is used during server startup to ensure no orphaned agents exist
|
|
647
|
+
* from previous crashes or improper shutdowns
|
|
648
|
+
*/
|
|
649
|
+
async cleanupAgents() {
|
|
650
|
+
return this.withDatabase(async () => {
|
|
651
|
+
try {
|
|
652
|
+
await this.db.delete(agentTable);
|
|
653
|
+
logger.success("Successfully cleaned up agent table");
|
|
654
|
+
} catch (error) {
|
|
655
|
+
logger.error("Error cleaning up agent table:", {
|
|
656
|
+
error: error instanceof Error ? error.message : String(error)
|
|
657
|
+
});
|
|
658
|
+
throw error;
|
|
659
|
+
}
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
async getEntityById(entityId) {
|
|
663
|
+
return this.withDatabase(async () => {
|
|
664
|
+
const result = await this.db.select({
|
|
665
|
+
entity: entityTable,
|
|
666
|
+
components: componentTable
|
|
667
|
+
}).from(entityTable).leftJoin(componentTable, eq(componentTable.entityId, entityTable.id)).where(
|
|
668
|
+
and(
|
|
669
|
+
eq(entityTable.id, entityId),
|
|
670
|
+
eq(entityTable.agentId, this.agentId)
|
|
671
|
+
)
|
|
672
|
+
);
|
|
673
|
+
if (result.length === 0) return null;
|
|
674
|
+
const entity = result[0].entity;
|
|
675
|
+
entity.components = result.filter((row) => row.components).map((row) => row.components);
|
|
676
|
+
return entity;
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
async getEntitiesForRoom(roomId, includeComponents) {
|
|
680
|
+
return this.withDatabase(async () => {
|
|
681
|
+
const query = this.db.select({
|
|
682
|
+
entity: entityTable,
|
|
683
|
+
...includeComponents && { components: componentTable }
|
|
684
|
+
}).from(participantTable).leftJoin(
|
|
685
|
+
entityTable,
|
|
686
|
+
and(
|
|
687
|
+
eq(participantTable.entityId, entityTable.id),
|
|
688
|
+
eq(entityTable.agentId, this.agentId)
|
|
689
|
+
)
|
|
690
|
+
);
|
|
691
|
+
if (includeComponents) {
|
|
692
|
+
query.leftJoin(
|
|
693
|
+
componentTable,
|
|
694
|
+
eq(componentTable.entityId, entityTable.id)
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
const result = await query.where(eq(participantTable.roomId, roomId));
|
|
698
|
+
const entitiesByIdMap = /* @__PURE__ */ new Map();
|
|
699
|
+
for (const row of result) {
|
|
700
|
+
if (!row.entity) continue;
|
|
701
|
+
const entityId = row.entity.id;
|
|
702
|
+
if (!entitiesByIdMap.has(entityId)) {
|
|
703
|
+
const entity = {
|
|
704
|
+
...row.entity,
|
|
705
|
+
components: includeComponents ? [] : void 0
|
|
706
|
+
};
|
|
707
|
+
entitiesByIdMap.set(entityId, entity);
|
|
708
|
+
}
|
|
709
|
+
if (includeComponents && row.components) {
|
|
710
|
+
const entity = entitiesByIdMap.get(entityId);
|
|
711
|
+
if (entity) {
|
|
712
|
+
if (!entity.components) {
|
|
713
|
+
entity.components = [];
|
|
714
|
+
}
|
|
715
|
+
entity.components.push(row.components);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
return Array.from(entitiesByIdMap.values());
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
async createEntity(entity) {
|
|
723
|
+
return this.withDatabase(async () => {
|
|
724
|
+
try {
|
|
725
|
+
return await this.db.transaction(async (tx) => {
|
|
726
|
+
await tx.insert(entityTable).values(entity);
|
|
727
|
+
logger.debug("Entity created successfully:", {
|
|
728
|
+
entity
|
|
729
|
+
});
|
|
730
|
+
return true;
|
|
731
|
+
});
|
|
732
|
+
} catch (error) {
|
|
733
|
+
logger.error("Error creating account:", {
|
|
734
|
+
error: error instanceof Error ? error.message : String(error),
|
|
735
|
+
entityId: entity.id,
|
|
736
|
+
name: entity.metadata?.name
|
|
737
|
+
});
|
|
738
|
+
return false;
|
|
739
|
+
}
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* Ensures an entity exists, creating it if it doesn't
|
|
744
|
+
* @param entity The entity to ensure exists
|
|
745
|
+
* @returns Promise resolving to boolean indicating success
|
|
746
|
+
*/
|
|
747
|
+
async ensureEntityExists(entity) {
|
|
748
|
+
if (!entity.id) {
|
|
749
|
+
logger.error("Entity ID is required for ensureEntityExists");
|
|
750
|
+
return false;
|
|
751
|
+
}
|
|
752
|
+
try {
|
|
753
|
+
const existingEntity = await this.getEntityById(entity.id);
|
|
754
|
+
if (!existingEntity) {
|
|
755
|
+
return await this.createEntity(entity);
|
|
756
|
+
}
|
|
757
|
+
return true;
|
|
758
|
+
} catch (error) {
|
|
759
|
+
logger.error("Error ensuring entity exists:", {
|
|
760
|
+
error: error instanceof Error ? error.message : String(error),
|
|
761
|
+
entityId: entity.id
|
|
762
|
+
});
|
|
763
|
+
return false;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
async updateEntity(entity) {
|
|
767
|
+
return this.withDatabase(async () => {
|
|
768
|
+
await this.db.update(entityTable).set(entity).where(
|
|
769
|
+
and(
|
|
770
|
+
eq(entityTable.id, entity.id),
|
|
771
|
+
eq(entityTable.agentId, entity.agentId)
|
|
772
|
+
)
|
|
773
|
+
);
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
async getComponent(entityId, type, worldId, sourceEntityId) {
|
|
777
|
+
return this.withDatabase(async () => {
|
|
778
|
+
const conditions = [
|
|
779
|
+
eq(componentTable.entityId, entityId),
|
|
780
|
+
eq(componentTable.type, type)
|
|
781
|
+
];
|
|
782
|
+
if (worldId) {
|
|
783
|
+
conditions.push(eq(componentTable.worldId, worldId));
|
|
784
|
+
}
|
|
785
|
+
if (sourceEntityId) {
|
|
786
|
+
conditions.push(eq(componentTable.sourceEntityId, sourceEntityId));
|
|
787
|
+
}
|
|
788
|
+
const result = await this.db.select().from(componentTable).where(and(...conditions));
|
|
789
|
+
return result.length > 0 ? result[0] : null;
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
async getComponents(entityId, worldId, sourceEntityId) {
|
|
793
|
+
return this.withDatabase(async () => {
|
|
794
|
+
const conditions = [eq(componentTable.entityId, entityId)];
|
|
795
|
+
if (worldId) {
|
|
796
|
+
conditions.push(eq(componentTable.worldId, worldId));
|
|
797
|
+
}
|
|
798
|
+
if (sourceEntityId) {
|
|
799
|
+
conditions.push(eq(componentTable.sourceEntityId, sourceEntityId));
|
|
800
|
+
}
|
|
801
|
+
const result = await this.db.select({
|
|
802
|
+
id: componentTable.id,
|
|
803
|
+
entityId: componentTable.entityId,
|
|
804
|
+
type: componentTable.type,
|
|
805
|
+
data: componentTable.data,
|
|
806
|
+
worldId: componentTable.worldId,
|
|
807
|
+
sourceEntityId: componentTable.sourceEntityId,
|
|
808
|
+
createdAt: componentTable.createdAt
|
|
809
|
+
}).from(componentTable).where(and(...conditions));
|
|
810
|
+
return result;
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
async createComponent(component) {
|
|
814
|
+
return this.withDatabase(async () => {
|
|
815
|
+
await this.db.insert(componentTable).values(component);
|
|
816
|
+
return true;
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
async updateComponent(component) {
|
|
820
|
+
return this.withDatabase(async () => {
|
|
821
|
+
await this.db.update(componentTable).set(component).where(eq(componentTable.id, component.id));
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
async deleteComponent(componentId) {
|
|
825
|
+
return this.withDatabase(async () => {
|
|
826
|
+
await this.db.delete(componentTable).where(eq(componentTable.id, componentId));
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
async getMemories(params) {
|
|
830
|
+
if (!params.tableName) throw new Error("tableName is required");
|
|
831
|
+
if (!params.roomId) throw new Error("roomId is required");
|
|
832
|
+
return this.withDatabase(async () => {
|
|
833
|
+
const conditions = [
|
|
834
|
+
eq(memoryTable.type, params.tableName),
|
|
835
|
+
eq(memoryTable.roomId, params.roomId)
|
|
836
|
+
];
|
|
837
|
+
if (params.start) {
|
|
838
|
+
conditions.push(gte(memoryTable.createdAt, params.start));
|
|
839
|
+
}
|
|
840
|
+
if (params.end) {
|
|
841
|
+
conditions.push(lte(memoryTable.createdAt, params.end));
|
|
842
|
+
}
|
|
843
|
+
if (params.unique) {
|
|
844
|
+
conditions.push(eq(memoryTable.unique, true));
|
|
845
|
+
}
|
|
846
|
+
conditions.push(eq(memoryTable.agentId, this.agentId));
|
|
847
|
+
const query = this.db.select({
|
|
848
|
+
memory: {
|
|
849
|
+
id: memoryTable.id,
|
|
850
|
+
type: memoryTable.type,
|
|
851
|
+
createdAt: memoryTable.createdAt,
|
|
852
|
+
content: memoryTable.content,
|
|
853
|
+
entityId: memoryTable.entityId,
|
|
854
|
+
agentId: memoryTable.agentId,
|
|
855
|
+
roomId: memoryTable.roomId,
|
|
856
|
+
unique: memoryTable.unique
|
|
857
|
+
},
|
|
858
|
+
embedding: embeddingTable[this.embeddingDimension]
|
|
859
|
+
}).from(memoryTable).leftJoin(embeddingTable, eq(embeddingTable.memoryId, memoryTable.id)).where(and(...conditions)).orderBy(desc(memoryTable.createdAt));
|
|
860
|
+
const rows = params.count ? await query.limit(params.count) : await query;
|
|
861
|
+
return rows.map((row) => ({
|
|
862
|
+
id: row.memory.id,
|
|
863
|
+
type: row.memory.type,
|
|
864
|
+
createdAt: row.memory.createdAt,
|
|
865
|
+
content: typeof row.memory.content === "string" ? JSON.parse(row.memory.content) : row.memory.content,
|
|
866
|
+
entityId: row.memory.entityId,
|
|
867
|
+
agentId: row.memory.agentId,
|
|
868
|
+
roomId: row.memory.roomId,
|
|
869
|
+
unique: row.memory.unique,
|
|
870
|
+
embedding: row.embedding ? Array.from(row.embedding) : void 0
|
|
871
|
+
}));
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
async getMemoriesByRoomIds(params) {
|
|
875
|
+
return this.withDatabase(async () => {
|
|
876
|
+
if (params.roomIds.length === 0) return [];
|
|
877
|
+
const conditions = [
|
|
878
|
+
eq(memoryTable.type, params.tableName),
|
|
879
|
+
inArray(memoryTable.roomId, params.roomIds)
|
|
880
|
+
];
|
|
881
|
+
conditions.push(eq(memoryTable.agentId, this.agentId));
|
|
882
|
+
const query = this.db.select({
|
|
883
|
+
id: memoryTable.id,
|
|
884
|
+
type: memoryTable.type,
|
|
885
|
+
createdAt: memoryTable.createdAt,
|
|
886
|
+
content: memoryTable.content,
|
|
887
|
+
entityId: memoryTable.entityId,
|
|
888
|
+
agentId: memoryTable.agentId,
|
|
889
|
+
roomId: memoryTable.roomId,
|
|
890
|
+
unique: memoryTable.unique
|
|
891
|
+
}).from(memoryTable).where(and(...conditions)).orderBy(desc(memoryTable.createdAt));
|
|
892
|
+
const rows = params.limit ? await query.limit(params.limit) : await query;
|
|
893
|
+
return rows.map((row) => ({
|
|
894
|
+
id: row.id,
|
|
895
|
+
createdAt: row.createdAt,
|
|
896
|
+
content: typeof row.content === "string" ? JSON.parse(row.content) : row.content,
|
|
897
|
+
entityId: row.entityId,
|
|
898
|
+
agentId: row.agentId,
|
|
899
|
+
roomId: row.roomId,
|
|
900
|
+
unique: row.unique
|
|
901
|
+
}));
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
async getMemoryById(id) {
|
|
905
|
+
return this.withDatabase(async () => {
|
|
906
|
+
const result = await this.db.select({
|
|
907
|
+
memory: memoryTable,
|
|
908
|
+
embedding: embeddingTable[this.embeddingDimension]
|
|
909
|
+
}).from(memoryTable).leftJoin(embeddingTable, eq(memoryTable.id, embeddingTable.memoryId)).where(eq(memoryTable.id, id)).limit(1);
|
|
910
|
+
if (result.length === 0) return null;
|
|
911
|
+
const row = result[0];
|
|
912
|
+
return {
|
|
913
|
+
id: row.memory.id,
|
|
914
|
+
createdAt: row.memory.createdAt,
|
|
915
|
+
content: typeof row.memory.content === "string" ? JSON.parse(row.memory.content) : row.memory.content,
|
|
916
|
+
entityId: row.memory.entityId,
|
|
917
|
+
agentId: row.memory.agentId,
|
|
918
|
+
roomId: row.memory.roomId,
|
|
919
|
+
unique: row.memory.unique,
|
|
920
|
+
embedding: row.embedding ?? void 0
|
|
921
|
+
};
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
async getMemoriesByIds(memoryIds, tableName) {
|
|
925
|
+
return this.withDatabase(async () => {
|
|
926
|
+
if (memoryIds.length === 0) return [];
|
|
927
|
+
const conditions = [inArray(memoryTable.id, memoryIds)];
|
|
928
|
+
if (tableName) {
|
|
929
|
+
conditions.push(eq(memoryTable.type, tableName));
|
|
930
|
+
}
|
|
931
|
+
const rows = await this.db.select({
|
|
932
|
+
memory: memoryTable,
|
|
933
|
+
embedding: embeddingTable[this.embeddingDimension]
|
|
934
|
+
}).from(memoryTable).leftJoin(embeddingTable, eq(embeddingTable.memoryId, memoryTable.id)).where(and(...conditions)).orderBy(desc(memoryTable.createdAt));
|
|
935
|
+
return rows.map((row) => ({
|
|
936
|
+
id: row.memory.id,
|
|
937
|
+
createdAt: row.memory.createdAt,
|
|
938
|
+
content: typeof row.memory.content === "string" ? JSON.parse(row.memory.content) : row.memory.content,
|
|
939
|
+
entityId: row.memory.entityId,
|
|
940
|
+
agentId: row.memory.agentId,
|
|
941
|
+
roomId: row.memory.roomId,
|
|
942
|
+
unique: row.memory.unique,
|
|
943
|
+
embedding: row.embedding ?? void 0
|
|
944
|
+
}));
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
async getCachedEmbeddings(opts) {
|
|
948
|
+
return this.withDatabase(async () => {
|
|
949
|
+
try {
|
|
950
|
+
const results = await this.db.execute(sql13`
|
|
951
|
+
WITH content_text AS (
|
|
952
|
+
SELECT
|
|
953
|
+
m.id,
|
|
954
|
+
COALESCE(
|
|
955
|
+
m.content->>${opts.query_field_sub_name},
|
|
956
|
+
''
|
|
957
|
+
) as content_text
|
|
958
|
+
FROM memories m
|
|
959
|
+
WHERE m.type = ${opts.query_table_name}
|
|
960
|
+
AND m.content->>${opts.query_field_sub_name} IS NOT NULL
|
|
961
|
+
),
|
|
962
|
+
embedded_text AS (
|
|
963
|
+
SELECT
|
|
964
|
+
ct.content_text,
|
|
965
|
+
COALESCE(
|
|
966
|
+
e.dim_384,
|
|
967
|
+
e.dim_512,
|
|
968
|
+
e.dim_768,
|
|
969
|
+
e.dim_1024,
|
|
970
|
+
e.dim_1536,
|
|
971
|
+
e.dim_3072
|
|
972
|
+
) as embedding
|
|
973
|
+
FROM content_text ct
|
|
974
|
+
LEFT JOIN embeddings e ON e.memory_id = ct.id
|
|
975
|
+
WHERE e.memory_id IS NOT NULL
|
|
976
|
+
)
|
|
977
|
+
SELECT
|
|
978
|
+
embedding,
|
|
979
|
+
levenshtein(${opts.query_input}, content_text) as levenshtein_score
|
|
980
|
+
FROM embedded_text
|
|
981
|
+
WHERE levenshtein(${opts.query_input}, content_text) <= ${opts.query_threshold}
|
|
982
|
+
ORDER BY levenshtein_score
|
|
983
|
+
LIMIT ${opts.query_match_count}
|
|
984
|
+
`);
|
|
985
|
+
return results.rows.map((row) => ({
|
|
986
|
+
embedding: Array.isArray(row.embedding) ? row.embedding : typeof row.embedding === "string" ? JSON.parse(row.embedding) : [],
|
|
987
|
+
levenshtein_score: Number(row.levenshtein_score)
|
|
988
|
+
})).filter((row) => Array.isArray(row.embedding));
|
|
989
|
+
} catch (error) {
|
|
990
|
+
logger.error("Error in getCachedEmbeddings:", {
|
|
991
|
+
error: error instanceof Error ? error.message : String(error),
|
|
992
|
+
tableName: opts.query_table_name,
|
|
993
|
+
fieldName: opts.query_field_name
|
|
994
|
+
});
|
|
995
|
+
if (error instanceof Error && error.message === "levenshtein argument exceeds maximum length of 255 characters") {
|
|
996
|
+
return [];
|
|
997
|
+
}
|
|
998
|
+
throw error;
|
|
999
|
+
}
|
|
1000
|
+
});
|
|
1001
|
+
}
|
|
1002
|
+
async log(params) {
|
|
1003
|
+
return this.withDatabase(async () => {
|
|
1004
|
+
try {
|
|
1005
|
+
await this.db.transaction(async (tx) => {
|
|
1006
|
+
await tx.insert(logTable).values({
|
|
1007
|
+
body: sql13`${params.body}::jsonb`,
|
|
1008
|
+
entityId: params.entityId,
|
|
1009
|
+
roomId: params.roomId,
|
|
1010
|
+
type: params.type
|
|
1011
|
+
});
|
|
1012
|
+
});
|
|
1013
|
+
} catch (error) {
|
|
1014
|
+
logger.error("Failed to create log entry:", {
|
|
1015
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1016
|
+
type: params.type,
|
|
1017
|
+
roomId: params.roomId,
|
|
1018
|
+
entityId: params.entityId
|
|
1019
|
+
});
|
|
1020
|
+
throw error;
|
|
1021
|
+
}
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
async searchMemories(params) {
|
|
1025
|
+
return await this.searchMemoriesByEmbedding(params.embedding, {
|
|
1026
|
+
match_threshold: params.match_threshold,
|
|
1027
|
+
count: params.count,
|
|
1028
|
+
roomId: params.roomId,
|
|
1029
|
+
unique: params.unique,
|
|
1030
|
+
tableName: params.tableName
|
|
1031
|
+
});
|
|
1032
|
+
}
|
|
1033
|
+
async updateGoalStatus(params) {
|
|
1034
|
+
return this.withDatabase(async () => {
|
|
1035
|
+
try {
|
|
1036
|
+
await this.db.transaction(async (tx) => {
|
|
1037
|
+
await tx.update(goalTable).set({
|
|
1038
|
+
status: params.status
|
|
1039
|
+
}).where(eq(goalTable.id, params.goalId));
|
|
1040
|
+
});
|
|
1041
|
+
} catch (error) {
|
|
1042
|
+
logger.error("Failed to update goal status:", {
|
|
1043
|
+
goalId: params.goalId,
|
|
1044
|
+
status: params.status,
|
|
1045
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1046
|
+
});
|
|
1047
|
+
throw error;
|
|
1048
|
+
}
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
async searchMemoriesByEmbedding(embedding, params) {
|
|
1052
|
+
return this.withDatabase(async () => {
|
|
1053
|
+
const cleanVector = embedding.map(
|
|
1054
|
+
(n) => Number.isFinite(n) ? Number(n.toFixed(6)) : 0
|
|
1055
|
+
);
|
|
1056
|
+
const similarity = sql13`1 - (${cosineDistance(
|
|
1057
|
+
embeddingTable[this.embeddingDimension],
|
|
1058
|
+
cleanVector
|
|
1059
|
+
)})`;
|
|
1060
|
+
const conditions = [eq(memoryTable.type, params.tableName)];
|
|
1061
|
+
if (params.unique) {
|
|
1062
|
+
conditions.push(eq(memoryTable.unique, true));
|
|
1063
|
+
}
|
|
1064
|
+
conditions.push(eq(memoryTable.agentId, this.agentId));
|
|
1065
|
+
if (params.roomId) {
|
|
1066
|
+
conditions.push(eq(memoryTable.roomId, params.roomId));
|
|
1067
|
+
}
|
|
1068
|
+
if (params.match_threshold) {
|
|
1069
|
+
conditions.push(gte(similarity, params.match_threshold));
|
|
1070
|
+
}
|
|
1071
|
+
const results = await this.db.select({
|
|
1072
|
+
memory: memoryTable,
|
|
1073
|
+
similarity,
|
|
1074
|
+
embedding: embeddingTable[this.embeddingDimension]
|
|
1075
|
+
}).from(embeddingTable).innerJoin(memoryTable, eq(memoryTable.id, embeddingTable.memoryId)).where(and(...conditions)).orderBy(desc(similarity)).limit(params.count ?? 10);
|
|
1076
|
+
return results.map((row) => ({
|
|
1077
|
+
id: row.memory.id,
|
|
1078
|
+
type: row.memory.type,
|
|
1079
|
+
createdAt: row.memory.createdAt,
|
|
1080
|
+
content: typeof row.memory.content === "string" ? JSON.parse(row.memory.content) : row.memory.content,
|
|
1081
|
+
entityId: row.memory.entityId,
|
|
1082
|
+
agentId: row.memory.agentId,
|
|
1083
|
+
roomId: row.memory.roomId,
|
|
1084
|
+
unique: row.memory.unique,
|
|
1085
|
+
embedding: row.embedding ?? void 0,
|
|
1086
|
+
similarity: row.similarity
|
|
1087
|
+
}));
|
|
1088
|
+
});
|
|
1089
|
+
}
|
|
1090
|
+
async createMemory(memory, tableName) {
|
|
1091
|
+
logger.debug("DrizzleAdapter createMemory:", {
|
|
1092
|
+
memoryId: memory.id,
|
|
1093
|
+
embeddingLength: memory.embedding?.length,
|
|
1094
|
+
contentLength: memory.content?.text?.length
|
|
1095
|
+
});
|
|
1096
|
+
let isUnique = true;
|
|
1097
|
+
if (memory.embedding && Array.isArray(memory.embedding)) {
|
|
1098
|
+
const similarMemories = await this.searchMemoriesByEmbedding(
|
|
1099
|
+
memory.embedding,
|
|
1100
|
+
{
|
|
1101
|
+
tableName,
|
|
1102
|
+
roomId: memory.roomId,
|
|
1103
|
+
match_threshold: 0.95,
|
|
1104
|
+
count: 1
|
|
1105
|
+
}
|
|
1106
|
+
);
|
|
1107
|
+
isUnique = similarMemories.length === 0;
|
|
1108
|
+
}
|
|
1109
|
+
const contentToInsert = typeof memory.content === "string" ? JSON.parse(memory.content) : memory.content;
|
|
1110
|
+
const memoryId = memory.id ?? v4();
|
|
1111
|
+
await this.db.transaction(async (tx) => {
|
|
1112
|
+
await tx.insert(memoryTable).values([
|
|
1113
|
+
{
|
|
1114
|
+
id: memoryId,
|
|
1115
|
+
type: tableName,
|
|
1116
|
+
content: sql13`${contentToInsert}::jsonb`,
|
|
1117
|
+
metadata: sql13`${memory.metadata || {}}::jsonb`,
|
|
1118
|
+
entityId: memory.entityId,
|
|
1119
|
+
roomId: memory.roomId,
|
|
1120
|
+
agentId: memory.agentId,
|
|
1121
|
+
unique: memory.unique ?? isUnique,
|
|
1122
|
+
createdAt: memory.createdAt
|
|
1123
|
+
}
|
|
1124
|
+
]);
|
|
1125
|
+
if (memory.embedding && Array.isArray(memory.embedding)) {
|
|
1126
|
+
const embeddingValues = {
|
|
1127
|
+
id: v4(),
|
|
1128
|
+
memoryId,
|
|
1129
|
+
createdAt: memory.createdAt
|
|
1130
|
+
};
|
|
1131
|
+
const cleanVector = memory.embedding.map(
|
|
1132
|
+
(n) => Number.isFinite(n) ? Number(n.toFixed(6)) : 0
|
|
1133
|
+
);
|
|
1134
|
+
embeddingValues[this.embeddingDimension] = cleanVector;
|
|
1135
|
+
await tx.insert(embeddingTable).values([embeddingValues]);
|
|
1136
|
+
}
|
|
1137
|
+
});
|
|
1138
|
+
return memoryId;
|
|
1139
|
+
}
|
|
1140
|
+
async removeMemory(memoryId, tableName) {
|
|
1141
|
+
return this.withDatabase(async () => {
|
|
1142
|
+
await this.db.transaction(async (tx) => {
|
|
1143
|
+
await tx.delete(embeddingTable).where(eq(embeddingTable.memoryId, memoryId));
|
|
1144
|
+
await tx.delete(memoryTable).where(
|
|
1145
|
+
and(eq(memoryTable.id, memoryId), eq(memoryTable.type, tableName))
|
|
1146
|
+
);
|
|
1147
|
+
});
|
|
1148
|
+
logger.debug("Memory removed successfully:", {
|
|
1149
|
+
memoryId,
|
|
1150
|
+
tableName
|
|
1151
|
+
});
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1154
|
+
async removeAllMemories(roomId, tableName) {
|
|
1155
|
+
return this.withDatabase(async () => {
|
|
1156
|
+
await this.db.transaction(async (tx) => {
|
|
1157
|
+
const memoryIds = await tx.select({ id: memoryTable.id }).from(memoryTable).where(
|
|
1158
|
+
and(
|
|
1159
|
+
eq(memoryTable.roomId, roomId),
|
|
1160
|
+
eq(memoryTable.type, tableName)
|
|
1161
|
+
)
|
|
1162
|
+
);
|
|
1163
|
+
if (memoryIds.length > 0) {
|
|
1164
|
+
await tx.delete(embeddingTable).where(
|
|
1165
|
+
inArray(
|
|
1166
|
+
embeddingTable.memoryId,
|
|
1167
|
+
memoryIds.map((m) => m.id)
|
|
1168
|
+
)
|
|
1169
|
+
);
|
|
1170
|
+
await tx.delete(memoryTable).where(
|
|
1171
|
+
and(
|
|
1172
|
+
eq(memoryTable.roomId, roomId),
|
|
1173
|
+
eq(memoryTable.type, tableName)
|
|
1174
|
+
)
|
|
1175
|
+
);
|
|
1176
|
+
}
|
|
1177
|
+
});
|
|
1178
|
+
logger.debug("All memories removed successfully:", {
|
|
1179
|
+
roomId,
|
|
1180
|
+
tableName
|
|
1181
|
+
});
|
|
1182
|
+
});
|
|
1183
|
+
}
|
|
1184
|
+
async countMemories(roomId, unique7 = true, tableName = "") {
|
|
1185
|
+
if (!tableName) throw new Error("tableName is required");
|
|
1186
|
+
return this.withDatabase(async () => {
|
|
1187
|
+
const conditions = [
|
|
1188
|
+
eq(memoryTable.roomId, roomId),
|
|
1189
|
+
eq(memoryTable.type, tableName)
|
|
1190
|
+
];
|
|
1191
|
+
if (unique7) {
|
|
1192
|
+
conditions.push(eq(memoryTable.unique, true));
|
|
1193
|
+
}
|
|
1194
|
+
const result = await this.db.select({ count: sql13`count(*)` }).from(memoryTable).where(and(...conditions));
|
|
1195
|
+
return Number(result[0]?.count ?? 0);
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1198
|
+
async getGoals(params) {
|
|
1199
|
+
return this.withDatabase(async () => {
|
|
1200
|
+
const conditions = [eq(goalTable.roomId, params.roomId)];
|
|
1201
|
+
if (params.entityId) {
|
|
1202
|
+
conditions.push(eq(goalTable.entityId, params.entityId));
|
|
1203
|
+
}
|
|
1204
|
+
if (params.onlyInProgress) {
|
|
1205
|
+
conditions.push(eq(goalTable.status, "IN_PROGRESS"));
|
|
1206
|
+
}
|
|
1207
|
+
const query = this.db.select().from(goalTable).where(and(...conditions)).orderBy(desc(goalTable.createdAt));
|
|
1208
|
+
const result = await (params.count ? query.limit(params.count) : query);
|
|
1209
|
+
return result.map((row) => ({
|
|
1210
|
+
id: row.id,
|
|
1211
|
+
roomId: row.roomId,
|
|
1212
|
+
entityId: row.entityId,
|
|
1213
|
+
name: row.name ?? "",
|
|
1214
|
+
status: row.status ?? "NOT_STARTED",
|
|
1215
|
+
description: row.description ?? "",
|
|
1216
|
+
objectives: row.objectives,
|
|
1217
|
+
createdAt: row.createdAt
|
|
1218
|
+
}));
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
async updateGoal(goal) {
|
|
1222
|
+
return this.withDatabase(async () => {
|
|
1223
|
+
try {
|
|
1224
|
+
await this.db.transaction(async (tx) => {
|
|
1225
|
+
await tx.update(goalTable).set({
|
|
1226
|
+
name: goal.name,
|
|
1227
|
+
status: goal.status,
|
|
1228
|
+
objectives: goal.objectives
|
|
1229
|
+
}).where(eq(goalTable.id, goal.id));
|
|
1230
|
+
});
|
|
1231
|
+
} catch (error) {
|
|
1232
|
+
logger.error("Failed to update goal:", {
|
|
1233
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1234
|
+
goalId: goal.id,
|
|
1235
|
+
status: goal.status
|
|
1236
|
+
});
|
|
1237
|
+
throw error;
|
|
1238
|
+
}
|
|
1239
|
+
});
|
|
1240
|
+
}
|
|
1241
|
+
async createGoal(goal) {
|
|
1242
|
+
return this.withDatabase(async () => {
|
|
1243
|
+
try {
|
|
1244
|
+
await this.db.transaction(async (tx) => {
|
|
1245
|
+
await tx.insert(goalTable).values({
|
|
1246
|
+
id: goal.id ?? v4(),
|
|
1247
|
+
roomId: goal.roomId,
|
|
1248
|
+
entityId: goal.entityId,
|
|
1249
|
+
name: goal.name,
|
|
1250
|
+
status: goal.status,
|
|
1251
|
+
objectives: sql13`${goal.objectives}::jsonb`
|
|
1252
|
+
});
|
|
1253
|
+
});
|
|
1254
|
+
} catch (error) {
|
|
1255
|
+
logger.error("Failed to update goal:", {
|
|
1256
|
+
goalId: goal.id,
|
|
1257
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1258
|
+
status: goal.status
|
|
1259
|
+
});
|
|
1260
|
+
throw error;
|
|
1261
|
+
}
|
|
1262
|
+
});
|
|
1263
|
+
}
|
|
1264
|
+
async removeGoal(goalId) {
|
|
1265
|
+
if (!goalId) throw new Error("Goal ID is required");
|
|
1266
|
+
return this.withDatabase(async () => {
|
|
1267
|
+
try {
|
|
1268
|
+
await this.db.transaction(async (tx) => {
|
|
1269
|
+
await tx.delete(goalTable).where(eq(goalTable.id, goalId));
|
|
1270
|
+
});
|
|
1271
|
+
logger.debug("Goal removal attempt:", {
|
|
1272
|
+
goalId,
|
|
1273
|
+
removed: true
|
|
1274
|
+
});
|
|
1275
|
+
} catch (error) {
|
|
1276
|
+
logger.error("Failed to remove goal:", {
|
|
1277
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1278
|
+
goalId
|
|
1279
|
+
});
|
|
1280
|
+
throw error;
|
|
1281
|
+
}
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
async removeAllGoals(roomId) {
|
|
1285
|
+
return this.withDatabase(async () => {
|
|
1286
|
+
await this.db.transaction(async (tx) => {
|
|
1287
|
+
await tx.delete(goalTable).where(eq(goalTable.roomId, roomId));
|
|
1288
|
+
});
|
|
1289
|
+
});
|
|
1290
|
+
}
|
|
1291
|
+
async getRoom(roomId) {
|
|
1292
|
+
return this.withDatabase(async () => {
|
|
1293
|
+
const result = await this.db.select({
|
|
1294
|
+
id: roomTable.id,
|
|
1295
|
+
channelId: roomTable.channelId,
|
|
1296
|
+
agentId: roomTable.agentId,
|
|
1297
|
+
serverId: roomTable.serverId,
|
|
1298
|
+
worldId: roomTable.worldId,
|
|
1299
|
+
type: roomTable.type,
|
|
1300
|
+
source: roomTable.source
|
|
1301
|
+
}).from(roomTable).where(
|
|
1302
|
+
and(eq(roomTable.id, roomId), eq(roomTable.agentId, this.agentId))
|
|
1303
|
+
).limit(1);
|
|
1304
|
+
if (result.length === 0) return null;
|
|
1305
|
+
return result[0];
|
|
1306
|
+
});
|
|
1307
|
+
}
|
|
1308
|
+
async getRooms(worldId) {
|
|
1309
|
+
return this.withDatabase(async () => {
|
|
1310
|
+
const result = await this.db.select().from(roomTable).where(eq(roomTable.worldId, worldId));
|
|
1311
|
+
return result;
|
|
1312
|
+
});
|
|
1313
|
+
}
|
|
1314
|
+
async updateRoom(room) {
|
|
1315
|
+
return this.withDatabase(async () => {
|
|
1316
|
+
await this.db.update(roomTable).set({ ...room, agentId: this.agentId }).where(eq(roomTable.id, room.id));
|
|
1317
|
+
});
|
|
1318
|
+
}
|
|
1319
|
+
async createRoom({
|
|
1320
|
+
id,
|
|
1321
|
+
name,
|
|
1322
|
+
source,
|
|
1323
|
+
type,
|
|
1324
|
+
channelId,
|
|
1325
|
+
serverId,
|
|
1326
|
+
worldId
|
|
1327
|
+
}) {
|
|
1328
|
+
return this.withDatabase(async () => {
|
|
1329
|
+
const newRoomId = id || v4();
|
|
1330
|
+
await this.db.insert(roomTable).values({
|
|
1331
|
+
id: newRoomId,
|
|
1332
|
+
name,
|
|
1333
|
+
agentId: this.agentId,
|
|
1334
|
+
source,
|
|
1335
|
+
type,
|
|
1336
|
+
channelId,
|
|
1337
|
+
serverId,
|
|
1338
|
+
worldId
|
|
1339
|
+
}).onConflictDoNothing({ target: roomTable.id });
|
|
1340
|
+
return newRoomId;
|
|
1341
|
+
});
|
|
1342
|
+
}
|
|
1343
|
+
async deleteRoom(roomId) {
|
|
1344
|
+
if (!roomId) throw new Error("Room ID is required");
|
|
1345
|
+
return this.withDatabase(async () => {
|
|
1346
|
+
await this.db.transaction(async (tx) => {
|
|
1347
|
+
await tx.delete(roomTable).where(eq(roomTable.id, roomId));
|
|
1348
|
+
});
|
|
1349
|
+
});
|
|
1350
|
+
}
|
|
1351
|
+
async getRoomsForParticipant(entityId) {
|
|
1352
|
+
return this.withDatabase(async () => {
|
|
1353
|
+
const result = await this.db.select({ roomId: participantTable.roomId }).from(participantTable).innerJoin(roomTable, eq(participantTable.roomId, roomTable.id)).where(
|
|
1354
|
+
and(
|
|
1355
|
+
eq(participantTable.entityId, entityId),
|
|
1356
|
+
eq(roomTable.agentId, this.agentId)
|
|
1357
|
+
)
|
|
1358
|
+
);
|
|
1359
|
+
return result.map((row) => row.roomId);
|
|
1360
|
+
});
|
|
1361
|
+
}
|
|
1362
|
+
async getRoomsForParticipants(entityIds) {
|
|
1363
|
+
return this.withDatabase(async () => {
|
|
1364
|
+
const result = await this.db.selectDistinct({ roomId: participantTable.roomId }).from(participantTable).innerJoin(roomTable, eq(participantTable.roomId, roomTable.id)).where(
|
|
1365
|
+
and(
|
|
1366
|
+
inArray(participantTable.entityId, entityIds),
|
|
1367
|
+
eq(roomTable.agentId, this.agentId)
|
|
1368
|
+
)
|
|
1369
|
+
);
|
|
1370
|
+
return result.map((row) => row.roomId);
|
|
1371
|
+
});
|
|
1372
|
+
}
|
|
1373
|
+
async addParticipant(entityId, roomId) {
|
|
1374
|
+
return this.withDatabase(async () => {
|
|
1375
|
+
try {
|
|
1376
|
+
await this.db.insert(participantTable).values({
|
|
1377
|
+
entityId,
|
|
1378
|
+
roomId,
|
|
1379
|
+
agentId: this.agentId
|
|
1380
|
+
}).onConflictDoNothing();
|
|
1381
|
+
return true;
|
|
1382
|
+
} catch (error) {
|
|
1383
|
+
logger.error("Error adding participant", {
|
|
1384
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1385
|
+
entityId,
|
|
1386
|
+
roomId,
|
|
1387
|
+
agentId: this.agentId
|
|
1388
|
+
});
|
|
1389
|
+
return false;
|
|
1390
|
+
}
|
|
1391
|
+
});
|
|
1392
|
+
}
|
|
1393
|
+
async removeParticipant(entityId, roomId) {
|
|
1394
|
+
return this.withDatabase(async () => {
|
|
1395
|
+
try {
|
|
1396
|
+
const result = await this.db.transaction(async (tx) => {
|
|
1397
|
+
return await tx.delete(participantTable).where(
|
|
1398
|
+
and(
|
|
1399
|
+
eq(participantTable.entityId, entityId),
|
|
1400
|
+
eq(participantTable.roomId, roomId)
|
|
1401
|
+
)
|
|
1402
|
+
).returning();
|
|
1403
|
+
});
|
|
1404
|
+
const removed = result.length > 0;
|
|
1405
|
+
logger.debug(`Participant ${removed ? "removed" : "not found"}:`, {
|
|
1406
|
+
entityId,
|
|
1407
|
+
roomId,
|
|
1408
|
+
removed
|
|
1409
|
+
});
|
|
1410
|
+
return removed;
|
|
1411
|
+
} catch (error) {
|
|
1412
|
+
logger.error("Failed to remove participant:", {
|
|
1413
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1414
|
+
entityId,
|
|
1415
|
+
roomId
|
|
1416
|
+
});
|
|
1417
|
+
return false;
|
|
1418
|
+
}
|
|
1419
|
+
});
|
|
1420
|
+
}
|
|
1421
|
+
async getParticipantsForEntity(entityId) {
|
|
1422
|
+
return this.withDatabase(async () => {
|
|
1423
|
+
const result = await this.db.select({
|
|
1424
|
+
id: participantTable.id,
|
|
1425
|
+
entityId: participantTable.entityId,
|
|
1426
|
+
roomId: participantTable.roomId
|
|
1427
|
+
}).from(participantTable).where(eq(participantTable.entityId, entityId));
|
|
1428
|
+
const entity = await this.getEntityById(entityId);
|
|
1429
|
+
if (!entity) {
|
|
1430
|
+
return [];
|
|
1431
|
+
}
|
|
1432
|
+
return result.map((row) => ({
|
|
1433
|
+
id: row.id,
|
|
1434
|
+
entity
|
|
1435
|
+
}));
|
|
1436
|
+
});
|
|
1437
|
+
}
|
|
1438
|
+
async getParticipantsForRoom(roomId) {
|
|
1439
|
+
return this.withDatabase(async () => {
|
|
1440
|
+
const result = await this.db.select({ entityId: participantTable.entityId }).from(participantTable).where(
|
|
1441
|
+
and(
|
|
1442
|
+
eq(participantTable.roomId, roomId),
|
|
1443
|
+
eq(participantTable.agentId, this.agentId)
|
|
1444
|
+
)
|
|
1445
|
+
);
|
|
1446
|
+
return result.map((row) => row.entityId);
|
|
1447
|
+
});
|
|
1448
|
+
}
|
|
1449
|
+
async getParticipantUserState(roomId, entityId) {
|
|
1450
|
+
return this.withDatabase(async () => {
|
|
1451
|
+
const result = await this.db.select({ roomState: participantTable.roomState }).from(participantTable).where(
|
|
1452
|
+
and(
|
|
1453
|
+
eq(participantTable.roomId, roomId),
|
|
1454
|
+
eq(participantTable.entityId, entityId),
|
|
1455
|
+
eq(participantTable.agentId, this.agentId)
|
|
1456
|
+
)
|
|
1457
|
+
).limit(1);
|
|
1458
|
+
return result[0]?.roomState ?? null;
|
|
1459
|
+
});
|
|
1460
|
+
}
|
|
1461
|
+
async setParticipantUserState(roomId, entityId, state) {
|
|
1462
|
+
return this.withDatabase(async () => {
|
|
1463
|
+
try {
|
|
1464
|
+
await this.db.transaction(async (tx) => {
|
|
1465
|
+
await tx.update(participantTable).set({ roomState: state }).where(
|
|
1466
|
+
and(
|
|
1467
|
+
eq(participantTable.roomId, roomId),
|
|
1468
|
+
eq(participantTable.entityId, entityId),
|
|
1469
|
+
eq(participantTable.agentId, this.agentId)
|
|
1470
|
+
)
|
|
1471
|
+
);
|
|
1472
|
+
});
|
|
1473
|
+
} catch (error) {
|
|
1474
|
+
logger.error("Failed to set participant user state:", {
|
|
1475
|
+
roomId,
|
|
1476
|
+
entityId,
|
|
1477
|
+
state,
|
|
1478
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1479
|
+
});
|
|
1480
|
+
throw error;
|
|
1481
|
+
}
|
|
1482
|
+
});
|
|
1483
|
+
}
|
|
1484
|
+
async createRelationship(params) {
|
|
1485
|
+
return this.withDatabase(async () => {
|
|
1486
|
+
const id = v4();
|
|
1487
|
+
const saveParams = {
|
|
1488
|
+
id,
|
|
1489
|
+
sourceEntityId: params.sourceEntityId,
|
|
1490
|
+
targetEntityId: params.targetEntityId,
|
|
1491
|
+
agentId: this.agentId,
|
|
1492
|
+
tags: params.tags || [],
|
|
1493
|
+
metadata: params.metadata || {}
|
|
1494
|
+
};
|
|
1495
|
+
try {
|
|
1496
|
+
await this.db.insert(relationshipTable).values(saveParams);
|
|
1497
|
+
return true;
|
|
1498
|
+
} catch (error) {
|
|
1499
|
+
logger.error("Error creating relationship:", {
|
|
1500
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1501
|
+
saveParams
|
|
1502
|
+
});
|
|
1503
|
+
return false;
|
|
1504
|
+
}
|
|
1505
|
+
});
|
|
1506
|
+
}
|
|
1507
|
+
async updateRelationship(relationship) {
|
|
1508
|
+
return this.withDatabase(async () => {
|
|
1509
|
+
try {
|
|
1510
|
+
await this.db.update(relationshipTable).set({
|
|
1511
|
+
tags: relationship.tags || [],
|
|
1512
|
+
metadata: relationship.metadata || {}
|
|
1513
|
+
}).where(eq(relationshipTable.id, relationship.id));
|
|
1514
|
+
} catch (error) {
|
|
1515
|
+
logger.error("Error updating relationship:", {
|
|
1516
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1517
|
+
relationship
|
|
1518
|
+
});
|
|
1519
|
+
throw error;
|
|
1520
|
+
}
|
|
1521
|
+
});
|
|
1522
|
+
}
|
|
1523
|
+
async getRelationship(params) {
|
|
1524
|
+
return this.withDatabase(async () => {
|
|
1525
|
+
try {
|
|
1526
|
+
const result = await this.db.select().from(relationshipTable).where(
|
|
1527
|
+
and(
|
|
1528
|
+
eq(relationshipTable.sourceEntityId, params.sourceEntityId),
|
|
1529
|
+
eq(relationshipTable.targetEntityId, params.targetEntityId),
|
|
1530
|
+
eq(relationshipTable.agentId, this.agentId)
|
|
1531
|
+
)
|
|
1532
|
+
).limit(1);
|
|
1533
|
+
if (result.length === 0) {
|
|
1534
|
+
return null;
|
|
1535
|
+
}
|
|
1536
|
+
return {
|
|
1537
|
+
id: result[0].id,
|
|
1538
|
+
sourceEntityId: result[0].sourceEntityId,
|
|
1539
|
+
targetEntityId: result[0].targetEntityId,
|
|
1540
|
+
agentId: result[0].agentId,
|
|
1541
|
+
tags: result[0].tags || [],
|
|
1542
|
+
metadata: result[0].metadata || {},
|
|
1543
|
+
createdAt: result[0].createdAt?.toString()
|
|
1544
|
+
};
|
|
1545
|
+
} catch (error) {
|
|
1546
|
+
logger.error("Error getting relationship:", {
|
|
1547
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1548
|
+
params
|
|
1549
|
+
});
|
|
1550
|
+
return null;
|
|
1551
|
+
}
|
|
1552
|
+
});
|
|
1553
|
+
}
|
|
1554
|
+
async getRelationships(params) {
|
|
1555
|
+
return this.withDatabase(async () => {
|
|
1556
|
+
try {
|
|
1557
|
+
let query = this.db.select().from(relationshipTable).where(
|
|
1558
|
+
and(
|
|
1559
|
+
or(
|
|
1560
|
+
eq(relationshipTable.sourceEntityId, params.entityId),
|
|
1561
|
+
eq(relationshipTable.targetEntityId, params.entityId)
|
|
1562
|
+
),
|
|
1563
|
+
eq(relationshipTable.agentId, this.agentId)
|
|
1564
|
+
)
|
|
1565
|
+
);
|
|
1566
|
+
if (params.tags && params.tags.length > 0) {
|
|
1567
|
+
const tagParams = params.tags.map((tag) => `'${tag.replace(/'/g, "''")}'`).join(", ");
|
|
1568
|
+
query = query.where(
|
|
1569
|
+
sql13`${relationshipTable.tags} @> ARRAY[${sql13.raw(tagParams)}]::text[]`
|
|
1570
|
+
);
|
|
1571
|
+
}
|
|
1572
|
+
const results = await query;
|
|
1573
|
+
return results.map((result) => ({
|
|
1574
|
+
id: result.id,
|
|
1575
|
+
sourceEntityId: result.sourceEntityId,
|
|
1576
|
+
targetEntityId: result.targetEntityId,
|
|
1577
|
+
agentId: result.agentId,
|
|
1578
|
+
tags: result.tags || [],
|
|
1579
|
+
metadata: result.metadata || {},
|
|
1580
|
+
createdAt: result.createdAt?.toString()
|
|
1581
|
+
}));
|
|
1582
|
+
} catch (error) {
|
|
1583
|
+
logger.error("Error getting relationships:", {
|
|
1584
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1585
|
+
params
|
|
1586
|
+
});
|
|
1587
|
+
return [];
|
|
1588
|
+
}
|
|
1589
|
+
});
|
|
1590
|
+
}
|
|
1591
|
+
async getCache(key) {
|
|
1592
|
+
return this.withDatabase(async () => {
|
|
1593
|
+
try {
|
|
1594
|
+
const result = await this.db.select().from(cacheTable).where(
|
|
1595
|
+
and(eq(cacheTable.agentId, this.agentId), eq(cacheTable.key, key))
|
|
1596
|
+
);
|
|
1597
|
+
return result[0]?.value;
|
|
1598
|
+
} catch (error) {
|
|
1599
|
+
logger.error("Error fetching cache", {
|
|
1600
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1601
|
+
key,
|
|
1602
|
+
agentId: this.agentId
|
|
1603
|
+
});
|
|
1604
|
+
return void 0;
|
|
1605
|
+
}
|
|
1606
|
+
});
|
|
1607
|
+
}
|
|
1608
|
+
async setCache(key, value) {
|
|
1609
|
+
return this.withDatabase(async () => {
|
|
1610
|
+
try {
|
|
1611
|
+
await this.db.transaction(async (tx) => {
|
|
1612
|
+
await tx.insert(cacheTable).values({
|
|
1613
|
+
key,
|
|
1614
|
+
agentId: this.agentId,
|
|
1615
|
+
value
|
|
1616
|
+
}).onConflictDoUpdate({
|
|
1617
|
+
target: [cacheTable.key, cacheTable.agentId],
|
|
1618
|
+
set: {
|
|
1619
|
+
value
|
|
1620
|
+
}
|
|
1621
|
+
});
|
|
1622
|
+
});
|
|
1623
|
+
return true;
|
|
1624
|
+
} catch (error) {
|
|
1625
|
+
logger.error("Error setting cache", {
|
|
1626
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1627
|
+
key,
|
|
1628
|
+
agentId: this.agentId
|
|
1629
|
+
});
|
|
1630
|
+
return false;
|
|
1631
|
+
}
|
|
1632
|
+
});
|
|
1633
|
+
}
|
|
1634
|
+
async deleteCache(key) {
|
|
1635
|
+
return this.withDatabase(async () => {
|
|
1636
|
+
try {
|
|
1637
|
+
await this.db.transaction(async (tx) => {
|
|
1638
|
+
await tx.delete(cacheTable).where(
|
|
1639
|
+
and(
|
|
1640
|
+
eq(cacheTable.agentId, this.agentId),
|
|
1641
|
+
eq(cacheTable.key, key)
|
|
1642
|
+
)
|
|
1643
|
+
);
|
|
1644
|
+
});
|
|
1645
|
+
return true;
|
|
1646
|
+
} catch (error) {
|
|
1647
|
+
logger.error("Error deleting cache", {
|
|
1648
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1649
|
+
key,
|
|
1650
|
+
agentId: this.agentId
|
|
1651
|
+
});
|
|
1652
|
+
return false;
|
|
1653
|
+
}
|
|
1654
|
+
});
|
|
1655
|
+
}
|
|
1656
|
+
async createWorld(world) {
|
|
1657
|
+
return this.withDatabase(async () => {
|
|
1658
|
+
const newWorldId = world.id || v4();
|
|
1659
|
+
await this.db.insert(worldTable).values({
|
|
1660
|
+
...world,
|
|
1661
|
+
id: newWorldId
|
|
1662
|
+
});
|
|
1663
|
+
return newWorldId;
|
|
1664
|
+
});
|
|
1665
|
+
}
|
|
1666
|
+
async getWorld(id) {
|
|
1667
|
+
return this.withDatabase(async () => {
|
|
1668
|
+
const result = await this.db.select().from(worldTable).where(eq(worldTable.id, id));
|
|
1669
|
+
return result[0];
|
|
1670
|
+
});
|
|
1671
|
+
}
|
|
1672
|
+
async getAllWorlds() {
|
|
1673
|
+
return this.withDatabase(async () => {
|
|
1674
|
+
const result = await this.db.select().from(worldTable).where(eq(worldTable.agentId, this.agentId));
|
|
1675
|
+
return result;
|
|
1676
|
+
});
|
|
1677
|
+
}
|
|
1678
|
+
async updateWorld(world) {
|
|
1679
|
+
return this.withDatabase(async () => {
|
|
1680
|
+
await this.db.update(worldTable).set(world).where(eq(worldTable.id, world.id));
|
|
1681
|
+
});
|
|
1682
|
+
}
|
|
1683
|
+
async removeWorld(id) {
|
|
1684
|
+
return this.withDatabase(async () => {
|
|
1685
|
+
await this.db.delete(worldTable).where(eq(worldTable.id, id));
|
|
1686
|
+
});
|
|
1687
|
+
}
|
|
1688
|
+
/**
|
|
1689
|
+
* Creates a new task in the database.
|
|
1690
|
+
* @param task The task object to create
|
|
1691
|
+
* @returns Promise resolving to the UUID of the created task
|
|
1692
|
+
*/
|
|
1693
|
+
async createTask(task) {
|
|
1694
|
+
return this.withRetry(async () => {
|
|
1695
|
+
return this.withDatabase(async () => {
|
|
1696
|
+
const now = /* @__PURE__ */ new Date();
|
|
1697
|
+
const metadata = task.metadata || {};
|
|
1698
|
+
const values = {
|
|
1699
|
+
id: task.id,
|
|
1700
|
+
name: task.name,
|
|
1701
|
+
description: task.description,
|
|
1702
|
+
roomId: task.roomId,
|
|
1703
|
+
worldId: task.worldId,
|
|
1704
|
+
tags: task.tags,
|
|
1705
|
+
metadata,
|
|
1706
|
+
createdAt: now,
|
|
1707
|
+
updatedAt: now,
|
|
1708
|
+
agentId: this.agentId
|
|
1709
|
+
};
|
|
1710
|
+
const result = await this.db.insert(taskTable).values(values).returning({ id: taskTable.id });
|
|
1711
|
+
return result[0].id;
|
|
1712
|
+
});
|
|
1713
|
+
});
|
|
1714
|
+
}
|
|
1715
|
+
/**
|
|
1716
|
+
* Retrieves tasks based on specified parameters.
|
|
1717
|
+
* @param params Object containing optional roomId and tags to filter tasks
|
|
1718
|
+
* @returns Promise resolving to an array of Task objects
|
|
1719
|
+
*/
|
|
1720
|
+
async getTasks(params) {
|
|
1721
|
+
return this.withRetry(async () => {
|
|
1722
|
+
return this.withDatabase(async () => {
|
|
1723
|
+
let query = this.db.select().from(taskTable).where(eq(taskTable.agentId, this.agentId));
|
|
1724
|
+
if (params.roomId) {
|
|
1725
|
+
query = query.where(eq(taskTable.roomId, params.roomId));
|
|
1726
|
+
}
|
|
1727
|
+
if (params.tags && params.tags.length > 0) {
|
|
1728
|
+
const tagParams = params.tags.map((tag) => `'${tag.replace(/'/g, "''")}'`).join(", ");
|
|
1729
|
+
query = query.where(
|
|
1730
|
+
sql13`${taskTable.tags} @> ARRAY[${sql13.raw(tagParams)}]::text[]`
|
|
1731
|
+
);
|
|
1732
|
+
}
|
|
1733
|
+
const result = await query;
|
|
1734
|
+
return result.map((row) => ({
|
|
1735
|
+
id: row.id,
|
|
1736
|
+
name: row.name,
|
|
1737
|
+
description: row.description,
|
|
1738
|
+
roomId: row.roomId,
|
|
1739
|
+
worldId: row.worldId,
|
|
1740
|
+
tags: row.tags,
|
|
1741
|
+
metadata: row.metadata
|
|
1742
|
+
}));
|
|
1743
|
+
});
|
|
1744
|
+
});
|
|
1745
|
+
}
|
|
1746
|
+
/**
|
|
1747
|
+
* Retrieves a specific task by its name.
|
|
1748
|
+
* @param name The name of the task to retrieve
|
|
1749
|
+
* @returns Promise resolving to the Task object if found, null otherwise
|
|
1750
|
+
*/
|
|
1751
|
+
async getTasksByName(name) {
|
|
1752
|
+
return this.withRetry(async () => {
|
|
1753
|
+
return this.withDatabase(async () => {
|
|
1754
|
+
const result = await this.db.select().from(taskTable).where(
|
|
1755
|
+
and(eq(taskTable.name, name), eq(taskTable.agentId, this.agentId))
|
|
1756
|
+
);
|
|
1757
|
+
return result.map((row) => ({
|
|
1758
|
+
id: row.id,
|
|
1759
|
+
name: row.name,
|
|
1760
|
+
description: row.description,
|
|
1761
|
+
roomId: row.roomId,
|
|
1762
|
+
worldId: row.worldId,
|
|
1763
|
+
tags: row.tags || [],
|
|
1764
|
+
metadata: row.metadata || {}
|
|
1765
|
+
}));
|
|
1766
|
+
});
|
|
1767
|
+
});
|
|
1768
|
+
}
|
|
1769
|
+
/**
|
|
1770
|
+
* Retrieves a specific task by its ID.
|
|
1771
|
+
* @param id The UUID of the task to retrieve
|
|
1772
|
+
* @returns Promise resolving to the Task object if found, null otherwise
|
|
1773
|
+
*/
|
|
1774
|
+
async getTask(id) {
|
|
1775
|
+
return this.withRetry(async () => {
|
|
1776
|
+
return this.withDatabase(async () => {
|
|
1777
|
+
const result = await this.db.select().from(taskTable).where(and(eq(taskTable.id, id), eq(taskTable.agentId, this.agentId))).limit(1);
|
|
1778
|
+
if (result.length === 0) {
|
|
1779
|
+
return null;
|
|
1780
|
+
}
|
|
1781
|
+
const row = result[0];
|
|
1782
|
+
return {
|
|
1783
|
+
id: row.id,
|
|
1784
|
+
name: row.name,
|
|
1785
|
+
description: row.description,
|
|
1786
|
+
roomId: row.roomId,
|
|
1787
|
+
worldId: row.worldId,
|
|
1788
|
+
tags: row.tags || [],
|
|
1789
|
+
metadata: row.metadata || {}
|
|
1790
|
+
};
|
|
1791
|
+
});
|
|
1792
|
+
});
|
|
1793
|
+
}
|
|
1794
|
+
/**
|
|
1795
|
+
* Updates an existing task in the database.
|
|
1796
|
+
* @param id The UUID of the task to update
|
|
1797
|
+
* @param task Partial Task object containing the fields to update
|
|
1798
|
+
* @returns Promise resolving when the update is complete
|
|
1799
|
+
*/
|
|
1800
|
+
async updateTask(id, task) {
|
|
1801
|
+
await this.withRetry(async () => {
|
|
1802
|
+
await this.withDatabase(async () => {
|
|
1803
|
+
const updateValues = {};
|
|
1804
|
+
if (task.name !== void 0) updateValues.name = task.name;
|
|
1805
|
+
if (task.description !== void 0)
|
|
1806
|
+
updateValues.description = task.description;
|
|
1807
|
+
if (task.roomId !== void 0) updateValues.roomId = task.roomId;
|
|
1808
|
+
if (task.worldId !== void 0) updateValues.worldId = task.worldId;
|
|
1809
|
+
if (task.tags !== void 0) updateValues.tags = task.tags;
|
|
1810
|
+
task.updatedAt = Date.now();
|
|
1811
|
+
if (task.metadata) {
|
|
1812
|
+
const currentTask = await this.getTask(id);
|
|
1813
|
+
if (currentTask) {
|
|
1814
|
+
const currentMetadata = currentTask.metadata || {};
|
|
1815
|
+
const newMetadata = {
|
|
1816
|
+
...currentMetadata,
|
|
1817
|
+
...task.metadata
|
|
1818
|
+
};
|
|
1819
|
+
updateValues.metadata = newMetadata;
|
|
1820
|
+
} else {
|
|
1821
|
+
updateValues.metadata = {
|
|
1822
|
+
...task.metadata
|
|
1823
|
+
};
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
await this.db.update(taskTable).set(updateValues).where(
|
|
1827
|
+
and(eq(taskTable.id, id), eq(taskTable.agentId, this.agentId))
|
|
1828
|
+
);
|
|
1829
|
+
});
|
|
1830
|
+
});
|
|
1831
|
+
}
|
|
1832
|
+
/**
|
|
1833
|
+
* Deletes a task from the database.
|
|
1834
|
+
* @param id The UUID of the task to delete
|
|
1835
|
+
* @returns Promise resolving when the deletion is complete
|
|
1836
|
+
*/
|
|
1837
|
+
async deleteTask(id) {
|
|
1838
|
+
await this.withRetry(async () => {
|
|
1839
|
+
await this.withDatabase(async () => {
|
|
1840
|
+
await this.db.delete(taskTable).where(
|
|
1841
|
+
and(eq(taskTable.id, id), eq(taskTable.agentId, this.agentId))
|
|
1842
|
+
);
|
|
1843
|
+
});
|
|
1844
|
+
});
|
|
1845
|
+
}
|
|
1846
|
+
};
|
|
1847
|
+
|
|
1848
|
+
// src/pg-lite/adapter.ts
|
|
1849
|
+
var PgliteDatabaseAdapter = class extends BaseDrizzleAdapter {
|
|
1850
|
+
manager;
|
|
1851
|
+
embeddingDimension = DIMENSION_MAP[384];
|
|
1852
|
+
constructor(agentId, manager) {
|
|
1853
|
+
super(agentId);
|
|
1854
|
+
this.manager = manager;
|
|
1855
|
+
this.db = drizzle(this.manager.getConnection());
|
|
1856
|
+
}
|
|
1857
|
+
async withDatabase(operation) {
|
|
1858
|
+
if (this.manager.isShuttingDown()) {
|
|
1859
|
+
logger2.warn("Database is shutting down");
|
|
1860
|
+
return null;
|
|
1861
|
+
}
|
|
1862
|
+
return operation();
|
|
1863
|
+
}
|
|
1864
|
+
async init() {
|
|
1865
|
+
try {
|
|
1866
|
+
await this.manager.runMigrations();
|
|
1867
|
+
} catch (error) {
|
|
1868
|
+
logger2.error("Failed to initialize database:", error);
|
|
1869
|
+
throw error;
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
async close() {
|
|
1873
|
+
await this.manager.close();
|
|
1874
|
+
}
|
|
1875
|
+
};
|
|
1876
|
+
|
|
1877
|
+
// src/pg-lite/manager.ts
|
|
1878
|
+
import { PGlite } from "@electric-sql/pglite";
|
|
1879
|
+
import { vector as vector3 } from "@electric-sql/pglite/vector";
|
|
1880
|
+
import { fuzzystrmatch } from "@electric-sql/pglite/contrib/fuzzystrmatch";
|
|
1881
|
+
import { logger as logger3 } from "@elizaos/core";
|
|
1882
|
+
import { migrate } from "drizzle-orm/pglite/migrator";
|
|
1883
|
+
import { fileURLToPath } from "node:url";
|
|
1884
|
+
import path from "node:path";
|
|
1885
|
+
import { drizzle as drizzle2 } from "drizzle-orm/pglite";
|
|
1886
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
1887
|
+
var __dirname = path.dirname(__filename);
|
|
1888
|
+
var PGliteClientManager = class {
|
|
1889
|
+
client;
|
|
1890
|
+
shuttingDown = false;
|
|
1891
|
+
shutdownTimeout = 800;
|
|
1892
|
+
constructor(options) {
|
|
1893
|
+
this.client = new PGlite({
|
|
1894
|
+
...options,
|
|
1895
|
+
extensions: {
|
|
1896
|
+
vector: vector3,
|
|
1897
|
+
fuzzystrmatch
|
|
1898
|
+
}
|
|
1899
|
+
});
|
|
1900
|
+
this.setupShutdownHandlers();
|
|
1901
|
+
}
|
|
1902
|
+
getConnection() {
|
|
1903
|
+
if (this.shuttingDown) {
|
|
1904
|
+
throw new Error("Client manager is shutting down");
|
|
1905
|
+
}
|
|
1906
|
+
return this.client;
|
|
1907
|
+
}
|
|
1908
|
+
async gracefulShutdown() {
|
|
1909
|
+
if (this.shuttingDown) {
|
|
1910
|
+
return;
|
|
1911
|
+
}
|
|
1912
|
+
this.shuttingDown = true;
|
|
1913
|
+
logger3.info("Starting graceful shutdown of PGlite client...");
|
|
1914
|
+
const timeout = setTimeout(() => {
|
|
1915
|
+
logger3.warn(
|
|
1916
|
+
"Shutdown timeout reached, forcing database connection closure..."
|
|
1917
|
+
);
|
|
1918
|
+
this.client.close().finally(() => {
|
|
1919
|
+
process.exit(1);
|
|
1920
|
+
});
|
|
1921
|
+
}, this.shutdownTimeout);
|
|
1922
|
+
try {
|
|
1923
|
+
await new Promise((resolve) => setTimeout(resolve, this.shutdownTimeout));
|
|
1924
|
+
await this.client.close();
|
|
1925
|
+
clearTimeout(timeout);
|
|
1926
|
+
logger3.info("PGlite client shutdown completed successfully");
|
|
1927
|
+
process.exit(0);
|
|
1928
|
+
} catch (error) {
|
|
1929
|
+
logger3.error("Error during graceful shutdown:", error);
|
|
1930
|
+
process.exit(1);
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
setupShutdownHandlers() {
|
|
1934
|
+
process.on("SIGINT", async () => {
|
|
1935
|
+
await this.gracefulShutdown();
|
|
1936
|
+
});
|
|
1937
|
+
process.on("SIGTERM", async () => {
|
|
1938
|
+
await this.gracefulShutdown();
|
|
1939
|
+
});
|
|
1940
|
+
process.on("beforeExit", async () => {
|
|
1941
|
+
await this.gracefulShutdown();
|
|
1942
|
+
});
|
|
1943
|
+
}
|
|
1944
|
+
async initialize() {
|
|
1945
|
+
try {
|
|
1946
|
+
await this.client.waitReady;
|
|
1947
|
+
logger3.info("PGlite client initialized successfully");
|
|
1948
|
+
} catch (error) {
|
|
1949
|
+
logger3.error("Failed to initialize PGlite client:", error);
|
|
1950
|
+
throw error;
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
async close() {
|
|
1954
|
+
if (!this.shuttingDown) {
|
|
1955
|
+
await this.gracefulShutdown();
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
isShuttingDown() {
|
|
1959
|
+
return this.shuttingDown;
|
|
1960
|
+
}
|
|
1961
|
+
async runMigrations() {
|
|
1962
|
+
try {
|
|
1963
|
+
const db = drizzle2(this.client);
|
|
1964
|
+
await migrate(db, {
|
|
1965
|
+
migrationsFolder: path.resolve(__dirname, "../drizzle/migrations")
|
|
1966
|
+
});
|
|
1967
|
+
logger3.info("Migrations completed successfully!");
|
|
1968
|
+
} catch (error) {
|
|
1969
|
+
logger3.error("Failed to run database migrations:", error);
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
};
|
|
1973
|
+
|
|
1974
|
+
// src/pg/adapter.ts
|
|
1975
|
+
import { logger as logger4 } from "@elizaos/core";
|
|
1976
|
+
import { drizzle as drizzle3 } from "drizzle-orm/node-postgres";
|
|
1977
|
+
var PgDatabaseAdapter = class extends BaseDrizzleAdapter {
|
|
1978
|
+
constructor(agentId, manager) {
|
|
1979
|
+
super(agentId);
|
|
1980
|
+
this.manager = manager;
|
|
1981
|
+
this.manager = manager;
|
|
1982
|
+
}
|
|
1983
|
+
embeddingDimension = DIMENSION_MAP[384];
|
|
1984
|
+
async withDatabase(operation) {
|
|
1985
|
+
return await this.withRetry(async () => {
|
|
1986
|
+
const client = await this.manager.getClient();
|
|
1987
|
+
try {
|
|
1988
|
+
const db = drizzle3(client);
|
|
1989
|
+
this.db = db;
|
|
1990
|
+
return await operation();
|
|
1991
|
+
} finally {
|
|
1992
|
+
client.release();
|
|
1993
|
+
}
|
|
1994
|
+
});
|
|
1995
|
+
}
|
|
1996
|
+
async init() {
|
|
1997
|
+
try {
|
|
1998
|
+
await this.manager.runMigrations();
|
|
1999
|
+
logger4.info("PgDatabaseAdapter initialized successfully");
|
|
2000
|
+
} catch (error) {
|
|
2001
|
+
logger4.error("Failed to initialize PgDatabaseAdapter:", error);
|
|
2002
|
+
throw error;
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
async close() {
|
|
2006
|
+
await this.manager.close();
|
|
2007
|
+
}
|
|
2008
|
+
};
|
|
2009
|
+
|
|
2010
|
+
// src/pg/manager.ts
|
|
2011
|
+
import pkg from "pg";
|
|
2012
|
+
import { logger as logger5 } from "@elizaos/core";
|
|
2013
|
+
import { migrate as migrate2 } from "drizzle-orm/node-postgres/migrator";
|
|
2014
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
2015
|
+
import path2 from "node:path";
|
|
2016
|
+
import { drizzle as drizzle4 } from "drizzle-orm/node-postgres";
|
|
2017
|
+
var { Pool } = pkg;
|
|
2018
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
2019
|
+
var __dirname2 = path2.dirname(__filename2);
|
|
2020
|
+
var PostgresConnectionManager = class {
|
|
2021
|
+
pool;
|
|
2022
|
+
isShuttingDown = false;
|
|
2023
|
+
connectionTimeout = 5e3;
|
|
2024
|
+
constructor(connectionString) {
|
|
2025
|
+
const defaultConfig = {
|
|
2026
|
+
max: 20,
|
|
2027
|
+
idleTimeoutMillis: 3e4,
|
|
2028
|
+
connectionTimeoutMillis: this.connectionTimeout
|
|
2029
|
+
};
|
|
2030
|
+
this.pool = new Pool({
|
|
2031
|
+
...defaultConfig,
|
|
2032
|
+
connectionString
|
|
2033
|
+
});
|
|
2034
|
+
this.pool.on("error", (err) => {
|
|
2035
|
+
logger5.error("Unexpected pool error", err);
|
|
2036
|
+
this.handlePoolError(err);
|
|
2037
|
+
});
|
|
2038
|
+
this.setupPoolErrorHandling();
|
|
2039
|
+
this.testConnection();
|
|
2040
|
+
}
|
|
2041
|
+
async handlePoolError(error) {
|
|
2042
|
+
logger5.error("Pool error occurred, attempting to reconnect", {
|
|
2043
|
+
error: error.message
|
|
2044
|
+
});
|
|
2045
|
+
try {
|
|
2046
|
+
await this.pool.end();
|
|
2047
|
+
this.pool = new Pool({
|
|
2048
|
+
...this.pool.options,
|
|
2049
|
+
connectionTimeoutMillis: this.connectionTimeout
|
|
2050
|
+
});
|
|
2051
|
+
await this.testConnection();
|
|
2052
|
+
logger5.success("Pool reconnection successful");
|
|
2053
|
+
} catch (reconnectError) {
|
|
2054
|
+
logger5.error("Failed to reconnect pool", {
|
|
2055
|
+
error: reconnectError instanceof Error ? reconnectError.message : String(reconnectError)
|
|
2056
|
+
});
|
|
2057
|
+
throw reconnectError;
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
async testConnection() {
|
|
2061
|
+
let client;
|
|
2062
|
+
try {
|
|
2063
|
+
client = await this.pool.connect();
|
|
2064
|
+
const result = await client.query("SELECT NOW()");
|
|
2065
|
+
logger5.success("Database connection test successful:", result.rows[0]);
|
|
2066
|
+
return true;
|
|
2067
|
+
} catch (error) {
|
|
2068
|
+
logger5.error("Database connection test failed:", error);
|
|
2069
|
+
throw new Error(
|
|
2070
|
+
`Failed to connect to database: ${error.message}`
|
|
2071
|
+
);
|
|
2072
|
+
} finally {
|
|
2073
|
+
if (client) client.release();
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
setupPoolErrorHandling() {
|
|
2077
|
+
process.on("SIGINT", async () => {
|
|
2078
|
+
await this.cleanup();
|
|
2079
|
+
process.exit(0);
|
|
2080
|
+
});
|
|
2081
|
+
process.on("SIGTERM", async () => {
|
|
2082
|
+
await this.cleanup();
|
|
2083
|
+
process.exit(0);
|
|
2084
|
+
});
|
|
2085
|
+
process.on("beforeExit", async () => {
|
|
2086
|
+
await this.cleanup();
|
|
2087
|
+
});
|
|
2088
|
+
}
|
|
2089
|
+
getConnection() {
|
|
2090
|
+
if (this.isShuttingDown) {
|
|
2091
|
+
throw new Error("Connection manager is shutting down");
|
|
2092
|
+
}
|
|
2093
|
+
try {
|
|
2094
|
+
return this.pool;
|
|
2095
|
+
} catch (error) {
|
|
2096
|
+
logger5.error("Failed to get connection from pool:", error);
|
|
2097
|
+
throw error;
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
async getClient() {
|
|
2101
|
+
try {
|
|
2102
|
+
return await this.pool.connect();
|
|
2103
|
+
} catch (error) {
|
|
2104
|
+
logger5.error("Failed to acquire a database client:", error);
|
|
2105
|
+
throw error;
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
async initialize() {
|
|
2109
|
+
try {
|
|
2110
|
+
await this.testConnection();
|
|
2111
|
+
logger5.info("PostgreSQL connection manager initialized successfully");
|
|
2112
|
+
} catch (error) {
|
|
2113
|
+
logger5.error("Failed to initialize connection manager:", error);
|
|
2114
|
+
throw error;
|
|
2115
|
+
}
|
|
2116
|
+
}
|
|
2117
|
+
async close() {
|
|
2118
|
+
await this.cleanup();
|
|
2119
|
+
}
|
|
2120
|
+
async cleanup() {
|
|
2121
|
+
try {
|
|
2122
|
+
await this.pool.end();
|
|
2123
|
+
logger5.info("Database pool closed");
|
|
2124
|
+
} catch (error) {
|
|
2125
|
+
logger5.error("Error closing database pool:", error);
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
async runMigrations() {
|
|
2129
|
+
try {
|
|
2130
|
+
const db = drizzle4(this.pool);
|
|
2131
|
+
await migrate2(db, {
|
|
2132
|
+
migrationsFolder: path2.resolve(__dirname2, "../drizzle/migrations")
|
|
2133
|
+
});
|
|
2134
|
+
logger5.info("Migrations completed successfully!");
|
|
2135
|
+
} catch (error) {
|
|
2136
|
+
logger5.error("Failed to run database migrations:", error);
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
};
|
|
2140
|
+
|
|
2141
|
+
// src/index.ts
|
|
2142
|
+
var pgLiteClientManager;
|
|
2143
|
+
function createDatabaseAdapter(config, agentId) {
|
|
2144
|
+
if (config.postgresUrl) {
|
|
2145
|
+
const manager = new PostgresConnectionManager(config.postgresUrl);
|
|
2146
|
+
return new PgDatabaseAdapter(agentId, manager);
|
|
2147
|
+
}
|
|
2148
|
+
const dataDir = config.dataDir ?? "../../pgLite";
|
|
2149
|
+
if (!pgLiteClientManager) {
|
|
2150
|
+
pgLiteClientManager = new PGliteClientManager({ dataDir });
|
|
2151
|
+
}
|
|
2152
|
+
return new PgliteDatabaseAdapter(agentId, pgLiteClientManager);
|
|
2153
|
+
}
|
|
2154
|
+
var drizzlePlugin = {
|
|
2155
|
+
name: "drizzle",
|
|
2156
|
+
description: "Database adapter plugin using Drizzle ORM",
|
|
2157
|
+
init: async (_, runtime) => {
|
|
2158
|
+
const config = {
|
|
2159
|
+
dataDir: runtime.getSetting("PGLITE_DATA_DIR"),
|
|
2160
|
+
postgresUrl: runtime.getSetting("POSTGRES_URL")
|
|
2161
|
+
};
|
|
2162
|
+
try {
|
|
2163
|
+
const db = createDatabaseAdapter(config, runtime.agentId);
|
|
2164
|
+
logger6.success("Database connection established successfully");
|
|
2165
|
+
runtime.registerDatabaseAdapter(db);
|
|
2166
|
+
} catch (error) {
|
|
2167
|
+
logger6.error("Failed to initialize database:", error);
|
|
2168
|
+
throw error;
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
};
|
|
2172
|
+
var index_default = drizzlePlugin;
|
|
2173
|
+
export {
|
|
2174
|
+
createDatabaseAdapter,
|
|
2175
|
+
index_default as default
|
|
2176
|
+
};
|
|
2177
|
+
//# sourceMappingURL=index.js.map
|