opcode-pg-memory 2.2.5 → 2.2.6
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 +1 -0
- package/dist/index.js +185 -174
- package/dist/mcp-server.js +107 -61
- package/dist/src/db/init-db.d.ts +1 -0
- package/dist/src/db/init-db.d.ts.map +1 -1
- package/dist/src/hooks/message-part-updated.d.ts.map +1 -1
- package/dist/src/hooks/message-updated.d.ts.map +1 -1
- package/dist/src/hooks/session-compacting.d.ts.map +1 -1
- package/dist/src/hooks/session-completed.d.ts.map +1 -1
- package/dist/src/hooks/session-created.d.ts.map +1 -1
- package/dist/src/hooks/tool-execute.d.ts.map +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/mcp/hindsight-reflect-omo.d.ts.map +1 -1
- package/dist/src/mcp/hindsight-reflect.d.ts.map +1 -1
- package/dist/src/mcp/recall-memory-omo.d.ts.map +1 -1
- package/dist/src/mcp/recall-memory.d.ts.map +1 -1
- package/package.json +7 -5
package/dist/index.js
CHANGED
|
@@ -11194,6 +11194,48 @@ var defaults = import_lib.default.defaults;
|
|
|
11194
11194
|
|
|
11195
11195
|
// src/db/init-db.ts
|
|
11196
11196
|
var import_pgvector = __toESM(require_src(), 1);
|
|
11197
|
+
|
|
11198
|
+
// src/services/logger.ts
|
|
11199
|
+
var LEVEL_MAP = {
|
|
11200
|
+
debug: 0,
|
|
11201
|
+
info: 1,
|
|
11202
|
+
warn: 2,
|
|
11203
|
+
error: 3
|
|
11204
|
+
};
|
|
11205
|
+
var LEVEL_NAMES = {
|
|
11206
|
+
debug: "DEBUG",
|
|
11207
|
+
info: "INFO",
|
|
11208
|
+
warn: "WARN",
|
|
11209
|
+
error: "ERROR"
|
|
11210
|
+
};
|
|
11211
|
+
function getConfiguredLevel() {
|
|
11212
|
+
const env = process.env.PG_MEMORY_LOG_LEVEL ?? "info";
|
|
11213
|
+
const level = env.toLowerCase();
|
|
11214
|
+
if (level in LEVEL_MAP) {
|
|
11215
|
+
return LEVEL_MAP[level];
|
|
11216
|
+
}
|
|
11217
|
+
return LEVEL_MAP.info;
|
|
11218
|
+
}
|
|
11219
|
+
var currentLevel = getConfiguredLevel();
|
|
11220
|
+
function log(level, module, msg, data) {
|
|
11221
|
+
if (currentLevel > LEVEL_MAP[level]) {
|
|
11222
|
+
return;
|
|
11223
|
+
}
|
|
11224
|
+
const line = data !== undefined ? `[PG Memory] [${LEVEL_NAMES[level]}] [${module}] ${msg} ${JSON.stringify(data)}
|
|
11225
|
+
` : `[PG Memory] [${LEVEL_NAMES[level]}] [${module}] ${msg}
|
|
11226
|
+
`;
|
|
11227
|
+
process.stderr.write(line);
|
|
11228
|
+
}
|
|
11229
|
+
function createLogger(module) {
|
|
11230
|
+
return {
|
|
11231
|
+
debug: (msg, data) => log("debug", module, msg, data),
|
|
11232
|
+
info: (msg, data) => log("info", module, msg, data),
|
|
11233
|
+
warn: (msg, data) => log("warn", module, msg, data),
|
|
11234
|
+
error: (msg, data) => log("error", module, msg, data)
|
|
11235
|
+
};
|
|
11236
|
+
}
|
|
11237
|
+
|
|
11238
|
+
// src/db/init-db.ts
|
|
11197
11239
|
var DEFAULT_DB_CONFIG = {
|
|
11198
11240
|
host: process.env.PG_HOST || "localhost",
|
|
11199
11241
|
port: parseInt(process.env.PG_PORT || "5432", 10),
|
|
@@ -11207,6 +11249,7 @@ var DEFAULT_DB_CONFIG = {
|
|
|
11207
11249
|
class DatabaseInitializer {
|
|
11208
11250
|
pool = null;
|
|
11209
11251
|
config;
|
|
11252
|
+
logger = createLogger("init-db");
|
|
11210
11253
|
constructor(config = {}) {
|
|
11211
11254
|
this.config = { ...DEFAULT_DB_CONFIG, ...config };
|
|
11212
11255
|
}
|
|
@@ -11233,9 +11276,9 @@ class DatabaseInitializer {
|
|
|
11233
11276
|
const client = await this.pool.connect();
|
|
11234
11277
|
const result = await client.query("SELECT NOW() as now");
|
|
11235
11278
|
client.release();
|
|
11236
|
-
|
|
11279
|
+
this.logger.info("Database connected:", result.rows[0].now);
|
|
11237
11280
|
} catch (error) {
|
|
11238
|
-
|
|
11281
|
+
this.logger.error("Database connection failed:", error);
|
|
11239
11282
|
throw new Error(`Failed to connect to PostgreSQL: ${error}`);
|
|
11240
11283
|
}
|
|
11241
11284
|
}
|
|
@@ -11251,10 +11294,10 @@ class DatabaseInitializer {
|
|
|
11251
11294
|
await this.migrateSessionsData(client);
|
|
11252
11295
|
await this.initializeOmOSchema(client);
|
|
11253
11296
|
await client.query("COMMIT");
|
|
11254
|
-
|
|
11297
|
+
this.logger.info("Database schema initialized successfully");
|
|
11255
11298
|
} catch (error) {
|
|
11256
11299
|
await client.query("ROLLBACK");
|
|
11257
|
-
|
|
11300
|
+
this.logger.error("Database setup failed:", error);
|
|
11258
11301
|
throw error;
|
|
11259
11302
|
} finally {
|
|
11260
11303
|
client.release();
|
|
@@ -11265,7 +11308,7 @@ class DatabaseInitializer {
|
|
|
11265
11308
|
CREATE EXTENSION IF NOT EXISTS vector;
|
|
11266
11309
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
|
11267
11310
|
`);
|
|
11268
|
-
|
|
11311
|
+
this.logger.info("Extensions created");
|
|
11269
11312
|
}
|
|
11270
11313
|
async createEnums(client) {
|
|
11271
11314
|
await client.query(`
|
|
@@ -11280,7 +11323,7 @@ class DatabaseInitializer {
|
|
|
11280
11323
|
END IF;
|
|
11281
11324
|
END $$;
|
|
11282
11325
|
`);
|
|
11283
|
-
|
|
11326
|
+
this.logger.info("Enums created");
|
|
11284
11327
|
}
|
|
11285
11328
|
async createTables(client) {
|
|
11286
11329
|
await client.query(`
|
|
@@ -11419,7 +11462,7 @@ class DatabaseInitializer {
|
|
|
11419
11462
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
11420
11463
|
);
|
|
11421
11464
|
`);
|
|
11422
|
-
|
|
11465
|
+
this.logger.info("Tables created");
|
|
11423
11466
|
}
|
|
11424
11467
|
async createIndexes(client) {
|
|
11425
11468
|
await client.query(`
|
|
@@ -11513,7 +11556,7 @@ class DatabaseInitializer {
|
|
|
11513
11556
|
DROP INDEX IF EXISTS idx_reflection_errors_session;
|
|
11514
11557
|
DROP INDEX IF EXISTS idx_token_usage_session;
|
|
11515
11558
|
`);
|
|
11516
|
-
|
|
11559
|
+
this.logger.info("Indexes created");
|
|
11517
11560
|
}
|
|
11518
11561
|
async migrateLegacyColumnNames(client) {
|
|
11519
11562
|
const tables = [
|
|
@@ -11539,13 +11582,13 @@ class DatabaseInitializer {
|
|
|
11539
11582
|
await client.query(`
|
|
11540
11583
|
ALTER TABLE "${table}" RENAME COLUMN session_id TO session_map_id
|
|
11541
11584
|
`);
|
|
11542
|
-
|
|
11585
|
+
this.logger.info(`Renamed ${table}.session_id → session_map_id`);
|
|
11543
11586
|
}
|
|
11544
11587
|
} catch (err) {
|
|
11545
|
-
|
|
11588
|
+
this.logger.info(`Skipped column rename for ${table}: ${err}`);
|
|
11546
11589
|
}
|
|
11547
11590
|
}
|
|
11548
|
-
|
|
11591
|
+
this.logger.info("Legacy column migration complete");
|
|
11549
11592
|
}
|
|
11550
11593
|
async migrateSessionsData(client) {
|
|
11551
11594
|
try {
|
|
@@ -11554,7 +11597,7 @@ class DatabaseInitializer {
|
|
|
11554
11597
|
WHERE table_schema = 'public' AND table_name = 'sessions'
|
|
11555
11598
|
`);
|
|
11556
11599
|
if (exists.rows.length === 0) {
|
|
11557
|
-
|
|
11600
|
+
this.logger.info("No legacy sessions table found, skipping migration");
|
|
11558
11601
|
return;
|
|
11559
11602
|
}
|
|
11560
11603
|
await client.query(`
|
|
@@ -11566,18 +11609,18 @@ class DatabaseInitializer {
|
|
|
11566
11609
|
FROM sessions
|
|
11567
11610
|
ON CONFLICT (opencode_session_id) DO NOTHING;
|
|
11568
11611
|
`);
|
|
11569
|
-
|
|
11612
|
+
this.logger.info("Legacy sessions data migrated to session_map");
|
|
11570
11613
|
} catch (error) {
|
|
11571
|
-
|
|
11614
|
+
this.logger.warn("Sessions migration warning (non-fatal):", error);
|
|
11572
11615
|
}
|
|
11573
11616
|
}
|
|
11574
11617
|
async initializeOmOSchema(client) {
|
|
11575
11618
|
const omOEnabled = process.env.OMO_ENABLED === "true" || process.env.OMO_INTEGRATION === "enabled";
|
|
11576
11619
|
if (!omOEnabled) {
|
|
11577
|
-
|
|
11620
|
+
this.logger.info("OmO integration not enabled, skipping OmO schema");
|
|
11578
11621
|
return;
|
|
11579
11622
|
}
|
|
11580
|
-
|
|
11623
|
+
this.logger.info("Initializing OmO schema...");
|
|
11581
11624
|
const tablesWithAgent = ["observations", "semantic_cache", "entities", "reflections"];
|
|
11582
11625
|
for (const table of tablesWithAgent) {
|
|
11583
11626
|
try {
|
|
@@ -11593,7 +11636,7 @@ class DatabaseInitializer {
|
|
|
11593
11636
|
CREATE INDEX IF NOT EXISTS idx_${table}_agent_task ON ${table}(agent_task_id)
|
|
11594
11637
|
`);
|
|
11595
11638
|
} catch (error) {
|
|
11596
|
-
|
|
11639
|
+
this.logger.warn(`Schema update warning for ${table}:`, error);
|
|
11597
11640
|
}
|
|
11598
11641
|
}
|
|
11599
11642
|
await client.query(`
|
|
@@ -11631,13 +11674,13 @@ class DatabaseInitializer {
|
|
|
11631
11674
|
await client.query(`
|
|
11632
11675
|
CREATE INDEX IF NOT EXISTS idx_omo_wisdom_type ON omo_wisdom(type)
|
|
11633
11676
|
`);
|
|
11634
|
-
|
|
11677
|
+
this.logger.info("OmO schema initialized");
|
|
11635
11678
|
}
|
|
11636
11679
|
async close() {
|
|
11637
11680
|
if (this.pool) {
|
|
11638
11681
|
await this.pool.end();
|
|
11639
11682
|
this.pool = null;
|
|
11640
|
-
|
|
11683
|
+
this.logger.info("Database connection closed");
|
|
11641
11684
|
}
|
|
11642
11685
|
}
|
|
11643
11686
|
getPool() {
|
|
@@ -11769,6 +11812,7 @@ function checkBudgetOverflow(facts, budget) {
|
|
|
11769
11812
|
}
|
|
11770
11813
|
|
|
11771
11814
|
// src/hooks/session-created.ts
|
|
11815
|
+
var logger = createLogger("session-created");
|
|
11772
11816
|
var DEFAULT_CONFIG = {
|
|
11773
11817
|
contextLimitRatio: 0.05,
|
|
11774
11818
|
minTokens: 500,
|
|
@@ -11779,7 +11823,7 @@ var DEFAULT_CONFIG = {
|
|
|
11779
11823
|
async function handleSessionCreated(input, output, pool, config = {}) {
|
|
11780
11824
|
const mergedConfig = { ...DEFAULT_CONFIG, ...config };
|
|
11781
11825
|
const { session } = input;
|
|
11782
|
-
|
|
11826
|
+
logger.info(`Session created: ${session.id}`);
|
|
11783
11827
|
try {
|
|
11784
11828
|
await upsertSession(session, pool);
|
|
11785
11829
|
const budget = calculateTokenBudget(session.model.contextLimit, {
|
|
@@ -11787,13 +11831,13 @@ async function handleSessionCreated(input, output, pool, config = {}) {
|
|
|
11787
11831
|
minTokens: mergedConfig.minTokens,
|
|
11788
11832
|
maxTokens: mergedConfig.maxTokens
|
|
11789
11833
|
});
|
|
11790
|
-
|
|
11834
|
+
logger.info(`Token budget for injection: ${budget}`);
|
|
11791
11835
|
const facts = await retrieveFactsForInjection(session.id, budget, pool, mergedConfig);
|
|
11792
|
-
|
|
11836
|
+
logger.info(`Retrieved ${facts.length} facts for injection`);
|
|
11793
11837
|
const memories = facts.map((f) => f.content);
|
|
11794
11838
|
output.context = { memories };
|
|
11795
11839
|
} catch (error) {
|
|
11796
|
-
|
|
11840
|
+
logger.error("Error handling session.created:", error);
|
|
11797
11841
|
}
|
|
11798
11842
|
}
|
|
11799
11843
|
async function upsertSession(session, pool) {
|
|
@@ -11939,6 +11983,7 @@ function stripPrivateContent(content) {
|
|
|
11939
11983
|
}
|
|
11940
11984
|
|
|
11941
11985
|
// src/hooks/tool-execute.ts
|
|
11986
|
+
var logger2 = createLogger("tool-execute");
|
|
11942
11987
|
var DEFAULT_CONFIG2 = {
|
|
11943
11988
|
maxInputSummaryLength: 500,
|
|
11944
11989
|
maxOutputSummaryLength: 1000,
|
|
@@ -11947,11 +11992,11 @@ var DEFAULT_CONFIG2 = {
|
|
|
11947
11992
|
async function handleToolExecuteBefore(input, output, pool, config = {}) {
|
|
11948
11993
|
const mergedConfig = { ...DEFAULT_CONFIG2, ...config };
|
|
11949
11994
|
const { session, tool, messageId } = input;
|
|
11950
|
-
|
|
11995
|
+
logger2.info(`Tool execute before: ${tool.name}`);
|
|
11951
11996
|
try {
|
|
11952
|
-
const sessionResult = await pool.query("SELECT id FROM
|
|
11997
|
+
const sessionResult = await pool.query("SELECT id FROM session_map WHERE opencode_session_id = $1", [session.id]);
|
|
11953
11998
|
if (sessionResult.rows.length === 0) {
|
|
11954
|
-
|
|
11999
|
+
logger2.warn(`Session not found: ${session.id}`);
|
|
11955
12000
|
return;
|
|
11956
12001
|
}
|
|
11957
12002
|
const sessionInternalId = sessionResult.rows[0].id;
|
|
@@ -11976,19 +12021,19 @@ async function handleToolExecuteBefore(input, output, pool, config = {}) {
|
|
|
11976
12021
|
parameters: sanitizeParameters(tool.parameters)
|
|
11977
12022
|
})
|
|
11978
12023
|
]);
|
|
11979
|
-
|
|
12024
|
+
logger2.info(`Recorded tool input: ${tool.name}`);
|
|
11980
12025
|
} catch (error) {
|
|
11981
|
-
|
|
12026
|
+
logger2.error("Error handling tool.execute.before:", error);
|
|
11982
12027
|
}
|
|
11983
12028
|
}
|
|
11984
12029
|
async function handleToolExecuteAfter(input, output, pool, config = {}) {
|
|
11985
12030
|
const mergedConfig = { ...DEFAULT_CONFIG2, ...config };
|
|
11986
12031
|
const { session, tool, result, messageId, executionTimeMs } = input;
|
|
11987
|
-
|
|
12032
|
+
logger2.info(`Tool execute after: ${tool.name}, success: ${result.success}`);
|
|
11988
12033
|
try {
|
|
11989
|
-
const sessionResult = await pool.query("SELECT id FROM
|
|
12034
|
+
const sessionResult = await pool.query("SELECT id FROM session_map WHERE opencode_session_id = $1", [session.id]);
|
|
11990
12035
|
if (sessionResult.rows.length === 0) {
|
|
11991
|
-
|
|
12036
|
+
logger2.warn(`Session not found: ${session.id}`);
|
|
11992
12037
|
return;
|
|
11993
12038
|
}
|
|
11994
12039
|
const sessionInternalId = sessionResult.rows[0].id;
|
|
@@ -12018,7 +12063,7 @@ async function handleToolExecuteAfter(input, output, pool, config = {}) {
|
|
|
12018
12063
|
}),
|
|
12019
12064
|
observationId
|
|
12020
12065
|
]);
|
|
12021
|
-
|
|
12066
|
+
logger2.info(`Updated observation: ${observationId}`);
|
|
12022
12067
|
} else {
|
|
12023
12068
|
await pool.query(`
|
|
12024
12069
|
INSERT INTO observations (
|
|
@@ -12041,7 +12086,7 @@ async function handleToolExecuteAfter(input, output, pool, config = {}) {
|
|
|
12041
12086
|
event: "tool.execute.after"
|
|
12042
12087
|
})
|
|
12043
12088
|
]);
|
|
12044
|
-
|
|
12089
|
+
logger2.info("Created new observation for tool output");
|
|
12045
12090
|
}
|
|
12046
12091
|
const estimatedTokens = estimateToolTokens(tool.name, result);
|
|
12047
12092
|
await pool.query(`
|
|
@@ -12058,7 +12103,7 @@ async function handleToolExecuteAfter(input, output, pool, config = {}) {
|
|
|
12058
12103
|
})
|
|
12059
12104
|
]);
|
|
12060
12105
|
} catch (error) {
|
|
12061
|
-
|
|
12106
|
+
logger2.error("Error handling tool.execute.after:", error);
|
|
12062
12107
|
}
|
|
12063
12108
|
}
|
|
12064
12109
|
function summarizeToolInput(parameters, maxLength) {
|
|
@@ -12130,6 +12175,7 @@ function estimateToolTokens(toolName, result) {
|
|
|
12130
12175
|
}
|
|
12131
12176
|
|
|
12132
12177
|
// src/hooks/message-updated.ts
|
|
12178
|
+
var logger3 = createLogger("message-updated");
|
|
12133
12179
|
var DEFAULT_CONFIG3 = {
|
|
12134
12180
|
minConfidence: 0.5,
|
|
12135
12181
|
minEntityNameLength: 2,
|
|
@@ -12139,18 +12185,18 @@ var DEFAULT_CONFIG3 = {
|
|
|
12139
12185
|
async function handleMessageUpdated(input, output, pool, config = {}) {
|
|
12140
12186
|
const mergedConfig = { ...DEFAULT_CONFIG3, ...config };
|
|
12141
12187
|
const { session, message } = input;
|
|
12142
|
-
|
|
12188
|
+
logger3.info(`Message updated: ${message.id}, role: ${message.role}`);
|
|
12143
12189
|
try {
|
|
12144
|
-
storeMessage(session.id, message, pool).catch((err) =>
|
|
12145
|
-
const sessionResult = await pool.query("SELECT id FROM
|
|
12190
|
+
storeMessage(session.id, message, pool).catch((err) => logger3.warn("Failed to store message:", err.message));
|
|
12191
|
+
const sessionResult = await pool.query("SELECT id FROM session_map WHERE opencode_session_id = $1", [session.id]);
|
|
12146
12192
|
if (sessionResult.rows.length === 0) {
|
|
12147
|
-
|
|
12193
|
+
logger3.warn(`Session not found: ${session.id}`);
|
|
12148
12194
|
return;
|
|
12149
12195
|
}
|
|
12150
12196
|
const sessionInternalId = sessionResult.rows[0].id;
|
|
12151
|
-
extractEntitiesAndRelations(sessionInternalId, message.content, session.id, pool, mergedConfig).catch((err) =>
|
|
12197
|
+
extractEntitiesAndRelations(sessionInternalId, message.content, session.id, pool, mergedConfig).catch((err) => logger3.warn("Failed to extract entities:", err.message));
|
|
12152
12198
|
} catch (error) {
|
|
12153
|
-
|
|
12199
|
+
logger3.error("Error handling message.updated:", error);
|
|
12154
12200
|
}
|
|
12155
12201
|
}
|
|
12156
12202
|
async function storeMessage(sessionId, message, pool) {
|
|
@@ -12191,7 +12237,7 @@ async function storeMessage(sessionId, message, pool) {
|
|
|
12191
12237
|
completed_at,
|
|
12192
12238
|
embedding
|
|
12193
12239
|
) VALUES (
|
|
12194
|
-
(SELECT id FROM
|
|
12240
|
+
(SELECT id FROM session_map WHERE opencode_session_id = $1),
|
|
12195
12241
|
$2, $3, $4, $5, $6, $7,
|
|
12196
12242
|
$8, $9, $10, $11, $12, $13, $14, $15, $16,
|
|
12197
12243
|
TO_TIMESTAMP($17/1000),
|
|
@@ -12230,15 +12276,15 @@ async function storeMessage(sessionId, message, pool) {
|
|
|
12230
12276
|
message.time?.created || Date.now(),
|
|
12231
12277
|
message.time?.completed
|
|
12232
12278
|
]);
|
|
12233
|
-
|
|
12279
|
+
logger3.info(`Stored message: ${message.id} (${message.role}, ${tokenTotal} tokens)`);
|
|
12234
12280
|
} catch (error) {
|
|
12235
|
-
|
|
12281
|
+
logger3.error("Failed to store message:", error);
|
|
12236
12282
|
throw error;
|
|
12237
12283
|
}
|
|
12238
12284
|
}
|
|
12239
12285
|
async function extractEntitiesAndRelations(sessionId, content, externalSessionId, pool, config) {
|
|
12240
12286
|
const extractedEntities = await extractEntities(content, sessionId, pool, config);
|
|
12241
|
-
|
|
12287
|
+
logger3.info(`Extracted ${extractedEntities.length} entities`);
|
|
12242
12288
|
if (extractedEntities.length >= 2) {
|
|
12243
12289
|
await extractAndStoreRelations(extractedEntities, content, sessionId, pool, config);
|
|
12244
12290
|
}
|
|
@@ -12274,7 +12320,7 @@ async function extractEntities(content, sessionId, pool, config) {
|
|
|
12274
12320
|
name: extracted.name,
|
|
12275
12321
|
type: extracted.type
|
|
12276
12322
|
});
|
|
12277
|
-
|
|
12323
|
+
logger3.info(`Updated entity: ${extracted.name} (weight: ${(currentWeight + 0.1).toFixed(2)})`);
|
|
12278
12324
|
} else {
|
|
12279
12325
|
const tier = determineEntityTier(extracted.type, content);
|
|
12280
12326
|
const insertResult = await pool.query(`
|
|
@@ -12299,7 +12345,7 @@ async function extractEntities(content, sessionId, pool, config) {
|
|
|
12299
12345
|
name: extracted.name,
|
|
12300
12346
|
type: extracted.type
|
|
12301
12347
|
});
|
|
12302
|
-
|
|
12348
|
+
logger3.info(`Created entity: ${extracted.name} (${extracted.type})`);
|
|
12303
12349
|
}
|
|
12304
12350
|
}
|
|
12305
12351
|
return entities;
|
|
@@ -12379,7 +12425,7 @@ async function extractAndStoreRelations(entities, content, sessionId, pool, conf
|
|
|
12379
12425
|
sessionId
|
|
12380
12426
|
]);
|
|
12381
12427
|
relationsCreated++;
|
|
12382
|
-
|
|
12428
|
+
logger3.info(`Created relation: ${source.name} ${relationType} ${target.name}`);
|
|
12383
12429
|
}
|
|
12384
12430
|
}
|
|
12385
12431
|
}
|
|
@@ -12410,6 +12456,7 @@ function inferRelationType(sourceType, targetType, content) {
|
|
|
12410
12456
|
}
|
|
12411
12457
|
|
|
12412
12458
|
// src/hooks/message-part-updated.ts
|
|
12459
|
+
var logger4 = createLogger("message-part-updated");
|
|
12413
12460
|
var partAccumulators = new Map;
|
|
12414
12461
|
function cleanupExpiredAccumulators(maxAgeMs = 300000) {
|
|
12415
12462
|
const now = Date.now();
|
|
@@ -12421,12 +12468,13 @@ function cleanupExpiredAccumulators(maxAgeMs = 300000) {
|
|
|
12421
12468
|
}
|
|
12422
12469
|
}
|
|
12423
12470
|
if (cleaned > 0) {
|
|
12424
|
-
|
|
12471
|
+
logger4.info(`Cleaned up ${cleaned} expired accumulators`);
|
|
12425
12472
|
}
|
|
12426
12473
|
}
|
|
12427
12474
|
setInterval(() => cleanupExpiredAccumulators(), 300000);
|
|
12428
12475
|
|
|
12429
12476
|
// src/hooks/session-compacting.ts
|
|
12477
|
+
var logger5 = createLogger("session-compacting");
|
|
12430
12478
|
var DEFAULT_CONFIG4 = {
|
|
12431
12479
|
preserveHighImportanceObservations: true,
|
|
12432
12480
|
minImportanceToPreserve: 4,
|
|
@@ -12435,11 +12483,11 @@ var DEFAULT_CONFIG4 = {
|
|
|
12435
12483
|
async function handleSessionCompacting(input, output, pool, config = {}) {
|
|
12436
12484
|
const mergedConfig = { ...DEFAULT_CONFIG4, ...config };
|
|
12437
12485
|
const { session, messagesToCompact, compactionStrategy } = input;
|
|
12438
|
-
|
|
12486
|
+
logger5.info(`Session compacting: ${session.id}, strategy: ${compactionStrategy}, messages: ${messagesToCompact.length}`);
|
|
12439
12487
|
try {
|
|
12440
|
-
const sessionResult = await pool.query("SELECT id FROM
|
|
12488
|
+
const sessionResult = await pool.query("SELECT id FROM session_map WHERE opencode_session_id = $1", [session.id]);
|
|
12441
12489
|
if (sessionResult.rows.length === 0) {
|
|
12442
|
-
|
|
12490
|
+
logger5.warn(`Session not found: ${session.id}`);
|
|
12443
12491
|
return;
|
|
12444
12492
|
}
|
|
12445
12493
|
const sessionInternalId = sessionResult.rows[0].id;
|
|
@@ -12460,12 +12508,12 @@ async function handleSessionCompacting(input, output, pool, config = {}) {
|
|
|
12460
12508
|
strategy: compactionStrategy
|
|
12461
12509
|
})
|
|
12462
12510
|
]);
|
|
12463
|
-
|
|
12511
|
+
logger5.info(`Session compacting complete. Preserved ${preserveMessageIds.length} high-value messages`);
|
|
12464
12512
|
if (preserveMessageIds.length > 0) {
|
|
12465
12513
|
output.preserveMessageIds = preserveMessageIds;
|
|
12466
12514
|
}
|
|
12467
12515
|
} catch (error) {
|
|
12468
|
-
|
|
12516
|
+
logger5.error("Error handling session.compacting:", error);
|
|
12469
12517
|
}
|
|
12470
12518
|
}
|
|
12471
12519
|
async function markCacheEntriesAsPruned(sessionId, messageIds, pool) {
|
|
@@ -12486,7 +12534,7 @@ async function markCacheEntriesAsPruned(sessionId, messageIds, pool) {
|
|
|
12486
12534
|
WHERE id = ANY($2) AND tool_output_summary IS NOT NULL
|
|
12487
12535
|
)
|
|
12488
12536
|
`, [sessionId, observationIds]);
|
|
12489
|
-
|
|
12537
|
+
logger5.info(`Marked ${result.rowCount} cache entries as pruned`);
|
|
12490
12538
|
}
|
|
12491
12539
|
async function determineMessagesToPreserve(sessionId, messagesToCompact, pool, config) {
|
|
12492
12540
|
if (!config.preserveHighImportanceObservations) {
|
|
@@ -12503,10 +12551,11 @@ async function determineMessagesToPreserve(sessionId, messagesToCompact, pool, c
|
|
|
12503
12551
|
return result.rows.map((row) => row.message_id);
|
|
12504
12552
|
}
|
|
12505
12553
|
async function handleSessionCompacted(input, output, pool, config = {}) {
|
|
12506
|
-
|
|
12554
|
+
logger5.info(`Session compacted: ${input.session.id}`);
|
|
12507
12555
|
}
|
|
12508
12556
|
|
|
12509
12557
|
// src/hooks/session-completed.ts
|
|
12558
|
+
var logger6 = createLogger("session-completed");
|
|
12510
12559
|
var DEFAULT_CONFIG5 = {
|
|
12511
12560
|
reflectionThreshold: 30,
|
|
12512
12561
|
minObservationThreshold: 30,
|
|
@@ -12517,23 +12566,23 @@ var DEFAULT_CONFIG5 = {
|
|
|
12517
12566
|
async function handleSessionCompleted(input, output, pool, config = {}) {
|
|
12518
12567
|
const mergedConfig = { ...DEFAULT_CONFIG5, ...config };
|
|
12519
12568
|
const { session, summary } = input;
|
|
12520
|
-
|
|
12569
|
+
logger6.info(`Session completed: ${session.id}, messages: ${session.messageCount}, duration: ${session.durationMs}ms`);
|
|
12521
12570
|
try {
|
|
12522
|
-
const sessionResult = await pool.query("SELECT id, reflection_last_at FROM
|
|
12571
|
+
const sessionResult = await pool.query("SELECT id, reflection_last_at FROM session_map WHERE opencode_session_id = $1", [session.id]);
|
|
12523
12572
|
if (sessionResult.rows.length === 0) {
|
|
12524
|
-
|
|
12573
|
+
logger6.warn(`Session not found: ${session.id}`);
|
|
12525
12574
|
return;
|
|
12526
12575
|
}
|
|
12527
12576
|
const sessionInternalId = sessionResult.rows[0].id;
|
|
12528
12577
|
const reflectionLastAt = sessionResult.rows[0].reflection_last_at;
|
|
12529
12578
|
const observationStats = await getObservationStats(sessionInternalId, reflectionLastAt, pool);
|
|
12530
|
-
|
|
12579
|
+
logger6.info(`Observations since last reflection: ${observationStats.countSinceLastReflection}`);
|
|
12531
12580
|
const shouldReflect = await checkShouldTriggerReflection(sessionInternalId, observationStats.countSinceLastReflection, mergedConfig, pool);
|
|
12532
12581
|
if (shouldReflect && mergedConfig.enableReflection) {
|
|
12533
12582
|
await scheduleReflectionTask(sessionInternalId, session.id, observationStats.countSinceLastReflection, mergedConfig, pool);
|
|
12534
12583
|
}
|
|
12535
12584
|
await pool.query(`
|
|
12536
|
-
UPDATE
|
|
12585
|
+
UPDATE session_map
|
|
12537
12586
|
SET updated_at = NOW(),
|
|
12538
12587
|
metadata = metadata || $1
|
|
12539
12588
|
WHERE id = $2
|
|
@@ -12559,9 +12608,9 @@ async function handleSessionCompleted(input, output, pool, config = {}) {
|
|
|
12559
12608
|
observationCount: observationStats.totalCount
|
|
12560
12609
|
})
|
|
12561
12610
|
]);
|
|
12562
|
-
|
|
12611
|
+
logger6.info(`Session completion processed: ${session.id}`);
|
|
12563
12612
|
} catch (error) {
|
|
12564
|
-
|
|
12613
|
+
logger6.error("Error handling session.completed:", error);
|
|
12565
12614
|
}
|
|
12566
12615
|
}
|
|
12567
12616
|
async function getObservationStats(sessionId, reflectionLastAt, pool) {
|
|
@@ -12583,7 +12632,7 @@ async function checkShouldTriggerReflection(sessionId, observationCount, config,
|
|
|
12583
12632
|
return false;
|
|
12584
12633
|
}
|
|
12585
12634
|
const threshold = Math.floor(Math.random() * (config.maxObservationThreshold - config.minObservationThreshold + 1)) + config.minObservationThreshold;
|
|
12586
|
-
|
|
12635
|
+
logger6.info(`Reflection threshold: ${threshold}, current: ${observationCount}`);
|
|
12587
12636
|
if (observationCount < threshold) {
|
|
12588
12637
|
return false;
|
|
12589
12638
|
}
|
|
@@ -12594,7 +12643,7 @@ async function checkShouldTriggerReflection(sessionId, observationCount, config,
|
|
|
12594
12643
|
`, [sessionId]);
|
|
12595
12644
|
const pendingCount = parseInt(pendingResult.rows[0].count, 10);
|
|
12596
12645
|
if (pendingCount > 0) {
|
|
12597
|
-
|
|
12646
|
+
logger6.info(`Reflection already in progress for session: ${sessionId}`);
|
|
12598
12647
|
return false;
|
|
12599
12648
|
}
|
|
12600
12649
|
return true;
|
|
@@ -12602,7 +12651,7 @@ async function checkShouldTriggerReflection(sessionId, observationCount, config,
|
|
|
12602
12651
|
async function scheduleReflectionTask(sessionInternalId, externalSessionId, observationCount, config, pool) {
|
|
12603
12652
|
const currentHour = new Date().getHours();
|
|
12604
12653
|
const isOffPeak = config.offPeakHours.includes(currentHour);
|
|
12605
|
-
|
|
12654
|
+
logger6.info(`Scheduling reflection task for session: ${externalSessionId}, off-peak: ${isOffPeak}`);
|
|
12606
12655
|
if (isOffPeak) {
|
|
12607
12656
|
await executeReflection(sessionInternalId, externalSessionId, observationCount, pool);
|
|
12608
12657
|
} else {
|
|
@@ -12610,7 +12659,7 @@ async function scheduleReflectionTask(sessionInternalId, externalSessionId, obse
|
|
|
12610
12659
|
}
|
|
12611
12660
|
}
|
|
12612
12661
|
async function executeReflection(sessionId, externalSessionId, observationCount, pool) {
|
|
12613
|
-
|
|
12662
|
+
logger6.info(`Executing reflection for session: ${externalSessionId}`);
|
|
12614
12663
|
try {
|
|
12615
12664
|
const observations = await pool.query(`
|
|
12616
12665
|
SELECT id, tool_name, tool_input_summary, tool_output_summary,
|
|
@@ -12621,7 +12670,7 @@ async function executeReflection(sessionId, externalSessionId, observationCount,
|
|
|
12621
12670
|
LIMIT 100
|
|
12622
12671
|
`, [sessionId]);
|
|
12623
12672
|
if (observations.rows.length === 0) {
|
|
12624
|
-
|
|
12673
|
+
logger6.info(`No observations to reflect on for session: ${externalSessionId}`);
|
|
12625
12674
|
return;
|
|
12626
12675
|
}
|
|
12627
12676
|
const reflectionResult = await performReflection(observations.rows);
|
|
@@ -12647,13 +12696,13 @@ async function executeReflection(sessionId, externalSessionId, observationCount,
|
|
|
12647
12696
|
}
|
|
12648
12697
|
}
|
|
12649
12698
|
await pool.query(`
|
|
12650
|
-
UPDATE
|
|
12699
|
+
UPDATE session_map
|
|
12651
12700
|
SET reflection_last_at = NOW()
|
|
12652
12701
|
WHERE id = $1
|
|
12653
12702
|
`, [sessionId]);
|
|
12654
|
-
|
|
12703
|
+
logger6.info(`Reflection completed for session: ${externalSessionId}, patterns: ${reflectionResult.patterns.length}`);
|
|
12655
12704
|
} catch (error) {
|
|
12656
|
-
|
|
12705
|
+
logger6.error(`Reflection failed for session: ${externalSessionId}`, error);
|
|
12657
12706
|
await pool.query(`
|
|
12658
12707
|
INSERT INTO reflection_errors (
|
|
12659
12708
|
session_id, error_message, error_stack,
|
|
@@ -12706,9 +12755,9 @@ async function performReflection(observations) {
|
|
|
12706
12755
|
return { patterns };
|
|
12707
12756
|
}
|
|
12708
12757
|
async function queueReflectionForOffPeak(sessionId, externalSessionId, observationCount, pool) {
|
|
12709
|
-
|
|
12758
|
+
logger6.info(`Queued reflection for off-peak execution: ${externalSessionId}`);
|
|
12710
12759
|
await pool.query(`
|
|
12711
|
-
UPDATE
|
|
12760
|
+
UPDATE session_map
|
|
12712
12761
|
SET metadata = metadata || $1
|
|
12713
12762
|
WHERE id = $2
|
|
12714
12763
|
`, [
|
|
@@ -18648,6 +18697,7 @@ function getEmbeddingService() {
|
|
|
18648
18697
|
}
|
|
18649
18698
|
|
|
18650
18699
|
// src/mcp/recall-memory.ts
|
|
18700
|
+
var logger7 = createLogger("recall-memory");
|
|
18651
18701
|
var DEFAULT_CONFIG6 = {
|
|
18652
18702
|
weights: {
|
|
18653
18703
|
semantic: 0.5,
|
|
@@ -18662,7 +18712,7 @@ var PER_STRATEGY_LIMIT = 20;
|
|
|
18662
18712
|
async function recallMemory(input, pool, config = {}) {
|
|
18663
18713
|
const mergedConfig = { ...DEFAULT_CONFIG6, ...config };
|
|
18664
18714
|
const startTime = Date.now();
|
|
18665
|
-
|
|
18715
|
+
logger7.info(`recall_memory called: "${input.query.substring(0, 100)}..."`);
|
|
18666
18716
|
try {
|
|
18667
18717
|
const sessionId = await resolveSessionId(input, pool);
|
|
18668
18718
|
const embeddingService = getEmbeddingService();
|
|
@@ -18678,7 +18728,7 @@ async function recallMemory(input, pool, config = {}) {
|
|
|
18678
18728
|
if (fusionResult) {
|
|
18679
18729
|
fusedEmbedding = fusionResult.fusedEmbedding;
|
|
18680
18730
|
contextUsed = fusionResult.contextUsed;
|
|
18681
|
-
|
|
18731
|
+
logger7.info(`Topic fusion applied (topic: ${fusionResult.contextUsed.topic_segment_id.substring(0, 8)}...)`);
|
|
18682
18732
|
}
|
|
18683
18733
|
}
|
|
18684
18734
|
const strategies = input.retrieval_strategies || ["semantic", "bm25", "graph"];
|
|
@@ -18717,7 +18767,7 @@ async function recallMemory(input, pool, config = {}) {
|
|
|
18717
18767
|
};
|
|
18718
18768
|
});
|
|
18719
18769
|
const retrievalTime = Date.now() - startTime;
|
|
18720
|
-
|
|
18770
|
+
logger7.info(`recall_memory completed: ${results.length} results in ${retrievalTime}ms`);
|
|
18721
18771
|
return {
|
|
18722
18772
|
query: input.query,
|
|
18723
18773
|
context_used: contextUsed,
|
|
@@ -18729,7 +18779,7 @@ async function recallMemory(input, pool, config = {}) {
|
|
|
18729
18779
|
session_id: sessionId
|
|
18730
18780
|
};
|
|
18731
18781
|
} catch (error) {
|
|
18732
|
-
|
|
18782
|
+
logger7.error("recall_memory error:", error);
|
|
18733
18783
|
const retrievalTime = Date.now() - startTime;
|
|
18734
18784
|
return {
|
|
18735
18785
|
query: input.query,
|
|
@@ -18756,13 +18806,13 @@ async function resolveSessionId(input, pool) {
|
|
|
18756
18806
|
try {
|
|
18757
18807
|
const recent = await pool.query("SELECT id, opencode_session_id FROM session_map ORDER BY last_active_at DESC LIMIT 1");
|
|
18758
18808
|
if (recent.rows.length > 0) {
|
|
18759
|
-
|
|
18809
|
+
logger7.info(`Auto-detected session: ${recent.rows[0].opencode_session_id}`);
|
|
18760
18810
|
return recent.rows[0].id;
|
|
18761
18811
|
}
|
|
18762
18812
|
} catch {}
|
|
18763
18813
|
const recentSession = await pool.query("SELECT id, external_id FROM sessions ORDER BY updated_at DESC NULLS LAST, created_at DESC LIMIT 1");
|
|
18764
18814
|
if (recentSession.rows.length > 0) {
|
|
18765
|
-
|
|
18815
|
+
logger7.info(`Auto-detected session (legacy): ${recentSession.rows[0].external_id}`);
|
|
18766
18816
|
return recentSession.rows[0].id;
|
|
18767
18817
|
}
|
|
18768
18818
|
throw new Error("No session found. Please start a conversation first or provide session_id explicitly.");
|
|
@@ -18823,7 +18873,7 @@ async function topicContextFusion(externalSessionId, queryEmbedding, embeddingSe
|
|
|
18823
18873
|
}
|
|
18824
18874
|
};
|
|
18825
18875
|
} catch (err) {
|
|
18826
|
-
|
|
18876
|
+
logger7.warn("Topic context fusion skipped:", err);
|
|
18827
18877
|
return null;
|
|
18828
18878
|
}
|
|
18829
18879
|
}
|
|
@@ -18852,7 +18902,7 @@ async function parallelRetrieve(query, queryEmbedding, sessionId, strategies, po
|
|
|
18852
18902
|
const r2 = await semanticSearch(queryEmbedding, sessionId, pool, filterSQL);
|
|
18853
18903
|
results.set("semantic", r2);
|
|
18854
18904
|
} catch (err) {
|
|
18855
|
-
|
|
18905
|
+
logger7.warn("Semantic search failed:", err);
|
|
18856
18906
|
results.set("semantic", []);
|
|
18857
18907
|
}
|
|
18858
18908
|
})());
|
|
@@ -18863,7 +18913,7 @@ async function parallelRetrieve(query, queryEmbedding, sessionId, strategies, po
|
|
|
18863
18913
|
const r2 = await bm25Search(query, sessionId, pool, filterSQL);
|
|
18864
18914
|
results.set("bm25", r2);
|
|
18865
18915
|
} catch (err) {
|
|
18866
|
-
|
|
18916
|
+
logger7.warn("BM25 search failed:", err);
|
|
18867
18917
|
results.set("bm25", []);
|
|
18868
18918
|
}
|
|
18869
18919
|
})());
|
|
@@ -18874,7 +18924,7 @@ async function parallelRetrieve(query, queryEmbedding, sessionId, strategies, po
|
|
|
18874
18924
|
const r2 = await graphTraversal(query, embeddingServiceFallback(query), sessionId, pool, filterSQL);
|
|
18875
18925
|
results.set("graph", r2);
|
|
18876
18926
|
} catch (err) {
|
|
18877
|
-
|
|
18927
|
+
logger7.warn("Graph traversal failed:", err);
|
|
18878
18928
|
results.set("graph", []);
|
|
18879
18929
|
}
|
|
18880
18930
|
})());
|
|
@@ -18885,7 +18935,7 @@ async function parallelRetrieve(query, queryEmbedding, sessionId, strategies, po
|
|
|
18885
18935
|
const r2 = await keywordSearch(query, sessionId, pool, filterSQL);
|
|
18886
18936
|
results.set("keyword", r2);
|
|
18887
18937
|
} catch (err) {
|
|
18888
|
-
|
|
18938
|
+
logger7.warn("Keyword search failed:", err);
|
|
18889
18939
|
results.set("keyword", []);
|
|
18890
18940
|
}
|
|
18891
18941
|
})());
|
|
@@ -18950,7 +19000,7 @@ async function semanticSearch(queryEmbedding, sessionId, pool, filters) {
|
|
|
18950
19000
|
_timestamp: row.created_at
|
|
18951
19001
|
})));
|
|
18952
19002
|
} catch (err) {
|
|
18953
|
-
|
|
19003
|
+
logger7.warn("Entity semantic search error:", err);
|
|
18954
19004
|
}
|
|
18955
19005
|
try {
|
|
18956
19006
|
const obsQuery = `
|
|
@@ -18984,7 +19034,7 @@ async function semanticSearch(queryEmbedding, sessionId, pool, filters) {
|
|
|
18984
19034
|
_timestamp: row.created_at
|
|
18985
19035
|
})));
|
|
18986
19036
|
} catch (err) {
|
|
18987
|
-
|
|
19037
|
+
logger7.warn("Observation semantic search error:", err);
|
|
18988
19038
|
}
|
|
18989
19039
|
try {
|
|
18990
19040
|
const refQuery = `
|
|
@@ -19018,7 +19068,7 @@ async function semanticSearch(queryEmbedding, sessionId, pool, filters) {
|
|
|
19018
19068
|
_timestamp: row.created_at
|
|
19019
19069
|
})));
|
|
19020
19070
|
} catch (err) {
|
|
19021
|
-
|
|
19071
|
+
logger7.warn("Reflection semantic search error:", err);
|
|
19022
19072
|
}
|
|
19023
19073
|
return facts;
|
|
19024
19074
|
}
|
|
@@ -19064,7 +19114,7 @@ async function bm25Search(query, sessionId, pool, filters) {
|
|
|
19064
19114
|
_timestamp: row.created_at
|
|
19065
19115
|
})));
|
|
19066
19116
|
} catch (err) {
|
|
19067
|
-
|
|
19117
|
+
logger7.warn("BM25 search failed (pg_trgm may not be installed):", err);
|
|
19068
19118
|
}
|
|
19069
19119
|
return facts;
|
|
19070
19120
|
}
|
|
@@ -19126,7 +19176,7 @@ async function graphTraversal(query, _queryEmb, sessionId, pool, filters) {
|
|
|
19126
19176
|
_timestamp: row.created_at
|
|
19127
19177
|
}));
|
|
19128
19178
|
} catch (err) {
|
|
19129
|
-
|
|
19179
|
+
logger7.warn("Graph traversal error:", err);
|
|
19130
19180
|
return [];
|
|
19131
19181
|
}
|
|
19132
19182
|
}
|
|
@@ -19166,7 +19216,7 @@ async function keywordSearch(query, sessionId, pool, filters) {
|
|
|
19166
19216
|
_timestamp: row.created_at
|
|
19167
19217
|
}));
|
|
19168
19218
|
} catch (err) {
|
|
19169
|
-
|
|
19219
|
+
logger7.warn("Keyword search error:", err);
|
|
19170
19220
|
return [];
|
|
19171
19221
|
}
|
|
19172
19222
|
}
|
|
@@ -19367,6 +19417,7 @@ function formatVectorLiteral(embedding) {
|
|
|
19367
19417
|
}
|
|
19368
19418
|
|
|
19369
19419
|
// src/mcp/hindsight-reflect.ts
|
|
19420
|
+
var logger8 = createLogger("hindsight-reflect");
|
|
19370
19421
|
var DEFAULT_REFLECTION_SYSTEM_PROMPT = `You are a reflection engine that analyzes coding session observations to extract reusable patterns and insights.
|
|
19371
19422
|
|
|
19372
19423
|
## Task
|
|
@@ -19466,11 +19517,11 @@ async function hindsightReflect(input, pool, config = {}) {
|
|
|
19466
19517
|
const mergedConfig = { ...DEFAULT_CONFIG7, ...config };
|
|
19467
19518
|
const startTime = Date.now();
|
|
19468
19519
|
let totalTokens = { input: 0, output: 0, total: 0 };
|
|
19469
|
-
|
|
19520
|
+
logger8.info(`hindsight_reflect called: session=${input.session_id || "none"}, ` + `omo_task=${input.omo_task_id || "none"}, ` + `topic_segment=${input.topic_segment_id || "none"}, ` + `aggregate=${input.aggregate ?? false}`);
|
|
19470
19521
|
try {
|
|
19471
19522
|
const scope = await resolveScope(input, pool);
|
|
19472
19523
|
if (!scope.observable) {
|
|
19473
|
-
|
|
19524
|
+
logger8.info("hindsight_reflect: no observations available for scope");
|
|
19474
19525
|
return {
|
|
19475
19526
|
generated_reflections: [],
|
|
19476
19527
|
token_usage: totalTokens,
|
|
@@ -19479,17 +19530,17 @@ async function hindsightReflect(input, pool, config = {}) {
|
|
|
19479
19530
|
}
|
|
19480
19531
|
const observations = await collectObservations(input, scope, mergedConfig, pool);
|
|
19481
19532
|
if (observations.length === 0) {
|
|
19482
|
-
|
|
19533
|
+
logger8.info("hindsight_reflect: zero observations collected");
|
|
19483
19534
|
return {
|
|
19484
19535
|
generated_reflections: [],
|
|
19485
19536
|
token_usage: totalTokens,
|
|
19486
19537
|
duration_ms: Date.now() - startTime
|
|
19487
19538
|
};
|
|
19488
19539
|
}
|
|
19489
|
-
|
|
19540
|
+
logger8.info(`Fetched ${observations.length} observations for reflection`);
|
|
19490
19541
|
const threshold = mergedConfig.observationThreshold;
|
|
19491
19542
|
if (observations.length < threshold && input.trigger_type !== "manual") {
|
|
19492
|
-
|
|
19543
|
+
logger8.info(`hindsight_reflect: observation count (${observations.length}) ` + `below threshold (${threshold}), skipping`);
|
|
19493
19544
|
return {
|
|
19494
19545
|
generated_reflections: [],
|
|
19495
19546
|
token_usage: totalTokens,
|
|
@@ -19498,7 +19549,7 @@ async function hindsightReflect(input, pool, config = {}) {
|
|
|
19498
19549
|
}
|
|
19499
19550
|
const shouldAggregate = input.aggregate === true || !!input.omo_task_id && input.aggregate !== false;
|
|
19500
19551
|
const segments = groupObservationsBySegment(observations, shouldAggregate);
|
|
19501
|
-
|
|
19552
|
+
logger8.info(`Grouped into ${segments.length} segment(s) ` + `(aggregate=${shouldAggregate})`);
|
|
19502
19553
|
const generatedReflections = [];
|
|
19503
19554
|
for (const segment of segments) {
|
|
19504
19555
|
const segmentReflections = await reflectOnSegment(segment, scope, input, mergedConfig, pool);
|
|
@@ -19508,7 +19559,7 @@ async function hindsightReflect(input, pool, config = {}) {
|
|
|
19508
19559
|
}
|
|
19509
19560
|
await updateReflectionTimestamp(scope, pool);
|
|
19510
19561
|
const elapsed = Date.now() - startTime;
|
|
19511
|
-
|
|
19562
|
+
logger8.info(`hindsight_reflect completed: ` + `${generatedReflections.length} reflections in ${elapsed}ms`);
|
|
19512
19563
|
return {
|
|
19513
19564
|
generated_reflections: generatedReflections,
|
|
19514
19565
|
token_usage: totalTokens,
|
|
@@ -19516,7 +19567,7 @@ async function hindsightReflect(input, pool, config = {}) {
|
|
|
19516
19567
|
};
|
|
19517
19568
|
} catch (error) {
|
|
19518
19569
|
const elapsed = Date.now() - startTime;
|
|
19519
|
-
|
|
19570
|
+
logger8.error("hindsight_reflect error:", error);
|
|
19520
19571
|
await logReflectionError(input, error, pool);
|
|
19521
19572
|
return {
|
|
19522
19573
|
generated_reflections: [],
|
|
@@ -19582,21 +19633,21 @@ async function collectObservations(input, scope, config, pool) {
|
|
|
19582
19633
|
try {
|
|
19583
19634
|
return await collectObservationsForSegment(scope.topicSegmentId, config, pool);
|
|
19584
19635
|
} catch (err) {
|
|
19585
|
-
|
|
19636
|
+
logger8.warn("New-schema segment collection failed, falling back:", err);
|
|
19586
19637
|
}
|
|
19587
19638
|
}
|
|
19588
19639
|
if (scope.omoTaskId && scope.usesNewSchema && scope.sessionMapIds.length > 0) {
|
|
19589
19640
|
try {
|
|
19590
19641
|
return await collectObservationsForOmoTask(scope.omoTaskId, config, pool);
|
|
19591
19642
|
} catch (err) {
|
|
19592
|
-
|
|
19643
|
+
logger8.warn("New-schema omo_task collection failed, falling back:", err);
|
|
19593
19644
|
}
|
|
19594
19645
|
}
|
|
19595
19646
|
if (scope.usesNewSchema && scope.sessionMapIds.length > 0) {
|
|
19596
19647
|
try {
|
|
19597
19648
|
return await collectObservationsForSessionMaps(scope.sessionMapIds, config, pool);
|
|
19598
19649
|
} catch (err) {
|
|
19599
|
-
|
|
19650
|
+
logger8.warn("New-schema session_map collection failed, falling back:", err);
|
|
19600
19651
|
}
|
|
19601
19652
|
}
|
|
19602
19653
|
return collectObservationsLegacy(scope.sessionInternalIds, config, pool);
|
|
@@ -19744,7 +19795,7 @@ function buildReflectionPrompt(segment, batch, config, _input) {
|
|
|
19744
19795
|
return template.replace("{topic_summary}", segment.topicSummary).replace("{session_context}", sessionContext).replace("{topics_summary}", segment.topicSummary).replace("{observations_json}", observationsJson);
|
|
19745
19796
|
}
|
|
19746
19797
|
async function performReflectionWithLLM(observations, topicSummary, config, prompt) {
|
|
19747
|
-
|
|
19798
|
+
logger8.info(`Performing reflection with ${config.modelSize} model ` + `on ${observations.length} observations (topic: ${topicSummary})`);
|
|
19748
19799
|
return performHeuristicReflection(observations, topicSummary);
|
|
19749
19800
|
}
|
|
19750
19801
|
function performHeuristicReflection(observations, topicSummary) {
|
|
@@ -19852,7 +19903,7 @@ async function storeReflection(pattern, segment, scope, pool, config, input) {
|
|
|
19852
19903
|
JSON.stringify(metadata)
|
|
19853
19904
|
]);
|
|
19854
19905
|
} catch {
|
|
19855
|
-
|
|
19906
|
+
logger8.warn("New schema INSERT failed, falling back to legacy");
|
|
19856
19907
|
}
|
|
19857
19908
|
}
|
|
19858
19909
|
if (!insertResult && scope.sessionInternalIds.length > 0) {
|
|
@@ -19870,7 +19921,7 @@ async function storeReflection(pattern, segment, scope, pool, config, input) {
|
|
|
19870
19921
|
]);
|
|
19871
19922
|
}
|
|
19872
19923
|
if (!insertResult) {
|
|
19873
|
-
|
|
19924
|
+
logger8.warn("No valid session reference to store reflection");
|
|
19874
19925
|
return null;
|
|
19875
19926
|
}
|
|
19876
19927
|
return {
|
|
@@ -19885,7 +19936,7 @@ async function storeReflection(pattern, segment, scope, pool, config, input) {
|
|
|
19885
19936
|
metadata
|
|
19886
19937
|
};
|
|
19887
19938
|
} catch (error) {
|
|
19888
|
-
|
|
19939
|
+
logger8.error("Failed to store reflection:", error);
|
|
19889
19940
|
return null;
|
|
19890
19941
|
}
|
|
19891
19942
|
}
|
|
@@ -19909,7 +19960,7 @@ async function updateReflectionTimestamp(scope, pool) {
|
|
|
19909
19960
|
WHERE id = $1`, [internalId]);
|
|
19910
19961
|
}
|
|
19911
19962
|
} catch (error) {
|
|
19912
|
-
|
|
19963
|
+
logger8.warn("Failed to update reflection timestamp:", error);
|
|
19913
19964
|
}
|
|
19914
19965
|
}
|
|
19915
19966
|
async function logReflectionError(input, error, pool) {
|
|
@@ -19928,7 +19979,7 @@ async function logReflectionError(input, error, pool) {
|
|
|
19928
19979
|
observation_count, retry_count
|
|
19929
19980
|
) VALUES ($1, $2, $3, $4, $5)`, [sessionId, errorMessage, errorStack, 0, 0]);
|
|
19930
19981
|
} catch (logError) {
|
|
19931
|
-
|
|
19982
|
+
logger8.error("Failed to log reflection error:", logError);
|
|
19932
19983
|
}
|
|
19933
19984
|
}
|
|
19934
19985
|
async function tableExists(pool, tableName) {
|
|
@@ -20255,46 +20306,6 @@ function createCacheManager(pool, config) {
|
|
|
20255
20306
|
return new SemanticCacheManager(pool, config);
|
|
20256
20307
|
}
|
|
20257
20308
|
|
|
20258
|
-
// src/services/logger.ts
|
|
20259
|
-
var LEVEL_MAP = {
|
|
20260
|
-
debug: 0,
|
|
20261
|
-
info: 1,
|
|
20262
|
-
warn: 2,
|
|
20263
|
-
error: 3
|
|
20264
|
-
};
|
|
20265
|
-
var LEVEL_NAMES = {
|
|
20266
|
-
debug: "DEBUG",
|
|
20267
|
-
info: "INFO",
|
|
20268
|
-
warn: "WARN",
|
|
20269
|
-
error: "ERROR"
|
|
20270
|
-
};
|
|
20271
|
-
function getConfiguredLevel() {
|
|
20272
|
-
const env = process.env.PG_MEMORY_LOG_LEVEL ?? "info";
|
|
20273
|
-
const level = env.toLowerCase();
|
|
20274
|
-
if (level in LEVEL_MAP) {
|
|
20275
|
-
return LEVEL_MAP[level];
|
|
20276
|
-
}
|
|
20277
|
-
return LEVEL_MAP.info;
|
|
20278
|
-
}
|
|
20279
|
-
var currentLevel = getConfiguredLevel();
|
|
20280
|
-
function log(level, module, msg, data) {
|
|
20281
|
-
if (currentLevel > LEVEL_MAP[level]) {
|
|
20282
|
-
return;
|
|
20283
|
-
}
|
|
20284
|
-
const line = data !== undefined ? `[PG Memory] [${LEVEL_NAMES[level]}] [${module}] ${msg} ${JSON.stringify(data)}
|
|
20285
|
-
` : `[PG Memory] [${LEVEL_NAMES[level]}] [${module}] ${msg}
|
|
20286
|
-
`;
|
|
20287
|
-
process.stderr.write(line);
|
|
20288
|
-
}
|
|
20289
|
-
function createLogger(module) {
|
|
20290
|
-
return {
|
|
20291
|
-
debug: (msg, data) => log("debug", module, msg, data),
|
|
20292
|
-
info: (msg, data) => log("info", module, msg, data),
|
|
20293
|
-
warn: (msg, data) => log("warn", module, msg, data),
|
|
20294
|
-
error: (msg, data) => log("error", module, msg, data)
|
|
20295
|
-
};
|
|
20296
|
-
}
|
|
20297
|
-
|
|
20298
20309
|
// src/services/keyword.ts
|
|
20299
20310
|
var CODE_BLOCK_PATTERN = /```[\s\S]*?```/g;
|
|
20300
20311
|
var INLINE_CODE_PATTERN = /`[^`]+`/g;
|
|
@@ -20332,6 +20343,7 @@ Consider using the following tools to persist this knowledge:
|
|
|
20332
20343
|
- The pg-memory plugin will automatically record this session's observations for future use.`;
|
|
20333
20344
|
|
|
20334
20345
|
// src/index.ts
|
|
20346
|
+
var logger9 = createLogger("plugin");
|
|
20335
20347
|
var DEFAULT_PLUGIN_CONFIG = {
|
|
20336
20348
|
database: {
|
|
20337
20349
|
host: process.env.PG_HOST || "localhost",
|
|
@@ -20401,7 +20413,7 @@ class OpenCodePGMemoryPlugin {
|
|
|
20401
20413
|
async initialize() {
|
|
20402
20414
|
if (this.initialized)
|
|
20403
20415
|
return;
|
|
20404
|
-
|
|
20416
|
+
logger9.info("Initializing plugin...");
|
|
20405
20417
|
try {
|
|
20406
20418
|
this.pool = await initializeDatabase(this.config.database);
|
|
20407
20419
|
if (this.config.cache.enabled) {
|
|
@@ -20409,16 +20421,16 @@ class OpenCodePGMemoryPlugin {
|
|
|
20409
20421
|
}
|
|
20410
20422
|
this.startCleanupTasks();
|
|
20411
20423
|
this.initialized = true;
|
|
20412
|
-
|
|
20424
|
+
logger9.info("Plugin initialized successfully");
|
|
20413
20425
|
} catch (error) {
|
|
20414
|
-
|
|
20426
|
+
logger9.error("Plugin initialization failed:", error);
|
|
20415
20427
|
throw error;
|
|
20416
20428
|
}
|
|
20417
20429
|
}
|
|
20418
20430
|
async close() {
|
|
20419
20431
|
if (!this.initialized)
|
|
20420
20432
|
return;
|
|
20421
|
-
|
|
20433
|
+
logger9.info("Closing plugin...");
|
|
20422
20434
|
for (const interval of this.cleanupIntervals) {
|
|
20423
20435
|
clearInterval(interval);
|
|
20424
20436
|
}
|
|
@@ -20427,7 +20439,7 @@ class OpenCodePGMemoryPlugin {
|
|
|
20427
20439
|
this.pool = null;
|
|
20428
20440
|
this.cacheManager = null;
|
|
20429
20441
|
this.initialized = false;
|
|
20430
|
-
|
|
20442
|
+
logger9.info("Plugin closed");
|
|
20431
20443
|
}
|
|
20432
20444
|
startCleanupTasks() {
|
|
20433
20445
|
this.cleanupIntervals.push(setInterval(() => {
|
|
@@ -20454,14 +20466,13 @@ var OpenCodePGMemory = async (ctx) => {
|
|
|
20454
20466
|
const plugin = new OpenCodePGMemoryPlugin(config);
|
|
20455
20467
|
await plugin.initialize();
|
|
20456
20468
|
const cleanup = () => {
|
|
20457
|
-
plugin.close().catch((err) =>
|
|
20469
|
+
plugin.close().catch((err) => logger9.error("Cleanup error:", err));
|
|
20458
20470
|
};
|
|
20459
20471
|
process.on("exit", cleanup);
|
|
20460
20472
|
process.on("SIGINT", cleanup);
|
|
20461
20473
|
process.on("SIGTERM", cleanup);
|
|
20462
20474
|
const pool = plugin.getPool();
|
|
20463
20475
|
const cacheManager = plugin.getCacheManager();
|
|
20464
|
-
const logger = createLogger("plugin");
|
|
20465
20476
|
const injectedSessions = new Set;
|
|
20466
20477
|
const injectedSystemPrompt = new Set;
|
|
20467
20478
|
return {
|
|
@@ -20483,7 +20494,7 @@ var OpenCodePGMemory = async (ctx) => {
|
|
|
20483
20494
|
await ctx.client.experimental.chat.system.transform(memory);
|
|
20484
20495
|
}
|
|
20485
20496
|
} catch (err) {
|
|
20486
|
-
|
|
20497
|
+
logger9.warn("Failed to inject memories via client:", err);
|
|
20487
20498
|
}
|
|
20488
20499
|
}
|
|
20489
20500
|
break;
|
|
@@ -20498,18 +20509,18 @@ var OpenCodePGMemory = async (ctx) => {
|
|
|
20498
20509
|
}, {}, pool);
|
|
20499
20510
|
if (cacheManager && properties.messagesToCompact?.length) {
|
|
20500
20511
|
try {
|
|
20501
|
-
const obsResult = await pool.query("SELECT id FROM observations WHERE session_id = (SELECT id FROM
|
|
20512
|
+
const obsResult = await pool.query("SELECT id FROM observations WHERE session_id = (SELECT id FROM session_map WHERE opencode_session_id = $1) AND message_id = ANY($2)", [sid, properties.messagesToCompact]);
|
|
20502
20513
|
if (obsResult.rows.length > 0) {
|
|
20503
20514
|
await cacheManager.markMultipleAsPruned(obsResult.rows.map((r2) => r2.id));
|
|
20504
20515
|
}
|
|
20505
20516
|
} catch (err) {
|
|
20506
|
-
|
|
20517
|
+
logger9.warn("Failed to mark cache pruned:", err);
|
|
20507
20518
|
}
|
|
20508
20519
|
}
|
|
20509
20520
|
const summaryText = properties.summary || properties.compactionSummary;
|
|
20510
20521
|
if (summaryText && String(summaryText).length >= 100) {
|
|
20511
20522
|
try {
|
|
20512
|
-
const sessionResult = await pool.query("SELECT id FROM
|
|
20523
|
+
const sessionResult = await pool.query("SELECT id FROM session_map WHERE opencode_session_id = $1", [sid]);
|
|
20513
20524
|
if (sessionResult.rows.length > 0) {
|
|
20514
20525
|
await pool.query(`
|
|
20515
20526
|
INSERT INTO reflections (session_id, summary, source_observation_ids, confidence, pattern_type, metadata)
|
|
@@ -20522,10 +20533,10 @@ var OpenCodePGMemory = async (ctx) => {
|
|
|
20522
20533
|
"session_summary",
|
|
20523
20534
|
JSON.stringify({ savedAt: new Date().toISOString() })
|
|
20524
20535
|
]);
|
|
20525
|
-
|
|
20536
|
+
logger9.info("Saved session summary as reflection", { sessionID: sid });
|
|
20526
20537
|
}
|
|
20527
20538
|
} catch (err) {
|
|
20528
|
-
|
|
20539
|
+
logger9.warn("Failed to save session summary", err);
|
|
20529
20540
|
}
|
|
20530
20541
|
}
|
|
20531
20542
|
}
|
|
@@ -20535,14 +20546,14 @@ var OpenCodePGMemory = async (ctx) => {
|
|
|
20535
20546
|
const sid = properties.sessionID || properties.session?.id;
|
|
20536
20547
|
if (sid) {
|
|
20537
20548
|
try {
|
|
20538
|
-
const sessionResult = await pool.query("SELECT id FROM
|
|
20549
|
+
const sessionResult = await pool.query("SELECT id FROM session_map WHERE opencode_session_id = $1", [sid]);
|
|
20539
20550
|
if (sessionResult.rows.length > 0) {
|
|
20540
20551
|
const internalId = sessionResult.rows[0].id;
|
|
20541
|
-
await pool.query("DELETE FROM
|
|
20542
|
-
|
|
20552
|
+
await pool.query("DELETE FROM session_map WHERE id = $1", [internalId]);
|
|
20553
|
+
logger9.info(`Cleaned up deleted session: ${sid}`);
|
|
20543
20554
|
}
|
|
20544
20555
|
} catch (err) {
|
|
20545
|
-
|
|
20556
|
+
logger9.error("Error cleaning up deleted session:", err);
|
|
20546
20557
|
}
|
|
20547
20558
|
}
|
|
20548
20559
|
break;
|
|
@@ -20571,7 +20582,7 @@ var OpenCodePGMemory = async (ctx) => {
|
|
|
20571
20582
|
case "message.updated": {
|
|
20572
20583
|
const sid = properties.sessionID || properties.session?.id;
|
|
20573
20584
|
if (sid && properties.message) {
|
|
20574
|
-
handleMessageUpdated({ session: { id: sid }, message: properties.message }, {}, pool).catch((err) =>
|
|
20585
|
+
handleMessageUpdated({ session: { id: sid }, message: properties.message }, {}, pool).catch((err) => logger9.warn("message.updated handler error:", err));
|
|
20575
20586
|
}
|
|
20576
20587
|
break;
|
|
20577
20588
|
}
|
|
@@ -20603,14 +20614,14 @@ var OpenCodePGMemory = async (ctx) => {
|
|
|
20603
20614
|
break;
|
|
20604
20615
|
}
|
|
20605
20616
|
} catch (error) {
|
|
20606
|
-
|
|
20617
|
+
logger9.error(`Error handling event '${type}':`, error);
|
|
20607
20618
|
}
|
|
20608
20619
|
},
|
|
20609
20620
|
"chat.message": async (input, output) => {
|
|
20610
20621
|
try {
|
|
20611
20622
|
if (!injectedSessions.has(input.sessionID)) {
|
|
20612
20623
|
injectedSessions.add(input.sessionID);
|
|
20613
|
-
|
|
20624
|
+
logger9.info("First message in session", { sessionID: input.sessionID });
|
|
20614
20625
|
const contextLimit = 128000;
|
|
20615
20626
|
const budget = calculateTokenBudget(contextLimit, config.tokenBudget || {});
|
|
20616
20627
|
const facts = await retrieveFactsForInjection2(input.sessionID, { maxTokens: typeof budget === "number" ? budget : 2000 }, pool, { minConfidence: 0.5, minWeight: 0.3 });
|
|
@@ -20625,15 +20636,15 @@ var OpenCodePGMemory = async (ctx) => {
|
|
|
20625
20636
|
text: contextBlock,
|
|
20626
20637
|
synthetic: true
|
|
20627
20638
|
});
|
|
20628
|
-
|
|
20639
|
+
logger9.info(`Injected ${facts.length} memories`, { sessionID: input.sessionID });
|
|
20629
20640
|
}
|
|
20630
20641
|
} else {
|
|
20631
|
-
|
|
20642
|
+
logger9.debug("No memories to inject");
|
|
20632
20643
|
}
|
|
20633
20644
|
}
|
|
20634
20645
|
const userText = output.parts.filter((p2) => p2.type === "text" && !p2.synthetic).map((p2) => p2.text || "").join(" ");
|
|
20635
20646
|
if (detectMemoryKeyword(userText)) {
|
|
20636
|
-
|
|
20647
|
+
logger9.info("Memory keyword detected", { sessionID: input.sessionID });
|
|
20637
20648
|
output.parts.push({
|
|
20638
20649
|
id: `prt_pgmemory-nudge-${Date.now()}`,
|
|
20639
20650
|
sessionID: input.sessionID,
|
|
@@ -20644,7 +20655,7 @@ var OpenCodePGMemory = async (ctx) => {
|
|
|
20644
20655
|
});
|
|
20645
20656
|
}
|
|
20646
20657
|
} catch (error) {
|
|
20647
|
-
|
|
20658
|
+
logger9.error("Failed to inject memories in chat.message", error);
|
|
20648
20659
|
}
|
|
20649
20660
|
},
|
|
20650
20661
|
"tool.execute.before": async (input, output) => {
|
|
@@ -20655,7 +20666,7 @@ var OpenCodePGMemory = async (ctx) => {
|
|
|
20655
20666
|
messageId: input.callID
|
|
20656
20667
|
}, { parameters: output.args }, pool);
|
|
20657
20668
|
} catch (error) {
|
|
20658
|
-
|
|
20669
|
+
logger9.error("Error in tool.execute.before:", error);
|
|
20659
20670
|
}
|
|
20660
20671
|
},
|
|
20661
20672
|
"tool.execute.after": async (input, output) => {
|
|
@@ -20673,7 +20684,7 @@ var OpenCodePGMemory = async (ctx) => {
|
|
|
20673
20684
|
executionTimeMs: output.metadata?.executionTimeMs || 0
|
|
20674
20685
|
}, {}, pool);
|
|
20675
20686
|
} catch (error) {
|
|
20676
|
-
|
|
20687
|
+
logger9.error("Error in tool.execute.after:", error);
|
|
20677
20688
|
}
|
|
20678
20689
|
},
|
|
20679
20690
|
"experimental.chat.system.transform": async (input, output) => {
|
|
@@ -20703,7 +20714,7 @@ Call this AFTER completing significant work to extract reusable patterns.
|
|
|
20703
20714
|
- When you need historical context about a specific topic → recall_memory(topic_segment_id=<id>)
|
|
20704
20715
|
`);
|
|
20705
20716
|
} catch (error) {
|
|
20706
|
-
|
|
20717
|
+
logger9.error("Error in system.transform:", error);
|
|
20707
20718
|
}
|
|
20708
20719
|
},
|
|
20709
20720
|
"experimental.session.compacting": async (input, output) => {
|
|
@@ -20736,7 +20747,7 @@ Call this AFTER completing significant work to extract reusable patterns.
|
|
|
20736
20747
|
}, 500);
|
|
20737
20748
|
}
|
|
20738
20749
|
} catch (error) {
|
|
20739
|
-
|
|
20750
|
+
logger9.error("Error in experimental.session.compacting:", error);
|
|
20740
20751
|
}
|
|
20741
20752
|
},
|
|
20742
20753
|
tool: {
|