@danielsimonjr/memoryjs 2.5.0 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1048 -1152
- package/dist/cli/index.js +446 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +574 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +167 -4
- package/dist/index.d.ts +167 -4
- package/dist/index.js +571 -1
- package/dist/index.js.map +1 -1
- package/package.json +9 -1
package/dist/index.cjs
CHANGED
|
@@ -3416,6 +3416,144 @@ var init_WorkerPoolManager = __esm({
|
|
|
3416
3416
|
}
|
|
3417
3417
|
});
|
|
3418
3418
|
|
|
3419
|
+
// src/utils/WorkerTaskManager.ts
|
|
3420
|
+
function getWorkerTaskManager() {
|
|
3421
|
+
if (!DEFAULT_INSTANCE) DEFAULT_INSTANCE = new WorkerTaskManager();
|
|
3422
|
+
return DEFAULT_INSTANCE;
|
|
3423
|
+
}
|
|
3424
|
+
async function batchProcessViaWorkers(items, workerType, methodName, mapArgs, opts = {}) {
|
|
3425
|
+
if (items.length === 0) return [];
|
|
3426
|
+
const wtm = getWorkerTaskManager();
|
|
3427
|
+
const promises = items.map(
|
|
3428
|
+
(item, i) => wtm.submit(workerType, methodName, mapArgs(item, i), opts)
|
|
3429
|
+
);
|
|
3430
|
+
return Promise.all(promises);
|
|
3431
|
+
}
|
|
3432
|
+
var SUBMISSION_COUNTER, WorkerTaskManager, DEFAULT_INSTANCE;
|
|
3433
|
+
var init_WorkerTaskManager = __esm({
|
|
3434
|
+
"src/utils/WorkerTaskManager.ts"() {
|
|
3435
|
+
"use strict";
|
|
3436
|
+
init_cjs_shims();
|
|
3437
|
+
init_taskScheduler();
|
|
3438
|
+
init_WorkerPoolManager();
|
|
3439
|
+
init_logger();
|
|
3440
|
+
SUBMISSION_COUNTER = 0;
|
|
3441
|
+
WorkerTaskManager = class {
|
|
3442
|
+
queue;
|
|
3443
|
+
poolManager;
|
|
3444
|
+
touchedPoolIds = /* @__PURE__ */ new Set();
|
|
3445
|
+
cancelledTaskIds = /* @__PURE__ */ new Set();
|
|
3446
|
+
handleStatus = /* @__PURE__ */ new Map();
|
|
3447
|
+
constructor(opts = {}) {
|
|
3448
|
+
this.queue = new TaskQueue({
|
|
3449
|
+
concurrency: opts.concurrency,
|
|
3450
|
+
timeout: opts.defaultTimeout,
|
|
3451
|
+
// We dispatch to WorkerPoolManager ourselves — the queue's
|
|
3452
|
+
// internal-worker-pool path would double-up.
|
|
3453
|
+
useWorkerPool: false
|
|
3454
|
+
});
|
|
3455
|
+
this.poolManager = getWorkerPoolManager();
|
|
3456
|
+
}
|
|
3457
|
+
/**
|
|
3458
|
+
* Submit a task and await its result. Throws on failure / cancellation.
|
|
3459
|
+
*
|
|
3460
|
+
* @param workerType — routing key. Maps to a named `WorkerPoolManager` pool.
|
|
3461
|
+
* @param methodName — function name exposed by the worker module.
|
|
3462
|
+
* @param args — positional arguments passed to the worker function.
|
|
3463
|
+
*/
|
|
3464
|
+
async submit(workerType, methodName, args, opts = {}) {
|
|
3465
|
+
const handle = this.submitWithHandle(workerType, methodName, args, opts);
|
|
3466
|
+
return handle.result;
|
|
3467
|
+
}
|
|
3468
|
+
/**
|
|
3469
|
+
* Submit a task and return a handle for cancellation + status polling.
|
|
3470
|
+
*/
|
|
3471
|
+
submitWithHandle(workerType, methodName, args, opts = {}) {
|
|
3472
|
+
const id = `wtm-${Date.now()}-${++SUBMISSION_COUNTER}`;
|
|
3473
|
+
this.handleStatus.set(id, "pending" /* PENDING */);
|
|
3474
|
+
const task = {
|
|
3475
|
+
id,
|
|
3476
|
+
priority: opts.priority ?? 1 /* NORMAL */,
|
|
3477
|
+
timeout: opts.timeout,
|
|
3478
|
+
input: { workerType, methodName, args },
|
|
3479
|
+
fn: async (input) => {
|
|
3480
|
+
if (this.cancelledTaskIds.has(id)) {
|
|
3481
|
+
throw new Error(`Task ${id} cancelled before dispatch`);
|
|
3482
|
+
}
|
|
3483
|
+
this.handleStatus.set(id, "running" /* RUNNING */);
|
|
3484
|
+
const pool = this.poolManager.getPool(input.workerType, opts.poolConfig);
|
|
3485
|
+
this.touchedPoolIds.add(input.workerType);
|
|
3486
|
+
return await pool.exec(input.methodName, input.args);
|
|
3487
|
+
}
|
|
3488
|
+
};
|
|
3489
|
+
const result = this.queue.enqueue(task).then(async (res) => {
|
|
3490
|
+
this.handleStatus.set(id, res.status);
|
|
3491
|
+
if (res.status === "completed" /* COMPLETED */) {
|
|
3492
|
+
return res.result;
|
|
3493
|
+
}
|
|
3494
|
+
if (res.status === "cancelled" /* CANCELLED */ || this.cancelledTaskIds.has(id)) {
|
|
3495
|
+
throw new Error(`Task ${id} cancelled`);
|
|
3496
|
+
}
|
|
3497
|
+
const err2 = res.error ?? new Error(`Task ${id} failed: ${res.status}`);
|
|
3498
|
+
throw err2;
|
|
3499
|
+
});
|
|
3500
|
+
return {
|
|
3501
|
+
id,
|
|
3502
|
+
result,
|
|
3503
|
+
cancel: () => {
|
|
3504
|
+
this.cancelledTaskIds.add(id);
|
|
3505
|
+
const evicted = this.queue.cancel(id);
|
|
3506
|
+
if (evicted) {
|
|
3507
|
+
this.handleStatus.set(id, "cancelled" /* CANCELLED */);
|
|
3508
|
+
}
|
|
3509
|
+
return evicted;
|
|
3510
|
+
},
|
|
3511
|
+
status: () => this.handleStatus.get(id) ?? "pending" /* PENDING */
|
|
3512
|
+
};
|
|
3513
|
+
}
|
|
3514
|
+
/**
|
|
3515
|
+
* Aggregated stats across the queue + touched pools.
|
|
3516
|
+
*/
|
|
3517
|
+
getStats() {
|
|
3518
|
+
const pools = [];
|
|
3519
|
+
for (const poolId of this.touchedPoolIds) {
|
|
3520
|
+
const stats = this.poolManager.getPoolStats(poolId);
|
|
3521
|
+
if (stats) {
|
|
3522
|
+
pools.push({
|
|
3523
|
+
poolId,
|
|
3524
|
+
workers: stats.totalWorkers,
|
|
3525
|
+
activeTasks: stats.activeTasks,
|
|
3526
|
+
pendingTasks: stats.pendingTasks,
|
|
3527
|
+
totalTasksExecuted: stats.totalTasksExecuted
|
|
3528
|
+
});
|
|
3529
|
+
}
|
|
3530
|
+
}
|
|
3531
|
+
return { queue: this.queue.getStats(), pools };
|
|
3532
|
+
}
|
|
3533
|
+
/**
|
|
3534
|
+
* Drain the queue (wait for all pending + running tasks) and return their
|
|
3535
|
+
* results in completion order. Pool workers are NOT terminated — call
|
|
3536
|
+
* `shutdown()` for that.
|
|
3537
|
+
*/
|
|
3538
|
+
async drain() {
|
|
3539
|
+
return this.queue.drain();
|
|
3540
|
+
}
|
|
3541
|
+
/**
|
|
3542
|
+
* Shut down the queue + the underlying WorkerPoolManager pools touched by
|
|
3543
|
+
* this instance. Idempotent. Returns after every owned worker has exited.
|
|
3544
|
+
*/
|
|
3545
|
+
async shutdown() {
|
|
3546
|
+
try {
|
|
3547
|
+
await this.queue.shutdown();
|
|
3548
|
+
} catch (e) {
|
|
3549
|
+
logger.warn("[WorkerTaskManager.shutdown] queue.shutdown failed:", e);
|
|
3550
|
+
}
|
|
3551
|
+
}
|
|
3552
|
+
};
|
|
3553
|
+
DEFAULT_INSTANCE = null;
|
|
3554
|
+
}
|
|
3555
|
+
});
|
|
3556
|
+
|
|
3419
3557
|
// src/utils/BatchProcessor.ts
|
|
3420
3558
|
async function processBatch(items, processor, options = {}) {
|
|
3421
3559
|
const batchProcessor = new BatchProcessor(options);
|
|
@@ -11270,6 +11408,7 @@ var init_utils = __esm({
|
|
|
11270
11408
|
init_taskScheduler();
|
|
11271
11409
|
init_operationUtils();
|
|
11272
11410
|
init_WorkerPoolManager();
|
|
11411
|
+
init_WorkerTaskManager();
|
|
11273
11412
|
init_BatchProcessor();
|
|
11274
11413
|
init_MemoryMonitor();
|
|
11275
11414
|
init_relationHelpers();
|
|
@@ -13775,6 +13914,7 @@ __export(index_exports, {
|
|
|
13775
13914
|
VisibilityResolver: () => VisibilityResolver,
|
|
13776
13915
|
WorkThreadManager: () => WorkThreadManager,
|
|
13777
13916
|
WorkerPoolManager: () => WorkerPoolManager,
|
|
13917
|
+
WorkerTaskManager: () => WorkerTaskManager,
|
|
13778
13918
|
WorkingMemoryManager: () => WorkingMemoryManager,
|
|
13779
13919
|
WorldModelManager: () => WorldModelManager,
|
|
13780
13920
|
WorldStateSnapshot: () => WorldStateSnapshot,
|
|
@@ -13784,6 +13924,7 @@ __export(index_exports, {
|
|
|
13784
13924
|
applyPagination: () => applyPagination,
|
|
13785
13925
|
asWarning: () => asWarning,
|
|
13786
13926
|
batchProcess: () => batchProcess,
|
|
13927
|
+
batchProcessViaWorkers: () => batchProcessViaWorkers,
|
|
13787
13928
|
buildTFVector: () => buildTFVector,
|
|
13788
13929
|
calculateIDF: () => calculateIDF,
|
|
13789
13930
|
calculateIDFFromTokenSets: () => calculateIDFFromTokenSets,
|
|
@@ -13854,6 +13995,7 @@ __export(index_exports, {
|
|
|
13854
13995
|
getQuickHint: () => getQuickHint,
|
|
13855
13996
|
getRoleProfile: () => getRoleProfile,
|
|
13856
13997
|
getWorkerPoolManager: () => getWorkerPoolManager,
|
|
13998
|
+
getWorkerTaskManager: () => getWorkerTaskManager,
|
|
13857
13999
|
globalMemoryMonitor: () => globalMemoryMonitor,
|
|
13858
14000
|
groupEntitiesByType: () => groupEntitiesByType,
|
|
13859
14001
|
hasAllTags: () => hasAllTags,
|
|
@@ -21032,6 +21174,431 @@ var ObservationStore = class _ObservationStore {
|
|
|
21032
21174
|
|
|
21033
21175
|
// src/core/StorageFactory.ts
|
|
21034
21176
|
init_cjs_shims();
|
|
21177
|
+
|
|
21178
|
+
// src/core/PostgreSQLStorage.ts
|
|
21179
|
+
init_cjs_shims();
|
|
21180
|
+
init_logger();
|
|
21181
|
+
var ENTITY_COLUMNS = [
|
|
21182
|
+
"name",
|
|
21183
|
+
"entity_type",
|
|
21184
|
+
"observations",
|
|
21185
|
+
"parent_id",
|
|
21186
|
+
"tags",
|
|
21187
|
+
"importance",
|
|
21188
|
+
"created_at",
|
|
21189
|
+
"last_modified",
|
|
21190
|
+
"ttl",
|
|
21191
|
+
"confidence",
|
|
21192
|
+
"project_id",
|
|
21193
|
+
"version",
|
|
21194
|
+
"parent_entity_name",
|
|
21195
|
+
"root_entity_name",
|
|
21196
|
+
"is_latest",
|
|
21197
|
+
"superseded_by",
|
|
21198
|
+
"content_hash",
|
|
21199
|
+
"valid_from",
|
|
21200
|
+
"valid_until",
|
|
21201
|
+
"observation_meta",
|
|
21202
|
+
"lifecycle_status",
|
|
21203
|
+
"extra"
|
|
21204
|
+
];
|
|
21205
|
+
var FIRST_CLASS_ENTITY_KEYS = /* @__PURE__ */ new Set([
|
|
21206
|
+
"name",
|
|
21207
|
+
"entityType",
|
|
21208
|
+
"observations",
|
|
21209
|
+
"parentId",
|
|
21210
|
+
"tags",
|
|
21211
|
+
"importance",
|
|
21212
|
+
"createdAt",
|
|
21213
|
+
"lastModified",
|
|
21214
|
+
"ttl",
|
|
21215
|
+
"confidence",
|
|
21216
|
+
"projectId",
|
|
21217
|
+
"version",
|
|
21218
|
+
"parentEntityName",
|
|
21219
|
+
"rootEntityName",
|
|
21220
|
+
"isLatest",
|
|
21221
|
+
"supersededBy",
|
|
21222
|
+
"contentHash",
|
|
21223
|
+
"validFrom",
|
|
21224
|
+
"validUntil",
|
|
21225
|
+
"observationMeta",
|
|
21226
|
+
"lifecycleStatus"
|
|
21227
|
+
]);
|
|
21228
|
+
var SCHEMA_DDL = `
|
|
21229
|
+
CREATE TABLE IF NOT EXISTS entities (
|
|
21230
|
+
name TEXT PRIMARY KEY,
|
|
21231
|
+
entity_type TEXT NOT NULL,
|
|
21232
|
+
observations TEXT[] NOT NULL DEFAULT '{}',
|
|
21233
|
+
parent_id TEXT,
|
|
21234
|
+
tags TEXT[],
|
|
21235
|
+
importance NUMERIC(4, 2),
|
|
21236
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
21237
|
+
last_modified TIMESTAMPTZ DEFAULT NOW(),
|
|
21238
|
+
ttl INTEGER,
|
|
21239
|
+
confidence NUMERIC(4, 3),
|
|
21240
|
+
project_id TEXT,
|
|
21241
|
+
version INTEGER,
|
|
21242
|
+
parent_entity_name TEXT,
|
|
21243
|
+
root_entity_name TEXT,
|
|
21244
|
+
is_latest BOOLEAN,
|
|
21245
|
+
superseded_by TEXT,
|
|
21246
|
+
content_hash TEXT,
|
|
21247
|
+
valid_from TIMESTAMPTZ,
|
|
21248
|
+
valid_until TIMESTAMPTZ,
|
|
21249
|
+
observation_meta JSONB,
|
|
21250
|
+
lifecycle_status TEXT,
|
|
21251
|
+
extra JSONB
|
|
21252
|
+
);
|
|
21253
|
+
|
|
21254
|
+
CREATE INDEX IF NOT EXISTS idx_entities_entity_type ON entities(entity_type);
|
|
21255
|
+
CREATE INDEX IF NOT EXISTS idx_entities_project_id ON entities(project_id);
|
|
21256
|
+
CREATE INDEX IF NOT EXISTS idx_entities_content_hash ON entities(content_hash);
|
|
21257
|
+
CREATE INDEX IF NOT EXISTS idx_entities_tags_gin ON entities USING GIN(tags);
|
|
21258
|
+
|
|
21259
|
+
CREATE TABLE IF NOT EXISTS relations (
|
|
21260
|
+
from_name TEXT NOT NULL,
|
|
21261
|
+
to_name TEXT NOT NULL,
|
|
21262
|
+
relation_type TEXT NOT NULL,
|
|
21263
|
+
metadata JSONB,
|
|
21264
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
21265
|
+
valid_from TIMESTAMPTZ,
|
|
21266
|
+
valid_until TIMESTAMPTZ,
|
|
21267
|
+
PRIMARY KEY (from_name, to_name, relation_type)
|
|
21268
|
+
);
|
|
21269
|
+
|
|
21270
|
+
CREATE INDEX IF NOT EXISTS idx_relations_to ON relations(to_name);
|
|
21271
|
+
CREATE INDEX IF NOT EXISTS idx_relations_type ON relations(relation_type);
|
|
21272
|
+
`;
|
|
21273
|
+
var PostgreSQLStorage = class {
|
|
21274
|
+
connectionString;
|
|
21275
|
+
pool = null;
|
|
21276
|
+
cache = null;
|
|
21277
|
+
nameIndex = /* @__PURE__ */ new Map();
|
|
21278
|
+
outgoingRelations = /* @__PURE__ */ new Map();
|
|
21279
|
+
incomingRelations = /* @__PURE__ */ new Map();
|
|
21280
|
+
pendingAppends = 0;
|
|
21281
|
+
schemaInitPromise = null;
|
|
21282
|
+
constructor(connectionString) {
|
|
21283
|
+
this.connectionString = connectionString;
|
|
21284
|
+
}
|
|
21285
|
+
// ==================== Connection management ====================
|
|
21286
|
+
/**
|
|
21287
|
+
* Get or lazily-construct the `pg.Pool`. Throws a friendly error if
|
|
21288
|
+
* the `pg` package isn't installed.
|
|
21289
|
+
*/
|
|
21290
|
+
async getPool() {
|
|
21291
|
+
if (this.pool) return this.pool;
|
|
21292
|
+
let pgModule;
|
|
21293
|
+
try {
|
|
21294
|
+
pgModule = await import("pg");
|
|
21295
|
+
} catch {
|
|
21296
|
+
throw new Error(
|
|
21297
|
+
"The 'pg' package is required for the PostgreSQL backend but is not installed. Run: npm install pg @types/pg"
|
|
21298
|
+
);
|
|
21299
|
+
}
|
|
21300
|
+
this.pool = new pgModule.Pool({ connectionString: this.connectionString });
|
|
21301
|
+
return this.pool;
|
|
21302
|
+
}
|
|
21303
|
+
/**
|
|
21304
|
+
* Run schema DDL idempotently. Called from `ensureLoaded`.
|
|
21305
|
+
*/
|
|
21306
|
+
async initSchema() {
|
|
21307
|
+
if (this.schemaInitPromise) return this.schemaInitPromise;
|
|
21308
|
+
this.schemaInitPromise = (async () => {
|
|
21309
|
+
const pool = await this.getPool();
|
|
21310
|
+
await pool.query(SCHEMA_DDL);
|
|
21311
|
+
})();
|
|
21312
|
+
return this.schemaInitPromise;
|
|
21313
|
+
}
|
|
21314
|
+
// ==================== Row ⇄ Entity / Relation mapping ====================
|
|
21315
|
+
entityToRow(entity) {
|
|
21316
|
+
const extra = {};
|
|
21317
|
+
for (const key of Object.keys(entity)) {
|
|
21318
|
+
if (!FIRST_CLASS_ENTITY_KEYS.has(key)) {
|
|
21319
|
+
extra[key] = entity[key];
|
|
21320
|
+
}
|
|
21321
|
+
}
|
|
21322
|
+
return {
|
|
21323
|
+
name: entity.name,
|
|
21324
|
+
entity_type: entity.entityType,
|
|
21325
|
+
observations: entity.observations,
|
|
21326
|
+
parent_id: entity.parentId ?? null,
|
|
21327
|
+
tags: entity.tags ?? null,
|
|
21328
|
+
importance: entity.importance ?? null,
|
|
21329
|
+
created_at: entity.createdAt ?? null,
|
|
21330
|
+
last_modified: entity.lastModified ?? null,
|
|
21331
|
+
ttl: entity.ttl ?? null,
|
|
21332
|
+
confidence: entity.confidence ?? null,
|
|
21333
|
+
project_id: entity.projectId ?? null,
|
|
21334
|
+
version: entity.version ?? null,
|
|
21335
|
+
parent_entity_name: entity.parentEntityName ?? null,
|
|
21336
|
+
root_entity_name: entity.rootEntityName ?? null,
|
|
21337
|
+
is_latest: entity.isLatest ?? null,
|
|
21338
|
+
superseded_by: entity.supersededBy ?? null,
|
|
21339
|
+
content_hash: entity.contentHash ?? null,
|
|
21340
|
+
valid_from: entity.validFrom ?? null,
|
|
21341
|
+
valid_until: entity.validUntil ?? null,
|
|
21342
|
+
observation_meta: entity.observationMeta ?? null,
|
|
21343
|
+
lifecycle_status: entity.lifecycleStatus ?? null,
|
|
21344
|
+
extra: Object.keys(extra).length > 0 ? extra : null
|
|
21345
|
+
};
|
|
21346
|
+
}
|
|
21347
|
+
rowToEntity(row) {
|
|
21348
|
+
const entity = {
|
|
21349
|
+
name: String(row.name),
|
|
21350
|
+
entityType: String(row.entity_type),
|
|
21351
|
+
observations: Array.isArray(row.observations) ? row.observations : []
|
|
21352
|
+
};
|
|
21353
|
+
if (row.parent_id != null) entity.parentId = String(row.parent_id);
|
|
21354
|
+
if (Array.isArray(row.tags)) entity.tags = row.tags;
|
|
21355
|
+
if (row.importance != null) entity.importance = Number(row.importance);
|
|
21356
|
+
if (row.created_at != null) entity.createdAt = new Date(String(row.created_at)).toISOString();
|
|
21357
|
+
if (row.last_modified != null) entity.lastModified = new Date(String(row.last_modified)).toISOString();
|
|
21358
|
+
if (row.ttl != null) entity.ttl = Number(row.ttl);
|
|
21359
|
+
if (row.confidence != null) entity.confidence = Number(row.confidence);
|
|
21360
|
+
if (row.project_id != null) entity.projectId = String(row.project_id);
|
|
21361
|
+
if (row.version != null) entity.version = Number(row.version);
|
|
21362
|
+
if (row.parent_entity_name != null) entity.parentEntityName = String(row.parent_entity_name);
|
|
21363
|
+
if (row.root_entity_name != null) entity.rootEntityName = String(row.root_entity_name);
|
|
21364
|
+
if (row.is_latest != null) entity.isLatest = Boolean(row.is_latest);
|
|
21365
|
+
if (row.superseded_by != null) entity.supersededBy = String(row.superseded_by);
|
|
21366
|
+
if (row.content_hash != null) entity.contentHash = String(row.content_hash);
|
|
21367
|
+
if (row.valid_from != null) entity.validFrom = new Date(String(row.valid_from)).toISOString();
|
|
21368
|
+
if (row.valid_until != null) entity.validUntil = new Date(String(row.valid_until)).toISOString();
|
|
21369
|
+
if (row.observation_meta != null) {
|
|
21370
|
+
entity.observationMeta = row.observation_meta;
|
|
21371
|
+
}
|
|
21372
|
+
if (row.lifecycle_status != null) {
|
|
21373
|
+
entity.lifecycleStatus = String(row.lifecycle_status);
|
|
21374
|
+
}
|
|
21375
|
+
if (row.extra != null && typeof row.extra === "object") {
|
|
21376
|
+
Object.assign(entity, row.extra);
|
|
21377
|
+
}
|
|
21378
|
+
return entity;
|
|
21379
|
+
}
|
|
21380
|
+
rowToRelation(row) {
|
|
21381
|
+
const relation = {
|
|
21382
|
+
from: String(row.from_name),
|
|
21383
|
+
to: String(row.to_name),
|
|
21384
|
+
relationType: String(row.relation_type)
|
|
21385
|
+
};
|
|
21386
|
+
return relation;
|
|
21387
|
+
}
|
|
21388
|
+
// ==================== Cache management ====================
|
|
21389
|
+
rebuildIndexes(graph) {
|
|
21390
|
+
this.nameIndex.clear();
|
|
21391
|
+
this.outgoingRelations.clear();
|
|
21392
|
+
this.incomingRelations.clear();
|
|
21393
|
+
for (const e of graph.entities) this.nameIndex.set(e.name, e);
|
|
21394
|
+
for (const r of graph.relations) {
|
|
21395
|
+
const outArr = this.outgoingRelations.get(r.from) ?? [];
|
|
21396
|
+
outArr.push(r);
|
|
21397
|
+
this.outgoingRelations.set(r.from, outArr);
|
|
21398
|
+
const inArr = this.incomingRelations.get(r.to) ?? [];
|
|
21399
|
+
inArr.push(r);
|
|
21400
|
+
this.incomingRelations.set(r.to, inArr);
|
|
21401
|
+
}
|
|
21402
|
+
}
|
|
21403
|
+
// ==================== IGraphStorage — read ====================
|
|
21404
|
+
async loadGraph() {
|
|
21405
|
+
await this.ensureLoaded();
|
|
21406
|
+
return this.cache;
|
|
21407
|
+
}
|
|
21408
|
+
async getGraphForMutation() {
|
|
21409
|
+
await this.ensureLoaded();
|
|
21410
|
+
const cache = this.cache;
|
|
21411
|
+
return {
|
|
21412
|
+
entities: cache.entities.map((e) => ({
|
|
21413
|
+
...e,
|
|
21414
|
+
observations: [...e.observations],
|
|
21415
|
+
tags: e.tags ? [...e.tags] : void 0
|
|
21416
|
+
})),
|
|
21417
|
+
relations: cache.relations.map((r) => ({ ...r }))
|
|
21418
|
+
};
|
|
21419
|
+
}
|
|
21420
|
+
async ensureLoaded() {
|
|
21421
|
+
if (this.cache) return;
|
|
21422
|
+
await this.initSchema();
|
|
21423
|
+
const pool = await this.getPool();
|
|
21424
|
+
const [eRes, rRes] = await Promise.all([
|
|
21425
|
+
pool.query("SELECT * FROM entities"),
|
|
21426
|
+
pool.query("SELECT * FROM relations")
|
|
21427
|
+
]);
|
|
21428
|
+
const graph = {
|
|
21429
|
+
entities: eRes.rows.map((r) => this.rowToEntity(r)),
|
|
21430
|
+
relations: rRes.rows.map((r) => this.rowToRelation(r))
|
|
21431
|
+
};
|
|
21432
|
+
this.cache = graph;
|
|
21433
|
+
this.rebuildIndexes(graph);
|
|
21434
|
+
}
|
|
21435
|
+
get cachedGraph() {
|
|
21436
|
+
return this.cache;
|
|
21437
|
+
}
|
|
21438
|
+
// ==================== IGraphStorage — write ====================
|
|
21439
|
+
async saveGraph(graph) {
|
|
21440
|
+
await this.initSchema();
|
|
21441
|
+
const pool = await this.getPool();
|
|
21442
|
+
await pool.query("TRUNCATE entities, relations");
|
|
21443
|
+
for (const entity of graph.entities) {
|
|
21444
|
+
await this.insertEntityRow(pool, entity);
|
|
21445
|
+
}
|
|
21446
|
+
for (const relation of graph.relations) {
|
|
21447
|
+
await this.insertRelationRow(pool, relation);
|
|
21448
|
+
}
|
|
21449
|
+
this.cache = {
|
|
21450
|
+
entities: graph.entities.map((e) => ({ ...e })),
|
|
21451
|
+
relations: graph.relations.map((r) => ({ ...r }))
|
|
21452
|
+
};
|
|
21453
|
+
this.rebuildIndexes(this.cache);
|
|
21454
|
+
this.pendingAppends = 0;
|
|
21455
|
+
}
|
|
21456
|
+
async insertEntityRow(pool, entity) {
|
|
21457
|
+
const row = this.entityToRow(entity);
|
|
21458
|
+
const cols = ENTITY_COLUMNS.join(", ");
|
|
21459
|
+
const placeholders = ENTITY_COLUMNS.map((_, i) => `$${i + 1}`).join(", ");
|
|
21460
|
+
const values = ENTITY_COLUMNS.map((c) => row[c]);
|
|
21461
|
+
await pool.query(
|
|
21462
|
+
`INSERT INTO entities (${cols}) VALUES (${placeholders})
|
|
21463
|
+
ON CONFLICT (name) DO UPDATE SET ${ENTITY_COLUMNS.filter((c) => c !== "name").map((c) => `${c} = EXCLUDED.${c}`).join(", ")}`,
|
|
21464
|
+
values
|
|
21465
|
+
);
|
|
21466
|
+
}
|
|
21467
|
+
async insertRelationRow(pool, relation) {
|
|
21468
|
+
await pool.query(
|
|
21469
|
+
`INSERT INTO relations (from_name, to_name, relation_type)
|
|
21470
|
+
VALUES ($1, $2, $3) ON CONFLICT DO NOTHING`,
|
|
21471
|
+
[relation.from, relation.to, relation.relationType]
|
|
21472
|
+
);
|
|
21473
|
+
}
|
|
21474
|
+
async appendEntity(entity) {
|
|
21475
|
+
await this.ensureLoaded();
|
|
21476
|
+
const pool = await this.getPool();
|
|
21477
|
+
await this.insertEntityRow(pool, entity);
|
|
21478
|
+
if (!this.cache) return;
|
|
21479
|
+
const existingIdx = this.cache.entities.findIndex((e) => e.name === entity.name);
|
|
21480
|
+
if (existingIdx >= 0) this.cache.entities[existingIdx] = entity;
|
|
21481
|
+
else this.cache.entities.push(entity);
|
|
21482
|
+
this.nameIndex.set(entity.name, entity);
|
|
21483
|
+
this.pendingAppends += 1;
|
|
21484
|
+
}
|
|
21485
|
+
async appendRelation(relation) {
|
|
21486
|
+
await this.ensureLoaded();
|
|
21487
|
+
const pool = await this.getPool();
|
|
21488
|
+
await this.insertRelationRow(pool, relation);
|
|
21489
|
+
if (!this.cache) return;
|
|
21490
|
+
const duplicate = this.cache.relations.some(
|
|
21491
|
+
(r) => r.from === relation.from && r.to === relation.to && r.relationType === relation.relationType
|
|
21492
|
+
);
|
|
21493
|
+
if (!duplicate) {
|
|
21494
|
+
this.cache.relations.push(relation);
|
|
21495
|
+
const outArr = this.outgoingRelations.get(relation.from) ?? [];
|
|
21496
|
+
outArr.push(relation);
|
|
21497
|
+
this.outgoingRelations.set(relation.from, outArr);
|
|
21498
|
+
const inArr = this.incomingRelations.get(relation.to) ?? [];
|
|
21499
|
+
inArr.push(relation);
|
|
21500
|
+
this.incomingRelations.set(relation.to, inArr);
|
|
21501
|
+
}
|
|
21502
|
+
this.pendingAppends += 1;
|
|
21503
|
+
}
|
|
21504
|
+
async updateEntity(entityName, updates) {
|
|
21505
|
+
await this.ensureLoaded();
|
|
21506
|
+
const existing = this.nameIndex.get(entityName);
|
|
21507
|
+
if (!existing) return false;
|
|
21508
|
+
const merged = {
|
|
21509
|
+
...existing,
|
|
21510
|
+
...updates,
|
|
21511
|
+
name: entityName,
|
|
21512
|
+
lastModified: updates.lastModified ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
21513
|
+
};
|
|
21514
|
+
const pool = await this.getPool();
|
|
21515
|
+
await this.insertEntityRow(pool, merged);
|
|
21516
|
+
if (this.cache) {
|
|
21517
|
+
const idx = this.cache.entities.findIndex((e) => e.name === entityName);
|
|
21518
|
+
if (idx >= 0) this.cache.entities[idx] = merged;
|
|
21519
|
+
}
|
|
21520
|
+
this.nameIndex.set(entityName, merged);
|
|
21521
|
+
this.pendingAppends += 1;
|
|
21522
|
+
return true;
|
|
21523
|
+
}
|
|
21524
|
+
async compact() {
|
|
21525
|
+
if (this.cache) await this.saveGraph(this.cache);
|
|
21526
|
+
}
|
|
21527
|
+
clearCache() {
|
|
21528
|
+
this.cache = null;
|
|
21529
|
+
this.nameIndex.clear();
|
|
21530
|
+
this.outgoingRelations.clear();
|
|
21531
|
+
this.incomingRelations.clear();
|
|
21532
|
+
this.pendingAppends = 0;
|
|
21533
|
+
}
|
|
21534
|
+
// ==================== IGraphStorage — sync getters ====================
|
|
21535
|
+
getEntityByName(name) {
|
|
21536
|
+
return this.nameIndex.get(name);
|
|
21537
|
+
}
|
|
21538
|
+
hasEntity(name) {
|
|
21539
|
+
return this.nameIndex.has(name);
|
|
21540
|
+
}
|
|
21541
|
+
getEntitiesByType(entityType) {
|
|
21542
|
+
const result = [];
|
|
21543
|
+
for (const entity of this.nameIndex.values()) {
|
|
21544
|
+
if (entity.entityType === entityType) result.push(entity);
|
|
21545
|
+
}
|
|
21546
|
+
return result;
|
|
21547
|
+
}
|
|
21548
|
+
getEntityTypes() {
|
|
21549
|
+
const types = /* @__PURE__ */ new Set();
|
|
21550
|
+
for (const entity of this.nameIndex.values()) types.add(entity.entityType);
|
|
21551
|
+
return Array.from(types);
|
|
21552
|
+
}
|
|
21553
|
+
getLowercased(entityName) {
|
|
21554
|
+
const e = this.nameIndex.get(entityName);
|
|
21555
|
+
if (!e) return void 0;
|
|
21556
|
+
return {
|
|
21557
|
+
name: e.name.toLowerCase(),
|
|
21558
|
+
entityType: e.entityType.toLowerCase(),
|
|
21559
|
+
observations: e.observations.map((o) => o.toLowerCase()),
|
|
21560
|
+
tags: e.tags?.map((t) => t.toLowerCase()) ?? []
|
|
21561
|
+
};
|
|
21562
|
+
}
|
|
21563
|
+
getRelationsFrom(entityName) {
|
|
21564
|
+
return this.outgoingRelations.get(entityName) ?? [];
|
|
21565
|
+
}
|
|
21566
|
+
getRelationsTo(entityName) {
|
|
21567
|
+
return this.incomingRelations.get(entityName) ?? [];
|
|
21568
|
+
}
|
|
21569
|
+
getRelationsFor(entityName) {
|
|
21570
|
+
return [
|
|
21571
|
+
...this.outgoingRelations.get(entityName) ?? [],
|
|
21572
|
+
...this.incomingRelations.get(entityName) ?? []
|
|
21573
|
+
];
|
|
21574
|
+
}
|
|
21575
|
+
hasRelations(entityName) {
|
|
21576
|
+
return (this.outgoingRelations.get(entityName)?.length ?? 0) + (this.incomingRelations.get(entityName)?.length ?? 0) > 0;
|
|
21577
|
+
}
|
|
21578
|
+
// ==================== Utility ====================
|
|
21579
|
+
getFilePath() {
|
|
21580
|
+
return this.connectionString;
|
|
21581
|
+
}
|
|
21582
|
+
getPendingAppends() {
|
|
21583
|
+
return this.pendingAppends;
|
|
21584
|
+
}
|
|
21585
|
+
/**
|
|
21586
|
+
* Close the underlying `pg.Pool`. Call from process-shutdown handlers to
|
|
21587
|
+
* avoid keeping the event loop alive on idle connections.
|
|
21588
|
+
*/
|
|
21589
|
+
async close() {
|
|
21590
|
+
if (this.pool) {
|
|
21591
|
+
try {
|
|
21592
|
+
await this.pool.end();
|
|
21593
|
+
} catch (e) {
|
|
21594
|
+
logger.warn("PostgreSQLStorage.close: pool.end failed:", e);
|
|
21595
|
+
}
|
|
21596
|
+
this.pool = null;
|
|
21597
|
+
}
|
|
21598
|
+
}
|
|
21599
|
+
};
|
|
21600
|
+
|
|
21601
|
+
// src/core/StorageFactory.ts
|
|
21035
21602
|
var DEFAULT_STORAGE_TYPE = "jsonl";
|
|
21036
21603
|
function createStorage(config) {
|
|
21037
21604
|
const storageType = process.env.MEMORY_STORAGE_TYPE || config.type || DEFAULT_STORAGE_TYPE;
|
|
@@ -21040,9 +21607,12 @@ function createStorage(config) {
|
|
|
21040
21607
|
return new GraphStorage(config.path);
|
|
21041
21608
|
case "sqlite":
|
|
21042
21609
|
return new SQLiteStorage(config.path);
|
|
21610
|
+
case "postgres":
|
|
21611
|
+
case "postgresql":
|
|
21612
|
+
return new PostgreSQLStorage(config.path);
|
|
21043
21613
|
default:
|
|
21044
21614
|
throw new Error(
|
|
21045
|
-
`Unknown storage type: ${storageType}. Supported types: jsonl, sqlite`
|
|
21615
|
+
`Unknown storage type: ${storageType}. Supported types: jsonl, sqlite, postgres`
|
|
21046
21616
|
);
|
|
21047
21617
|
}
|
|
21048
21618
|
}
|
|
@@ -51743,6 +52313,7 @@ function extractRoleAndContent(entity) {
|
|
|
51743
52313
|
VisibilityResolver,
|
|
51744
52314
|
WorkThreadManager,
|
|
51745
52315
|
WorkerPoolManager,
|
|
52316
|
+
WorkerTaskManager,
|
|
51746
52317
|
WorkingMemoryManager,
|
|
51747
52318
|
WorldModelManager,
|
|
51748
52319
|
WorldStateSnapshot,
|
|
@@ -51752,6 +52323,7 @@ function extractRoleAndContent(entity) {
|
|
|
51752
52323
|
applyPagination,
|
|
51753
52324
|
asWarning,
|
|
51754
52325
|
batchProcess,
|
|
52326
|
+
batchProcessViaWorkers,
|
|
51755
52327
|
buildTFVector,
|
|
51756
52328
|
calculateIDF,
|
|
51757
52329
|
calculateIDFFromTokenSets,
|
|
@@ -51822,6 +52394,7 @@ function extractRoleAndContent(entity) {
|
|
|
51822
52394
|
getQuickHint,
|
|
51823
52395
|
getRoleProfile,
|
|
51824
52396
|
getWorkerPoolManager,
|
|
52397
|
+
getWorkerTaskManager,
|
|
51825
52398
|
globalMemoryMonitor,
|
|
51826
52399
|
groupEntitiesByType,
|
|
51827
52400
|
hasAllTags,
|