mcp-agents-memory 0.9.1 → 0.9.4
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/build/index.js +844 -541
- package/build/migrations/005_provenance.js +1 -1
- package/build/migrations/006_canonical_validation.js +1 -1
- package/build/migrations/007_seed_real_models.js +1 -1
- package/build/migrations/008_schema_realignment.js +1 -1
- package/build/migrations/009_skills_tables.js +1 -1
- package/build/migrations/010_subject_relationships.js +1 -1
- package/build/migrations/011_memories_metadata.js +1 -1
- package/build/migrations/012_memory_sources.js +1 -1
- package/build/migrations/013_refresh_models.js +1 -1
- package/build/migrations/014_drop_legacy_facts_constraints.js +1 -1
- package/build/migrations/015_agent_provenance.js +1 -1
- package/build/migrations/016_agent_curator.js +1 -1
- package/build/migrations/017_drop_trust_weight.js +1 -1
- package/build/migrations/018_transcript_queue.js +1 -1
- package/build/migrations/019_respec_fresh_v1.js +1 -1
- package/build/migrations/020_tag_processed_and_external_uuid.js +1 -1
- package/build/migrations/021_agent_model_nullable.js +1 -1
- package/build/migrations/022_device_name.js +25989 -0
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -1655,11 +1655,11 @@ var require_pg_connection_string = __commonJS({
|
|
|
1655
1655
|
config2.client_encoding = result.searchParams.get("encoding");
|
|
1656
1656
|
return config2;
|
|
1657
1657
|
}
|
|
1658
|
-
const
|
|
1658
|
+
const hostname8 = dummyHost ? "" : result.hostname;
|
|
1659
1659
|
if (!config2.host) {
|
|
1660
|
-
config2.host = decodeURIComponent(
|
|
1661
|
-
} else if (
|
|
1662
|
-
result.pathname =
|
|
1660
|
+
config2.host = decodeURIComponent(hostname8);
|
|
1661
|
+
} else if (hostname8 && /^%2f/i.test(hostname8)) {
|
|
1662
|
+
result.pathname = hostname8 + result.pathname;
|
|
1663
1663
|
}
|
|
1664
1664
|
if (!config2.port) {
|
|
1665
1665
|
config2.port = result.port;
|
|
@@ -3079,7 +3079,7 @@ var require_dist = __commonJS({
|
|
|
3079
3079
|
function parse3(stream, callback) {
|
|
3080
3080
|
const parser = new parser_1.Parser();
|
|
3081
3081
|
stream.on("data", (buffer) => parser.parse(buffer, callback));
|
|
3082
|
-
return new Promise((
|
|
3082
|
+
return new Promise((resolve) => stream.on("end", () => resolve()));
|
|
3083
3083
|
}
|
|
3084
3084
|
exports.parse = parse3;
|
|
3085
3085
|
}
|
|
@@ -3810,12 +3810,12 @@ var require_client = __commonJS({
|
|
|
3810
3810
|
this._connect(callback);
|
|
3811
3811
|
return;
|
|
3812
3812
|
}
|
|
3813
|
-
return new this._Promise((
|
|
3813
|
+
return new this._Promise((resolve, reject) => {
|
|
3814
3814
|
this._connect((error2) => {
|
|
3815
3815
|
if (error2) {
|
|
3816
3816
|
reject(error2);
|
|
3817
3817
|
} else {
|
|
3818
|
-
|
|
3818
|
+
resolve(this);
|
|
3819
3819
|
}
|
|
3820
3820
|
});
|
|
3821
3821
|
});
|
|
@@ -4161,8 +4161,8 @@ var require_client = __commonJS({
|
|
|
4161
4161
|
readTimeout = config2.query_timeout || this.connectionParameters.query_timeout;
|
|
4162
4162
|
query = new Query2(config2, values, callback);
|
|
4163
4163
|
if (!query.callback) {
|
|
4164
|
-
result = new this._Promise((
|
|
4165
|
-
query.callback = (err2, res) => err2 ? reject(err2) :
|
|
4164
|
+
result = new this._Promise((resolve, reject) => {
|
|
4165
|
+
query.callback = (err2, res) => err2 ? reject(err2) : resolve(res);
|
|
4166
4166
|
}).catch((err2) => {
|
|
4167
4167
|
Error.captureStackTrace(err2);
|
|
4168
4168
|
throw err2;
|
|
@@ -4239,8 +4239,8 @@ var require_client = __commonJS({
|
|
|
4239
4239
|
if (cb) {
|
|
4240
4240
|
this.connection.once("end", cb);
|
|
4241
4241
|
} else {
|
|
4242
|
-
return new this._Promise((
|
|
4243
|
-
this.connection.once("end",
|
|
4242
|
+
return new this._Promise((resolve) => {
|
|
4243
|
+
this.connection.once("end", resolve);
|
|
4244
4244
|
});
|
|
4245
4245
|
}
|
|
4246
4246
|
}
|
|
@@ -4289,8 +4289,8 @@ var require_pg_pool = __commonJS({
|
|
|
4289
4289
|
const cb = function(err2, client2) {
|
|
4290
4290
|
err2 ? rej(err2) : res(client2);
|
|
4291
4291
|
};
|
|
4292
|
-
const result = new Promise2(function(
|
|
4293
|
-
res =
|
|
4292
|
+
const result = new Promise2(function(resolve, reject) {
|
|
4293
|
+
res = resolve;
|
|
4294
4294
|
rej = reject;
|
|
4295
4295
|
}).catch((err2) => {
|
|
4296
4296
|
Error.captureStackTrace(err2);
|
|
@@ -4351,7 +4351,7 @@ var require_pg_pool = __commonJS({
|
|
|
4351
4351
|
if (typeof Promise2.try === "function") {
|
|
4352
4352
|
return Promise2.try(f);
|
|
4353
4353
|
}
|
|
4354
|
-
return new Promise2((
|
|
4354
|
+
return new Promise2((resolve) => resolve(f()));
|
|
4355
4355
|
}
|
|
4356
4356
|
_isFull() {
|
|
4357
4357
|
return this._clients.length >= this.options.max;
|
|
@@ -4744,8 +4744,8 @@ var require_query2 = __commonJS({
|
|
|
4744
4744
|
NativeQuery.prototype._getPromise = function() {
|
|
4745
4745
|
if (this._promise) return this._promise;
|
|
4746
4746
|
this._promise = new Promise(
|
|
4747
|
-
function(
|
|
4748
|
-
this._once("end",
|
|
4747
|
+
function(resolve, reject) {
|
|
4748
|
+
this._once("end", resolve);
|
|
4749
4749
|
this._once("error", reject);
|
|
4750
4750
|
}.bind(this)
|
|
4751
4751
|
);
|
|
@@ -4922,12 +4922,12 @@ var require_client2 = __commonJS({
|
|
|
4922
4922
|
this._connect(callback);
|
|
4923
4923
|
return;
|
|
4924
4924
|
}
|
|
4925
|
-
return new this._Promise((
|
|
4925
|
+
return new this._Promise((resolve, reject) => {
|
|
4926
4926
|
this._connect((error2) => {
|
|
4927
4927
|
if (error2) {
|
|
4928
4928
|
reject(error2);
|
|
4929
4929
|
} else {
|
|
4930
|
-
|
|
4930
|
+
resolve(this);
|
|
4931
4931
|
}
|
|
4932
4932
|
});
|
|
4933
4933
|
});
|
|
@@ -4951,8 +4951,8 @@ var require_client2 = __commonJS({
|
|
|
4951
4951
|
query = new NativeQuery(config2, values, callback);
|
|
4952
4952
|
if (!query.callback) {
|
|
4953
4953
|
let resolveOut, rejectOut;
|
|
4954
|
-
result = new this._Promise((
|
|
4955
|
-
resolveOut =
|
|
4954
|
+
result = new this._Promise((resolve, reject) => {
|
|
4955
|
+
resolveOut = resolve;
|
|
4956
4956
|
rejectOut = reject;
|
|
4957
4957
|
}).catch((err2) => {
|
|
4958
4958
|
Error.captureStackTrace(err2);
|
|
@@ -5012,8 +5012,8 @@ var require_client2 = __commonJS({
|
|
|
5012
5012
|
}
|
|
5013
5013
|
let result;
|
|
5014
5014
|
if (!cb) {
|
|
5015
|
-
result = new this._Promise(function(
|
|
5016
|
-
cb = (err2) => err2 ? reject(err2) :
|
|
5015
|
+
result = new this._Promise(function(resolve, reject) {
|
|
5016
|
+
cb = (err2) => err2 ? reject(err2) : resolve();
|
|
5017
5017
|
});
|
|
5018
5018
|
}
|
|
5019
5019
|
this.native.end(function() {
|
|
@@ -11652,7 +11652,7 @@ var require_crypto = __commonJS({
|
|
|
11652
11652
|
MAC_INFO,
|
|
11653
11653
|
bindingAvailable: !!binding,
|
|
11654
11654
|
init: (() => {
|
|
11655
|
-
return new Promise(async (
|
|
11655
|
+
return new Promise(async (resolve, reject) => {
|
|
11656
11656
|
try {
|
|
11657
11657
|
POLY1305_WASM_MODULE = await require_poly1305()();
|
|
11658
11658
|
POLY1305_RESULT_MALLOC = POLY1305_WASM_MODULE._malloc(16);
|
|
@@ -11664,7 +11664,7 @@ var require_crypto = __commonJS({
|
|
|
11664
11664
|
} catch (ex) {
|
|
11665
11665
|
return reject(ex);
|
|
11666
11666
|
}
|
|
11667
|
-
|
|
11667
|
+
resolve();
|
|
11668
11668
|
});
|
|
11669
11669
|
})(),
|
|
11670
11670
|
NullCipher,
|
|
@@ -12922,7 +12922,7 @@ var require_agent = __commonJS({
|
|
|
12922
12922
|
"use strict";
|
|
12923
12923
|
var { Socket } = __require("net");
|
|
12924
12924
|
var { Duplex } = __require("stream");
|
|
12925
|
-
var { resolve
|
|
12925
|
+
var { resolve } = __require("path");
|
|
12926
12926
|
var { readFile } = __require("fs");
|
|
12927
12927
|
var { execFile, spawn: spawn2 } = __require("child_process");
|
|
12928
12928
|
var { isParsedKey, parseKey } = require_keyParser();
|
|
@@ -13058,7 +13058,7 @@ var require_agent = __commonJS({
|
|
|
13058
13058
|
const RET_ERR_BINSTDIN = 13;
|
|
13059
13059
|
const RET_ERR_BINSTDOUT = 14;
|
|
13060
13060
|
const RET_ERR_BADLEN = 15;
|
|
13061
|
-
const EXEPATH =
|
|
13061
|
+
const EXEPATH = resolve(__dirname, "..", "util/pagent.exe");
|
|
13062
13062
|
const ERROR = {
|
|
13063
13063
|
[RET_ERR_BADARGS]: new Error("Invalid pagent.exe arguments"),
|
|
13064
13064
|
[RET_ERR_UNAVAILABLE]: new Error("Pageant is not running"),
|
|
@@ -17103,7 +17103,7 @@ var require_Protocol = __commonJS({
|
|
|
17103
17103
|
sendPacket(this, this._packetRW.write.finalize(packet));
|
|
17104
17104
|
});
|
|
17105
17105
|
}
|
|
17106
|
-
authHostbased(username, pubKey,
|
|
17106
|
+
authHostbased(username, pubKey, hostname8, userlocal, keyAlgo, cbSign) {
|
|
17107
17107
|
if (this._server)
|
|
17108
17108
|
throw new Error("Client-only method called in server mode");
|
|
17109
17109
|
pubKey = parseKey(pubKey);
|
|
@@ -17122,7 +17122,7 @@ var require_Protocol = __commonJS({
|
|
|
17122
17122
|
const pubKeyLen = pubKey.length;
|
|
17123
17123
|
const sessionID = this._kex.sessionID;
|
|
17124
17124
|
const sesLen = sessionID.length;
|
|
17125
|
-
const hostnameLen = Buffer.byteLength(
|
|
17125
|
+
const hostnameLen = Buffer.byteLength(hostname8);
|
|
17126
17126
|
const userlocalLen = Buffer.byteLength(userlocal);
|
|
17127
17127
|
const data = Buffer.allocUnsafe(
|
|
17128
17128
|
4 + sesLen + 1 + 4 + userLen + 4 + 14 + 4 + 9 + 4 + algoLen + 4 + pubKeyLen + 4 + hostnameLen + 4 + userlocalLen
|
|
@@ -17142,7 +17142,7 @@ var require_Protocol = __commonJS({
|
|
|
17142
17142
|
writeUInt32BE(data, pubKeyLen, p += algoLen);
|
|
17143
17143
|
data.set(pubKey, p += 4);
|
|
17144
17144
|
writeUInt32BE(data, hostnameLen, p += pubKeyLen);
|
|
17145
|
-
data.utf8Write(
|
|
17145
|
+
data.utf8Write(hostname8, p += 4, hostnameLen);
|
|
17146
17146
|
writeUInt32BE(data, userlocalLen, p += hostnameLen);
|
|
17147
17147
|
data.utf8Write(userlocal, p += 4, userlocalLen);
|
|
17148
17148
|
cbSign(data, (signature) => {
|
|
@@ -25300,7 +25300,7 @@ var require_tunnel_ssh = __commonJS({
|
|
|
25300
25300
|
"node_modules/tunnel-ssh/index.js"(exports) {
|
|
25301
25301
|
var net = __require("net");
|
|
25302
25302
|
var { Client: Client2 } = require_lib4();
|
|
25303
|
-
var
|
|
25303
|
+
var os8 = __require("os");
|
|
25304
25304
|
function autoClose(server, connection) {
|
|
25305
25305
|
connection.on("close", () => {
|
|
25306
25306
|
server.getConnections((error2, count) => {
|
|
@@ -25315,7 +25315,7 @@ var require_tunnel_ssh = __commonJS({
|
|
|
25315
25315
|
if (!serverOptions.port && !serverOptions.path) {
|
|
25316
25316
|
serverOptions = null;
|
|
25317
25317
|
}
|
|
25318
|
-
return new Promise((
|
|
25318
|
+
return new Promise((resolve, reject) => {
|
|
25319
25319
|
let server = net.createServer();
|
|
25320
25320
|
let errorHandler = function(error2) {
|
|
25321
25321
|
reject(error2);
|
|
@@ -25325,14 +25325,14 @@ var require_tunnel_ssh = __commonJS({
|
|
|
25325
25325
|
server.listen(serverOptions);
|
|
25326
25326
|
server.on("listening", () => {
|
|
25327
25327
|
process.removeListener("uncaughtException", errorHandler);
|
|
25328
|
-
|
|
25328
|
+
resolve(server);
|
|
25329
25329
|
});
|
|
25330
25330
|
});
|
|
25331
25331
|
}
|
|
25332
25332
|
async function createSSHConnection(config2) {
|
|
25333
|
-
return new Promise(function(
|
|
25333
|
+
return new Promise(function(resolve, reject) {
|
|
25334
25334
|
let conn = new Client2();
|
|
25335
|
-
conn.on("ready", () =>
|
|
25335
|
+
conn.on("ready", () => resolve(conn));
|
|
25336
25336
|
conn.on("error", reject);
|
|
25337
25337
|
conn.connect(config2);
|
|
25338
25338
|
});
|
|
@@ -25342,7 +25342,7 @@ var require_tunnel_ssh = __commonJS({
|
|
|
25342
25342
|
let forwardOptionsLocal = Object.assign({ dstAddr: "0.0.0.0" }, forwardOptions);
|
|
25343
25343
|
let tunnelOptionsLocal = Object.assign({ autoClose: false, reconnectOnError: false }, tunnelOptions || {});
|
|
25344
25344
|
let server, sshConnection;
|
|
25345
|
-
return new Promise(async function(
|
|
25345
|
+
return new Promise(async function(resolve, reject) {
|
|
25346
25346
|
try {
|
|
25347
25347
|
sshConnection = await createSSHConnection(sshOptionslocal);
|
|
25348
25348
|
addListenerSshConnection(sshConnection);
|
|
@@ -25407,7 +25407,7 @@ var require_tunnel_ssh = __commonJS({
|
|
|
25407
25407
|
}
|
|
25408
25408
|
);
|
|
25409
25409
|
}
|
|
25410
|
-
|
|
25410
|
+
resolve([server, sshConnection]);
|
|
25411
25411
|
});
|
|
25412
25412
|
}
|
|
25413
25413
|
exports.createTunnel = createTunnel2;
|
|
@@ -25487,7 +25487,7 @@ var require_main = __commonJS({
|
|
|
25487
25487
|
"node_modules/dotenv/lib/main.js"(exports, module) {
|
|
25488
25488
|
var fs8 = __require("fs");
|
|
25489
25489
|
var path8 = __require("path");
|
|
25490
|
-
var
|
|
25490
|
+
var os8 = __require("os");
|
|
25491
25491
|
var crypto3 = __require("crypto");
|
|
25492
25492
|
var packageJson2 = require_package2();
|
|
25493
25493
|
var version2 = packageJson2.version;
|
|
@@ -25610,7 +25610,7 @@ var require_main = __commonJS({
|
|
|
25610
25610
|
return null;
|
|
25611
25611
|
}
|
|
25612
25612
|
function _resolveHome(envPath) {
|
|
25613
|
-
return envPath[0] === "~" ? path8.join(
|
|
25613
|
+
return envPath[0] === "~" ? path8.join(os8.homedir(), envPath.slice(1)) : envPath;
|
|
25614
25614
|
}
|
|
25615
25615
|
function _configVault(options) {
|
|
25616
25616
|
const debug = Boolean(options && options.debug);
|
|
@@ -25788,8 +25788,8 @@ import { fileURLToPath } from "url";
|
|
|
25788
25788
|
function configSearchPaths() {
|
|
25789
25789
|
const paths = [];
|
|
25790
25790
|
if (process.env.MEMORY_CONFIG_PATH) paths.push(process.env.MEMORY_CONFIG_PATH);
|
|
25791
|
-
paths.push(path.resolve(process.cwd(), ".env"));
|
|
25792
25791
|
paths.push(path.join(os.homedir(), ".config", "mcp-agents-memory", ".env"));
|
|
25792
|
+
paths.push(path.resolve(process.cwd(), ".env"));
|
|
25793
25793
|
paths.push(path.resolve(__dirname2, "..", ".env"));
|
|
25794
25794
|
return paths;
|
|
25795
25795
|
}
|
|
@@ -25882,7 +25882,7 @@ var init_db = __esm({
|
|
|
25882
25882
|
dstAddr: process.env.DB_HOST || "localhost",
|
|
25883
25883
|
dstPort: parseInt(process.env.DB_PORT || "5432")
|
|
25884
25884
|
};
|
|
25885
|
-
return new Promise((
|
|
25885
|
+
return new Promise((resolve, reject) => {
|
|
25886
25886
|
(0, import_tunnel_ssh.createTunnel)(tunnelOptions, serverOptions, sshOptions, forwardOptions).then(async ([server, conn]) => {
|
|
25887
25887
|
this.tunnelServer = server;
|
|
25888
25888
|
this.tunnelConn = conn;
|
|
@@ -25903,7 +25903,7 @@ var init_db = __esm({
|
|
|
25903
25903
|
await pool2.query("SELECT 1");
|
|
25904
25904
|
console.error("\u2705 Database connection verified.");
|
|
25905
25905
|
this.pool = pool2;
|
|
25906
|
-
|
|
25906
|
+
resolve(pool2);
|
|
25907
25907
|
} catch (err2) {
|
|
25908
25908
|
console.error("\u274C Database connection probe failed:", err2);
|
|
25909
25909
|
await pool2.end().catch(() => {
|
|
@@ -25955,11 +25955,11 @@ var init_db = __esm({
|
|
|
25955
25955
|
this.tunnelConn = null;
|
|
25956
25956
|
}
|
|
25957
25957
|
if (this.tunnelServer) {
|
|
25958
|
-
await new Promise((
|
|
25958
|
+
await new Promise((resolve) => {
|
|
25959
25959
|
this.tunnelServer.close(() => {
|
|
25960
25960
|
this.tunnelServer = null;
|
|
25961
25961
|
console.error("\u{1F512} SSH Tunnel closed.");
|
|
25962
|
-
|
|
25962
|
+
resolve();
|
|
25963
25963
|
});
|
|
25964
25964
|
});
|
|
25965
25965
|
}
|
|
@@ -28920,7 +28920,7 @@ var require_compile = __commonJS({
|
|
|
28920
28920
|
const schOrFunc = root.refs[ref];
|
|
28921
28921
|
if (schOrFunc)
|
|
28922
28922
|
return schOrFunc;
|
|
28923
|
-
let _sch =
|
|
28923
|
+
let _sch = resolve.call(this, root, ref);
|
|
28924
28924
|
if (_sch === void 0) {
|
|
28925
28925
|
const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
|
|
28926
28926
|
const { schemaId } = this.opts;
|
|
@@ -28947,7 +28947,7 @@ var require_compile = __commonJS({
|
|
|
28947
28947
|
function sameSchemaEnv(s1, s2) {
|
|
28948
28948
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
28949
28949
|
}
|
|
28950
|
-
function
|
|
28950
|
+
function resolve(root, ref) {
|
|
28951
28951
|
let sch;
|
|
28952
28952
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
28953
28953
|
ref = sch;
|
|
@@ -29522,7 +29522,7 @@ var require_fast_uri = __commonJS({
|
|
|
29522
29522
|
}
|
|
29523
29523
|
return uri;
|
|
29524
29524
|
}
|
|
29525
|
-
function
|
|
29525
|
+
function resolve(baseURI, relativeURI, options) {
|
|
29526
29526
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
29527
29527
|
const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
29528
29528
|
schemelessOptions.skipEscape = true;
|
|
@@ -29749,7 +29749,7 @@ var require_fast_uri = __commonJS({
|
|
|
29749
29749
|
var fastUri = {
|
|
29750
29750
|
SCHEMES,
|
|
29751
29751
|
normalize,
|
|
29752
|
-
resolve
|
|
29752
|
+
resolve,
|
|
29753
29753
|
resolveComponent,
|
|
29754
29754
|
equal,
|
|
29755
29755
|
serialize,
|
|
@@ -32777,13 +32777,13 @@ function listMigrationFiles() {
|
|
|
32777
32777
|
return fs6.readdirSync(migrationsDir).filter((f) => MIGRATION_FILE_RE.test(f)).sort();
|
|
32778
32778
|
}
|
|
32779
32779
|
function runOne(file) {
|
|
32780
|
-
return new Promise((
|
|
32780
|
+
return new Promise((resolve, reject) => {
|
|
32781
32781
|
const child = spawn(process.execPath, [file], {
|
|
32782
32782
|
stdio: "inherit",
|
|
32783
32783
|
env: process.env
|
|
32784
32784
|
});
|
|
32785
32785
|
child.on("exit", (code) => {
|
|
32786
|
-
if (code === 0) return
|
|
32786
|
+
if (code === 0) return resolve();
|
|
32787
32787
|
reject(new Error(`Migration ${path7.basename(file)} exited with code ${code}`));
|
|
32788
32788
|
});
|
|
32789
32789
|
child.on("error", reject);
|
|
@@ -44906,7 +44906,7 @@ var Protocol = class {
|
|
|
44906
44906
|
return;
|
|
44907
44907
|
}
|
|
44908
44908
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
44909
|
-
await new Promise((
|
|
44909
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
44910
44910
|
options?.signal?.throwIfAborted();
|
|
44911
44911
|
}
|
|
44912
44912
|
} catch (error2) {
|
|
@@ -44923,7 +44923,7 @@ var Protocol = class {
|
|
|
44923
44923
|
*/
|
|
44924
44924
|
request(request, resultSchema, options) {
|
|
44925
44925
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
44926
|
-
return new Promise((
|
|
44926
|
+
return new Promise((resolve, reject) => {
|
|
44927
44927
|
const earlyReject = (error2) => {
|
|
44928
44928
|
reject(error2);
|
|
44929
44929
|
};
|
|
@@ -45001,7 +45001,7 @@ var Protocol = class {
|
|
|
45001
45001
|
if (!parseResult.success) {
|
|
45002
45002
|
reject(parseResult.error);
|
|
45003
45003
|
} else {
|
|
45004
|
-
|
|
45004
|
+
resolve(parseResult.data);
|
|
45005
45005
|
}
|
|
45006
45006
|
} catch (error2) {
|
|
45007
45007
|
reject(error2);
|
|
@@ -45262,12 +45262,12 @@ var Protocol = class {
|
|
|
45262
45262
|
}
|
|
45263
45263
|
} catch {
|
|
45264
45264
|
}
|
|
45265
|
-
return new Promise((
|
|
45265
|
+
return new Promise((resolve, reject) => {
|
|
45266
45266
|
if (signal.aborted) {
|
|
45267
45267
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
45268
45268
|
return;
|
|
45269
45269
|
}
|
|
45270
|
-
const timeoutId = setTimeout(
|
|
45270
|
+
const timeoutId = setTimeout(resolve, interval);
|
|
45271
45271
|
signal.addEventListener("abort", () => {
|
|
45272
45272
|
clearTimeout(timeoutId);
|
|
45273
45273
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -46367,7 +46367,7 @@ var McpServer = class {
|
|
|
46367
46367
|
let task = createTaskResult.task;
|
|
46368
46368
|
const pollInterval = task.pollInterval ?? 5e3;
|
|
46369
46369
|
while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
|
|
46370
|
-
await new Promise((
|
|
46370
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
46371
46371
|
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
46372
46372
|
if (!updatedTask) {
|
|
46373
46373
|
throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
@@ -47016,12 +47016,12 @@ var StdioServerTransport = class {
|
|
|
47016
47016
|
this.onclose?.();
|
|
47017
47017
|
}
|
|
47018
47018
|
send(message) {
|
|
47019
|
-
return new Promise((
|
|
47019
|
+
return new Promise((resolve) => {
|
|
47020
47020
|
const json = serializeMessage(message);
|
|
47021
47021
|
if (this._stdout.write(json)) {
|
|
47022
|
-
|
|
47022
|
+
resolve();
|
|
47023
47023
|
} else {
|
|
47024
|
-
this._stdout.once("drain",
|
|
47024
|
+
this._stdout.once("drain", resolve);
|
|
47025
47025
|
}
|
|
47026
47026
|
});
|
|
47027
47027
|
}
|
|
@@ -47029,6 +47029,7 @@ var StdioServerTransport = class {
|
|
|
47029
47029
|
|
|
47030
47030
|
// src/tools/manage_knowledge.ts
|
|
47031
47031
|
init_db();
|
|
47032
|
+
import * as os2 from "node:os";
|
|
47032
47033
|
|
|
47033
47034
|
// src/hot_path.ts
|
|
47034
47035
|
init_db();
|
|
@@ -47046,7 +47047,8 @@ async function insertRawMemory(params) {
|
|
|
47046
47047
|
p_tag_id = null,
|
|
47047
47048
|
d_tag = [],
|
|
47048
47049
|
embedding = null,
|
|
47049
|
-
external_uuid = null
|
|
47050
|
+
external_uuid = null,
|
|
47051
|
+
device_name = null
|
|
47050
47052
|
} = params;
|
|
47051
47053
|
const embeddingSql = embedding && embedding.length > 0 ? `[${embedding.join(",")}]` : null;
|
|
47052
47054
|
const tagProcessed = p_tag_id !== null;
|
|
@@ -47056,13 +47058,15 @@ async function insertRawMemory(params) {
|
|
|
47056
47058
|
subagent, subagent_model, subagent_role,
|
|
47057
47059
|
role, message,
|
|
47058
47060
|
p_tag_id, d_tag, embedding,
|
|
47059
|
-
is_pinned, tag_processed, external_uuid
|
|
47061
|
+
is_pinned, tag_processed, external_uuid,
|
|
47062
|
+
device_name
|
|
47060
47063
|
) VALUES (
|
|
47061
47064
|
$1, $2, $3,
|
|
47062
47065
|
$4, $5, $6,
|
|
47063
47066
|
$7, $8,
|
|
47064
47067
|
$9, $10::text[], $11::halfvec,
|
|
47065
|
-
$12, $13, $14
|
|
47068
|
+
$12, $13, $14,
|
|
47069
|
+
$15
|
|
47066
47070
|
)
|
|
47067
47071
|
ON CONFLICT (external_uuid) WHERE external_uuid IS NOT NULL
|
|
47068
47072
|
DO NOTHING
|
|
@@ -47081,7 +47085,8 @@ async function insertRawMemory(params) {
|
|
|
47081
47085
|
embeddingSql,
|
|
47082
47086
|
is_pinned,
|
|
47083
47087
|
tagProcessed,
|
|
47084
|
-
external_uuid
|
|
47088
|
+
external_uuid,
|
|
47089
|
+
device_name
|
|
47085
47090
|
]
|
|
47086
47091
|
);
|
|
47087
47092
|
if (result.rows.length === 0) {
|
|
@@ -47388,7 +47393,7 @@ var safeJSON = (text) => {
|
|
|
47388
47393
|
};
|
|
47389
47394
|
|
|
47390
47395
|
// node_modules/openai/internal/utils/sleep.mjs
|
|
47391
|
-
var sleep = (ms) => new Promise((
|
|
47396
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
47392
47397
|
|
|
47393
47398
|
// node_modules/openai/version.mjs
|
|
47394
47399
|
var VERSION = "6.34.0";
|
|
@@ -48467,8 +48472,8 @@ function addRequestID(value, response) {
|
|
|
48467
48472
|
var _APIPromise_client;
|
|
48468
48473
|
var APIPromise = class _APIPromise extends Promise {
|
|
48469
48474
|
constructor(client2, responsePromise, parseResponse2 = defaultParseResponse) {
|
|
48470
|
-
super((
|
|
48471
|
-
|
|
48475
|
+
super((resolve) => {
|
|
48476
|
+
resolve(null);
|
|
48472
48477
|
});
|
|
48473
48478
|
this.responsePromise = responsePromise;
|
|
48474
48479
|
this.parseResponse = parseResponse2;
|
|
@@ -49116,12 +49121,12 @@ var EventStream = class {
|
|
|
49116
49121
|
_EventStream_errored.set(this, false);
|
|
49117
49122
|
_EventStream_aborted.set(this, false);
|
|
49118
49123
|
_EventStream_catchingPromiseCreated.set(this, false);
|
|
49119
|
-
__classPrivateFieldSet(this, _EventStream_connectedPromise, new Promise((
|
|
49120
|
-
__classPrivateFieldSet(this, _EventStream_resolveConnectedPromise,
|
|
49124
|
+
__classPrivateFieldSet(this, _EventStream_connectedPromise, new Promise((resolve, reject) => {
|
|
49125
|
+
__classPrivateFieldSet(this, _EventStream_resolveConnectedPromise, resolve, "f");
|
|
49121
49126
|
__classPrivateFieldSet(this, _EventStream_rejectConnectedPromise, reject, "f");
|
|
49122
49127
|
}), "f");
|
|
49123
|
-
__classPrivateFieldSet(this, _EventStream_endPromise, new Promise((
|
|
49124
|
-
__classPrivateFieldSet(this, _EventStream_resolveEndPromise,
|
|
49128
|
+
__classPrivateFieldSet(this, _EventStream_endPromise, new Promise((resolve, reject) => {
|
|
49129
|
+
__classPrivateFieldSet(this, _EventStream_resolveEndPromise, resolve, "f");
|
|
49125
49130
|
__classPrivateFieldSet(this, _EventStream_rejectEndPromise, reject, "f");
|
|
49126
49131
|
}), "f");
|
|
49127
49132
|
__classPrivateFieldGet(this, _EventStream_connectedPromise, "f").catch(() => {
|
|
@@ -49205,11 +49210,11 @@ var EventStream = class {
|
|
|
49205
49210
|
* const message = await stream.emitted('message') // rejects if the stream errors
|
|
49206
49211
|
*/
|
|
49207
49212
|
emitted(event) {
|
|
49208
|
-
return new Promise((
|
|
49213
|
+
return new Promise((resolve, reject) => {
|
|
49209
49214
|
__classPrivateFieldSet(this, _EventStream_catchingPromiseCreated, true, "f");
|
|
49210
49215
|
if (event !== "error")
|
|
49211
49216
|
this.once("error", reject);
|
|
49212
|
-
this.once(event,
|
|
49217
|
+
this.once(event, resolve);
|
|
49213
49218
|
});
|
|
49214
49219
|
}
|
|
49215
49220
|
async done() {
|
|
@@ -50148,7 +50153,7 @@ var ChatCompletionStream = class _ChatCompletionStream extends AbstractChatCompl
|
|
|
50148
50153
|
if (done) {
|
|
50149
50154
|
return { value: void 0, done: true };
|
|
50150
50155
|
}
|
|
50151
|
-
return new Promise((
|
|
50156
|
+
return new Promise((resolve, reject) => readQueue.push({ resolve, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
|
|
50152
50157
|
}
|
|
50153
50158
|
const chunk = pushQueue.shift();
|
|
50154
50159
|
return { value: chunk, done: false };
|
|
@@ -50980,7 +50985,7 @@ var AssistantStream = class extends EventStream {
|
|
|
50980
50985
|
if (done) {
|
|
50981
50986
|
return { value: void 0, done: true };
|
|
50982
50987
|
}
|
|
50983
|
-
return new Promise((
|
|
50988
|
+
return new Promise((resolve, reject) => readQueue.push({ resolve, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
|
|
50984
50989
|
}
|
|
50985
50990
|
const chunk = pushQueue.shift();
|
|
50986
50991
|
return { value: chunk, done: false };
|
|
@@ -52929,7 +52934,7 @@ var ResponseStream = class _ResponseStream extends EventStream {
|
|
|
52929
52934
|
if (done) {
|
|
52930
52935
|
return { value: void 0, done: true };
|
|
52931
52936
|
}
|
|
52932
|
-
return new Promise((
|
|
52937
|
+
return new Promise((resolve, reject) => readQueue.push({ resolve, reject })).then((event2) => event2 ? { value: event2, done: false } : { value: void 0, done: true });
|
|
52933
52938
|
}
|
|
52934
52939
|
const event = pushQueue.shift();
|
|
52935
52940
|
return { value: event, done: false };
|
|
@@ -55330,8 +55335,9 @@ function inferProvider(modelName) {
|
|
|
55330
55335
|
return null;
|
|
55331
55336
|
}
|
|
55332
55337
|
var DEFAULTS = {
|
|
55333
|
-
tagger: { provider: "
|
|
55334
|
-
librarian: { provider: "
|
|
55338
|
+
tagger: { provider: "xai", model_name: "grok-4-1-fast-non-reasoning" },
|
|
55339
|
+
librarian: { provider: "xai", model_name: "grok-4-1-fast-non-reasoning" },
|
|
55340
|
+
clusterer: { provider: "xai", model_name: "grok-4-1-fast-non-reasoning" }
|
|
55335
55341
|
};
|
|
55336
55342
|
function envEnvelope(role) {
|
|
55337
55343
|
const upper = role.toUpperCase();
|
|
@@ -55357,7 +55363,8 @@ function envEnvelope(role) {
|
|
|
55357
55363
|
}
|
|
55358
55364
|
var ROLE_REGISTRY = {
|
|
55359
55365
|
tagger: envEnvelope("tagger"),
|
|
55360
|
-
librarian: envEnvelope("librarian")
|
|
55366
|
+
librarian: envEnvelope("librarian"),
|
|
55367
|
+
clusterer: envEnvelope("clusterer")
|
|
55361
55368
|
};
|
|
55362
55369
|
for (const [role, spec] of Object.entries(ROLE_REGISTRY)) {
|
|
55363
55370
|
try {
|
|
@@ -55627,6 +55634,7 @@ function resolveAgentIdentity(server, args) {
|
|
|
55627
55634
|
}
|
|
55628
55635
|
|
|
55629
55636
|
// src/tools/manage_knowledge.ts
|
|
55637
|
+
var DEVICE_NAME = os2.hostname();
|
|
55630
55638
|
function ok(payload) {
|
|
55631
55639
|
return {
|
|
55632
55640
|
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }]
|
|
@@ -55791,7 +55799,8 @@ ${args.content}` : args.content;
|
|
|
55791
55799
|
// 강제 기억은 archive 면제
|
|
55792
55800
|
p_tag_id,
|
|
55793
55801
|
d_tag,
|
|
55794
|
-
embedding
|
|
55802
|
+
embedding,
|
|
55803
|
+
device_name: DEVICE_NAME
|
|
55795
55804
|
});
|
|
55796
55805
|
return ok({
|
|
55797
55806
|
stored: true,
|
|
@@ -56094,6 +56103,7 @@ date_range \uC778\uC2DD \uD615\uC2DD:
|
|
|
56094
56103
|
|
|
56095
56104
|
// src/briefing.ts
|
|
56096
56105
|
init_db();
|
|
56106
|
+
import * as os3 from "node:os";
|
|
56097
56107
|
var RECENT_CURRENT_LIMIT = 8;
|
|
56098
56108
|
var RECENT_OTHERS_LIMIT = 4;
|
|
56099
56109
|
var RECENT_MESSAGE_PREVIEW = 100;
|
|
@@ -56125,7 +56135,7 @@ async function collectBrief(opts = {}) {
|
|
|
56125
56135
|
let recentOthers = [];
|
|
56126
56136
|
if (currentPlatform) {
|
|
56127
56137
|
const currentMsgs = await db.query(
|
|
56128
|
-
`SELECT role, agent_platform, message, created_at
|
|
56138
|
+
`SELECT role, agent_platform, device_name, message, created_at
|
|
56129
56139
|
FROM memory
|
|
56130
56140
|
WHERE user_id = $1
|
|
56131
56141
|
AND is_active = TRUE
|
|
@@ -56137,7 +56147,7 @@ async function collectBrief(opts = {}) {
|
|
|
56137
56147
|
);
|
|
56138
56148
|
recentCurrent = currentMsgs.rows.reverse().map(rowToMsg);
|
|
56139
56149
|
const othersMsgs = await db.query(
|
|
56140
|
-
`SELECT role, agent_platform, message, created_at
|
|
56150
|
+
`SELECT role, agent_platform, device_name, message, created_at
|
|
56141
56151
|
FROM memory
|
|
56142
56152
|
WHERE user_id = $1
|
|
56143
56153
|
AND is_active = TRUE
|
|
@@ -56150,7 +56160,7 @@ async function collectBrief(opts = {}) {
|
|
|
56150
56160
|
recentOthers = othersMsgs.rows.reverse().map(rowToMsg);
|
|
56151
56161
|
} else {
|
|
56152
56162
|
const msgs = await db.query(
|
|
56153
|
-
`SELECT role, agent_platform, message, created_at
|
|
56163
|
+
`SELECT role, agent_platform, device_name, message, created_at
|
|
56154
56164
|
FROM memory
|
|
56155
56165
|
WHERE user_id = $1
|
|
56156
56166
|
AND is_active = TRUE
|
|
@@ -56180,6 +56190,7 @@ function rowToMsg(r) {
|
|
|
56180
56190
|
return {
|
|
56181
56191
|
role: r.role,
|
|
56182
56192
|
agent_platform: r.agent_platform,
|
|
56193
|
+
device_name: r.device_name ?? null,
|
|
56183
56194
|
preview: String(r.message ?? "").slice(0, RECENT_MESSAGE_PREVIEW),
|
|
56184
56195
|
created_at: r.created_at
|
|
56185
56196
|
};
|
|
@@ -56188,7 +56199,7 @@ function formatBriefMarkdown(brief) {
|
|
|
56188
56199
|
const lines = [];
|
|
56189
56200
|
lines.push(`# Memory Briefing (user: ${brief.user_name})`);
|
|
56190
56201
|
if (brief.current_platform) {
|
|
56191
|
-
lines.push(`Current platform: \`${brief.current_platform}\``);
|
|
56202
|
+
lines.push(`Current platform: \`${brief.current_platform} @ ${os3.hostname()}\``);
|
|
56192
56203
|
}
|
|
56193
56204
|
lines.push("");
|
|
56194
56205
|
if (brief.core_profile) {
|
|
@@ -56234,7 +56245,8 @@ function formatBriefMarkdown(brief) {
|
|
|
56234
56245
|
}
|
|
56235
56246
|
function formatMsgLine(m, showPlatform) {
|
|
56236
56247
|
const dt = m.created_at?.toISOString?.().slice(11, 16) ?? "";
|
|
56237
|
-
const
|
|
56248
|
+
const device = m.device_name ? `@${m.device_name} ` : "";
|
|
56249
|
+
const platformTag = showPlatform ? `${m.agent_platform} ${device}` : device;
|
|
56238
56250
|
const truncated = m.preview.length >= RECENT_MESSAGE_PREVIEW ? "\u2026" : "";
|
|
56239
56251
|
return `- [${dt} ${platformTag}${m.role}] ${m.preview}${truncated}`;
|
|
56240
56252
|
}
|
|
@@ -56288,6 +56300,394 @@ agent_platform_filter=false.`,
|
|
|
56288
56300
|
}
|
|
56289
56301
|
|
|
56290
56302
|
// src/auto_save/save_message_tool.ts
|
|
56303
|
+
import * as os5 from "node:os";
|
|
56304
|
+
|
|
56305
|
+
// src/auto_save/gemini_capture.ts
|
|
56306
|
+
import * as fs2 from "node:fs";
|
|
56307
|
+
import * as path3 from "node:path";
|
|
56308
|
+
import * as os4 from "node:os";
|
|
56309
|
+
import * as crypto2 from "node:crypto";
|
|
56310
|
+
var CLIENT_PLATFORM = "gemini-cli-mcp-client";
|
|
56311
|
+
var DEVICE_NAME2 = os4.hostname();
|
|
56312
|
+
var GEMINI_TMP_ROOT = path3.join(os4.homedir(), ".gemini", "tmp");
|
|
56313
|
+
var FLUSH_DEBOUNCE_MS = 200;
|
|
56314
|
+
var PARSE_RETRY_MS = 80;
|
|
56315
|
+
var POLL_INTERVAL_MS = 3e3;
|
|
56316
|
+
var _state = null;
|
|
56317
|
+
var _watchers = [];
|
|
56318
|
+
var _pollTimer = null;
|
|
56319
|
+
var _flushInProgress = false;
|
|
56320
|
+
var _flushPending = false;
|
|
56321
|
+
var _flushDebounceTimer = null;
|
|
56322
|
+
function extractShortId(filename) {
|
|
56323
|
+
const m = filename.match(/^session-.+?-([0-9a-f]+)\.json$/);
|
|
56324
|
+
return m ? m[1] : filename.replace(/\.json$/, "");
|
|
56325
|
+
}
|
|
56326
|
+
function deriveChatsDirCandidates(cwd) {
|
|
56327
|
+
const cwdHash = crypto2.createHash("sha256").update(cwd).digest("hex");
|
|
56328
|
+
const candidates = [
|
|
56329
|
+
path3.join(GEMINI_TMP_ROOT, cwdHash, "chats"),
|
|
56330
|
+
path3.join(GEMINI_TMP_ROOT, path3.basename(cwd), "chats")
|
|
56331
|
+
];
|
|
56332
|
+
return candidates.filter((d) => fs2.existsSync(d));
|
|
56333
|
+
}
|
|
56334
|
+
async function readSessionFile(filePath) {
|
|
56335
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
56336
|
+
try {
|
|
56337
|
+
const raw = fs2.readFileSync(filePath, "utf-8");
|
|
56338
|
+
if (!raw.trim()) return null;
|
|
56339
|
+
return JSON.parse(raw);
|
|
56340
|
+
} catch {
|
|
56341
|
+
if (attempt === 0) {
|
|
56342
|
+
await new Promise((r) => setTimeout(r, PARSE_RETRY_MS));
|
|
56343
|
+
continue;
|
|
56344
|
+
}
|
|
56345
|
+
return null;
|
|
56346
|
+
}
|
|
56347
|
+
}
|
|
56348
|
+
return null;
|
|
56349
|
+
}
|
|
56350
|
+
function parseMessage(msg, fallbackId) {
|
|
56351
|
+
let role;
|
|
56352
|
+
if (msg.type === "user") role = "user";
|
|
56353
|
+
else if (msg.type === "gemini") role = "assistant";
|
|
56354
|
+
else return null;
|
|
56355
|
+
let text = "";
|
|
56356
|
+
const c = msg.content;
|
|
56357
|
+
if (typeof c === "string") {
|
|
56358
|
+
text = c;
|
|
56359
|
+
} else if (Array.isArray(c)) {
|
|
56360
|
+
for (const block of c) {
|
|
56361
|
+
if (block && typeof block === "object" && typeof block.text === "string") {
|
|
56362
|
+
text += (text ? "\n" : "") + block.text;
|
|
56363
|
+
}
|
|
56364
|
+
}
|
|
56365
|
+
}
|
|
56366
|
+
text = text.trim();
|
|
56367
|
+
if (!text) return null;
|
|
56368
|
+
if (text.startsWith("System:") || text === "Please continue.") return null;
|
|
56369
|
+
const msgId = typeof msg.id === "string" && msg.id ? msg.id : fallbackId;
|
|
56370
|
+
const agent_model = role === "assistant" && typeof msg.model === "string" ? msg.model : null;
|
|
56371
|
+
return { msgId, role, message: text, agent_model };
|
|
56372
|
+
}
|
|
56373
|
+
async function flushDeltaForFile(filePath, fileState, contentSeen) {
|
|
56374
|
+
return fileState.format === "jsonl" ? flushJsonlFile(filePath, fileState, contentSeen) : flushJsonFile(filePath, fileState, contentSeen);
|
|
56375
|
+
}
|
|
56376
|
+
async function flushJsonFile(filePath, fileState, contentSeen) {
|
|
56377
|
+
const session = await readSessionFile(filePath);
|
|
56378
|
+
if (!session || !Array.isArray(session.messages)) {
|
|
56379
|
+
return { inserted: 0, skipped: 0, dedup: 0 };
|
|
56380
|
+
}
|
|
56381
|
+
const messages = session.messages;
|
|
56382
|
+
if (messages.length <= fileState.cursor) {
|
|
56383
|
+
return { inserted: 0, skipped: 0, dedup: 0 };
|
|
56384
|
+
}
|
|
56385
|
+
if (typeof session.sessionId === "string" && session.sessionId) {
|
|
56386
|
+
fileState.sessionId = session.sessionId;
|
|
56387
|
+
}
|
|
56388
|
+
const userId = await getDefaultUserId();
|
|
56389
|
+
let inserted = 0, skipped = 0, dedup = 0;
|
|
56390
|
+
for (let i = fileState.cursor; i < messages.length; i++) {
|
|
56391
|
+
const msg = messages[i];
|
|
56392
|
+
const fallbackId = `${fileState.sessionId}-${i}`;
|
|
56393
|
+
const parsed = parseMessage(msg, fallbackId);
|
|
56394
|
+
if (!parsed) {
|
|
56395
|
+
skipped++;
|
|
56396
|
+
continue;
|
|
56397
|
+
}
|
|
56398
|
+
const ck = `${parsed.role}::${parsed.message}`;
|
|
56399
|
+
if (contentSeen.has(ck)) {
|
|
56400
|
+
dedup++;
|
|
56401
|
+
continue;
|
|
56402
|
+
}
|
|
56403
|
+
contentSeen.add(ck);
|
|
56404
|
+
const externalUuid = `gemini:${fileState.sessionId}:${parsed.msgId}`;
|
|
56405
|
+
const agentModel = parsed.role === "user" ? null : parsed.agent_model ?? "unknown";
|
|
56406
|
+
try {
|
|
56407
|
+
const result = await insertRawMemory({
|
|
56408
|
+
user_id: userId,
|
|
56409
|
+
agent_platform: CLIENT_PLATFORM,
|
|
56410
|
+
agent_model: agentModel,
|
|
56411
|
+
role: parsed.role,
|
|
56412
|
+
message: parsed.message,
|
|
56413
|
+
external_uuid: externalUuid,
|
|
56414
|
+
device_name: DEVICE_NAME2
|
|
56415
|
+
});
|
|
56416
|
+
if (result.inserted) inserted++;
|
|
56417
|
+
else dedup++;
|
|
56418
|
+
} catch (err2) {
|
|
56419
|
+
console.error(`\u26A0\uFE0F [Gemini] insert failed at index ${i}:`, err2);
|
|
56420
|
+
skipped++;
|
|
56421
|
+
}
|
|
56422
|
+
}
|
|
56423
|
+
fileState.cursor = messages.length;
|
|
56424
|
+
return { inserted, skipped, dedup };
|
|
56425
|
+
}
|
|
56426
|
+
async function flushJsonlFile(filePath, fileState, contentSeen) {
|
|
56427
|
+
if (!fs2.existsSync(filePath)) return { inserted: 0, skipped: 0, dedup: 0 };
|
|
56428
|
+
let stat;
|
|
56429
|
+
try {
|
|
56430
|
+
stat = fs2.statSync(filePath);
|
|
56431
|
+
} catch {
|
|
56432
|
+
return { inserted: 0, skipped: 0, dedup: 0 };
|
|
56433
|
+
}
|
|
56434
|
+
if (stat.size <= fileState.cursor) return { inserted: 0, skipped: 0, dedup: 0 };
|
|
56435
|
+
const length = stat.size - fileState.cursor;
|
|
56436
|
+
const fd = fs2.openSync(filePath, "r");
|
|
56437
|
+
let raw;
|
|
56438
|
+
try {
|
|
56439
|
+
const buf = Buffer.alloc(length);
|
|
56440
|
+
fs2.readSync(fd, buf, 0, length, fileState.cursor);
|
|
56441
|
+
raw = buf.toString("utf-8");
|
|
56442
|
+
} finally {
|
|
56443
|
+
fs2.closeSync(fd);
|
|
56444
|
+
}
|
|
56445
|
+
const lastNl = raw.lastIndexOf("\n");
|
|
56446
|
+
if (lastNl === -1) return { inserted: 0, skipped: 0, dedup: 0 };
|
|
56447
|
+
const parsable = raw.slice(0, lastNl);
|
|
56448
|
+
const advanceBy = Buffer.byteLength(parsable, "utf-8") + 1;
|
|
56449
|
+
const newCursor = fileState.cursor + advanceBy;
|
|
56450
|
+
const userId = await getDefaultUserId();
|
|
56451
|
+
let inserted = 0, skipped = 0, dedup = 0;
|
|
56452
|
+
let lineIdx = fileState.cursor === 0 ? 0 : -1;
|
|
56453
|
+
for (const line of parsable.split("\n")) {
|
|
56454
|
+
const trimmed = line.trim();
|
|
56455
|
+
if (!trimmed) {
|
|
56456
|
+
lineIdx++;
|
|
56457
|
+
continue;
|
|
56458
|
+
}
|
|
56459
|
+
let obj;
|
|
56460
|
+
try {
|
|
56461
|
+
obj = JSON.parse(trimmed);
|
|
56462
|
+
} catch {
|
|
56463
|
+
lineIdx++;
|
|
56464
|
+
continue;
|
|
56465
|
+
}
|
|
56466
|
+
if (lineIdx === 0 && typeof obj.sessionId === "string" && obj.sessionId) {
|
|
56467
|
+
fileState.sessionId = obj.sessionId;
|
|
56468
|
+
lineIdx++;
|
|
56469
|
+
continue;
|
|
56470
|
+
}
|
|
56471
|
+
lineIdx++;
|
|
56472
|
+
if (obj.$set !== void 0) continue;
|
|
56473
|
+
const parsed = parseMessage(obj, `${fileState.sessionId}-${lineIdx}`);
|
|
56474
|
+
if (!parsed) {
|
|
56475
|
+
skipped++;
|
|
56476
|
+
continue;
|
|
56477
|
+
}
|
|
56478
|
+
const ck = `${parsed.role}::${parsed.message}`;
|
|
56479
|
+
if (contentSeen.has(ck)) {
|
|
56480
|
+
dedup++;
|
|
56481
|
+
continue;
|
|
56482
|
+
}
|
|
56483
|
+
contentSeen.add(ck);
|
|
56484
|
+
const externalUuid = `gemini:${fileState.sessionId}:${parsed.msgId}`;
|
|
56485
|
+
const agentModel = parsed.role === "user" ? null : parsed.agent_model ?? "unknown";
|
|
56486
|
+
try {
|
|
56487
|
+
const result = await insertRawMemory({
|
|
56488
|
+
user_id: userId,
|
|
56489
|
+
agent_platform: CLIENT_PLATFORM,
|
|
56490
|
+
agent_model: agentModel,
|
|
56491
|
+
role: parsed.role,
|
|
56492
|
+
message: parsed.message,
|
|
56493
|
+
external_uuid: externalUuid,
|
|
56494
|
+
device_name: DEVICE_NAME2
|
|
56495
|
+
});
|
|
56496
|
+
if (result.inserted) inserted++;
|
|
56497
|
+
else dedup++;
|
|
56498
|
+
} catch (err2) {
|
|
56499
|
+
console.error(`\u26A0\uFE0F [Gemini] jsonl insert failed:`, err2);
|
|
56500
|
+
skipped++;
|
|
56501
|
+
}
|
|
56502
|
+
}
|
|
56503
|
+
fileState.cursor = newCursor;
|
|
56504
|
+
return { inserted, skipped, dedup };
|
|
56505
|
+
}
|
|
56506
|
+
function isCaptureArmed() {
|
|
56507
|
+
return _state !== null && _state.chatsDirs.length > 0;
|
|
56508
|
+
}
|
|
56509
|
+
async function flushAllFiles() {
|
|
56510
|
+
if (!_state) return { inserted: 0, skipped: 0, dedup: 0 };
|
|
56511
|
+
let totalI = 0, totalS = 0, totalD = 0;
|
|
56512
|
+
const contentSeen = /* @__PURE__ */ new Set();
|
|
56513
|
+
for (const dir of _state.chatsDirs) {
|
|
56514
|
+
if (!fs2.existsSync(dir)) continue;
|
|
56515
|
+
let entries;
|
|
56516
|
+
try {
|
|
56517
|
+
entries = fs2.readdirSync(dir, { withFileTypes: true });
|
|
56518
|
+
} catch {
|
|
56519
|
+
continue;
|
|
56520
|
+
}
|
|
56521
|
+
for (const entry of entries) {
|
|
56522
|
+
if (!entry.isFile()) continue;
|
|
56523
|
+
if (!entry.name.startsWith("session-")) continue;
|
|
56524
|
+
if (!entry.name.endsWith(".json") && !entry.name.endsWith(".jsonl")) continue;
|
|
56525
|
+
const filePath = path3.join(dir, entry.name);
|
|
56526
|
+
let fileState = _state.files.get(filePath);
|
|
56527
|
+
if (!fileState) {
|
|
56528
|
+
const sessionId = extractShortId(entry.name);
|
|
56529
|
+
const format = entry.name.endsWith(".jsonl") ? "jsonl" : "json";
|
|
56530
|
+
fileState = { cursor: 0, format, sessionId };
|
|
56531
|
+
_state.files.set(filePath, fileState);
|
|
56532
|
+
console.error(`\u{1F4DD} [Gemini] new session detected: ${entry.name} (${format})`);
|
|
56533
|
+
}
|
|
56534
|
+
const r = await flushDeltaForFile(filePath, fileState, contentSeen);
|
|
56535
|
+
totalI += r.inserted;
|
|
56536
|
+
totalS += r.skipped;
|
|
56537
|
+
totalD += r.dedup;
|
|
56538
|
+
}
|
|
56539
|
+
}
|
|
56540
|
+
return { inserted: totalI, skipped: totalS, dedup: totalD };
|
|
56541
|
+
}
|
|
56542
|
+
async function flushWithMutex() {
|
|
56543
|
+
if (_flushInProgress) {
|
|
56544
|
+
_flushPending = true;
|
|
56545
|
+
return;
|
|
56546
|
+
}
|
|
56547
|
+
_flushInProgress = true;
|
|
56548
|
+
try {
|
|
56549
|
+
const r = await flushAllFiles();
|
|
56550
|
+
if (r.inserted > 0 || r.skipped > 0 || r.dedup > 0) {
|
|
56551
|
+
console.error(`\u{1F4DD} [Gemini] live flush: inserted=${r.inserted}, dedup=${r.dedup}, skipped=${r.skipped}`);
|
|
56552
|
+
}
|
|
56553
|
+
} catch (err2) {
|
|
56554
|
+
console.error("\u26A0\uFE0F [Gemini] live flush error:", err2);
|
|
56555
|
+
} finally {
|
|
56556
|
+
_flushInProgress = false;
|
|
56557
|
+
if (_flushPending) {
|
|
56558
|
+
_flushPending = false;
|
|
56559
|
+
setImmediate(() => {
|
|
56560
|
+
void flushWithMutex();
|
|
56561
|
+
});
|
|
56562
|
+
}
|
|
56563
|
+
}
|
|
56564
|
+
}
|
|
56565
|
+
function scheduleFlush() {
|
|
56566
|
+
if (_flushDebounceTimer) clearTimeout(_flushDebounceTimer);
|
|
56567
|
+
_flushDebounceTimer = setTimeout(() => {
|
|
56568
|
+
_flushDebounceTimer = null;
|
|
56569
|
+
void flushWithMutex();
|
|
56570
|
+
}, FLUSH_DEBOUNCE_MS);
|
|
56571
|
+
}
|
|
56572
|
+
function captureSessionStart(cwd) {
|
|
56573
|
+
const chatsDirs = deriveChatsDirCandidates(cwd);
|
|
56574
|
+
_state = { cwd, chatsDirs, files: /* @__PURE__ */ new Map() };
|
|
56575
|
+
if (chatsDirs.length === 0) {
|
|
56576
|
+
return;
|
|
56577
|
+
}
|
|
56578
|
+
let count = 0;
|
|
56579
|
+
for (const dir of chatsDirs) {
|
|
56580
|
+
let entries;
|
|
56581
|
+
try {
|
|
56582
|
+
entries = fs2.readdirSync(dir, { withFileTypes: true });
|
|
56583
|
+
} catch {
|
|
56584
|
+
continue;
|
|
56585
|
+
}
|
|
56586
|
+
for (const entry of entries) {
|
|
56587
|
+
if (!entry.isFile()) continue;
|
|
56588
|
+
if (!entry.name.startsWith("session-")) continue;
|
|
56589
|
+
if (!entry.name.endsWith(".json") && !entry.name.endsWith(".jsonl")) continue;
|
|
56590
|
+
const filePath = path3.join(dir, entry.name);
|
|
56591
|
+
const format = entry.name.endsWith(".jsonl") ? "jsonl" : "json";
|
|
56592
|
+
const sessionId = extractShortId(entry.name);
|
|
56593
|
+
let cursor = 0;
|
|
56594
|
+
let realSessionId = sessionId;
|
|
56595
|
+
if (format === "json") {
|
|
56596
|
+
let session = null;
|
|
56597
|
+
try {
|
|
56598
|
+
session = JSON.parse(fs2.readFileSync(filePath, "utf-8"));
|
|
56599
|
+
} catch {
|
|
56600
|
+
}
|
|
56601
|
+
cursor = session && Array.isArray(session.messages) ? session.messages.length : 0;
|
|
56602
|
+
if (session?.sessionId) realSessionId = session.sessionId;
|
|
56603
|
+
} else {
|
|
56604
|
+
try {
|
|
56605
|
+
cursor = fs2.statSync(filePath).size;
|
|
56606
|
+
} catch {
|
|
56607
|
+
}
|
|
56608
|
+
try {
|
|
56609
|
+
const fd = fs2.openSync(filePath, "r");
|
|
56610
|
+
const buf = Buffer.alloc(512);
|
|
56611
|
+
const n = fs2.readSync(fd, buf, 0, 512, 0);
|
|
56612
|
+
fs2.closeSync(fd);
|
|
56613
|
+
const firstLine = buf.slice(0, n).toString("utf-8").split("\n")[0].trim();
|
|
56614
|
+
const header = JSON.parse(firstLine);
|
|
56615
|
+
if (typeof header.sessionId === "string" && header.sessionId) {
|
|
56616
|
+
realSessionId = header.sessionId;
|
|
56617
|
+
}
|
|
56618
|
+
} catch {
|
|
56619
|
+
}
|
|
56620
|
+
}
|
|
56621
|
+
_state.files.set(filePath, { cursor, format, sessionId: realSessionId });
|
|
56622
|
+
count++;
|
|
56623
|
+
}
|
|
56624
|
+
}
|
|
56625
|
+
console.error(
|
|
56626
|
+
`\u{1F4DD} [Gemini] capture armed: ${count} session(s) across ${chatsDirs.length} chats dir(s)`
|
|
56627
|
+
);
|
|
56628
|
+
armDirWatchers();
|
|
56629
|
+
}
|
|
56630
|
+
function armDirWatchers() {
|
|
56631
|
+
if (!_state) return;
|
|
56632
|
+
if (_watchers.length > 0) return;
|
|
56633
|
+
for (const dir of _state.chatsDirs) {
|
|
56634
|
+
try {
|
|
56635
|
+
const w = fs2.watch(dir, (_evt, filename) => {
|
|
56636
|
+
if (filename && !filename.endsWith(".json") && !filename.endsWith(".jsonl")) return;
|
|
56637
|
+
scheduleFlush();
|
|
56638
|
+
});
|
|
56639
|
+
_watchers.push(w);
|
|
56640
|
+
} catch (err2) {
|
|
56641
|
+
console.error(`\u26A0\uFE0F [Gemini] fs.watch ${dir} failed:`, err2);
|
|
56642
|
+
}
|
|
56643
|
+
}
|
|
56644
|
+
if (_watchers.length > 0) {
|
|
56645
|
+
console.error(`\u{1F4DD} [Gemini] dir watcher(s) armed: ${_watchers.length}`);
|
|
56646
|
+
}
|
|
56647
|
+
_pollTimer = setInterval(() => {
|
|
56648
|
+
void flushWithMutex();
|
|
56649
|
+
}, POLL_INTERVAL_MS);
|
|
56650
|
+
}
|
|
56651
|
+
function disarmDirWatchers() {
|
|
56652
|
+
if (_flushDebounceTimer) {
|
|
56653
|
+
clearTimeout(_flushDebounceTimer);
|
|
56654
|
+
_flushDebounceTimer = null;
|
|
56655
|
+
}
|
|
56656
|
+
if (_pollTimer) {
|
|
56657
|
+
clearInterval(_pollTimer);
|
|
56658
|
+
_pollTimer = null;
|
|
56659
|
+
}
|
|
56660
|
+
for (const w of _watchers) {
|
|
56661
|
+
try {
|
|
56662
|
+
w.close();
|
|
56663
|
+
} catch {
|
|
56664
|
+
}
|
|
56665
|
+
}
|
|
56666
|
+
_watchers.length = 0;
|
|
56667
|
+
}
|
|
56668
|
+
async function captureSessionEnd() {
|
|
56669
|
+
disarmDirWatchers();
|
|
56670
|
+
const waitStart = Date.now();
|
|
56671
|
+
while (_flushInProgress && Date.now() - waitStart < 2e3) {
|
|
56672
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
56673
|
+
}
|
|
56674
|
+
if (!_state || _state.chatsDirs.length === 0) {
|
|
56675
|
+
return { inserted: 0, skipped: 0, error: "session not armed" };
|
|
56676
|
+
}
|
|
56677
|
+
_flushInProgress = true;
|
|
56678
|
+
try {
|
|
56679
|
+
const r = await flushAllFiles();
|
|
56680
|
+
if (r.inserted > 0 || r.skipped > 0 || r.dedup > 0) {
|
|
56681
|
+
console.error(`\u{1F4DD} [Gemini] final flush: inserted=${r.inserted}, dedup=${r.dedup}, skipped=${r.skipped}`);
|
|
56682
|
+
}
|
|
56683
|
+
return { inserted: r.inserted, skipped: r.skipped };
|
|
56684
|
+
} finally {
|
|
56685
|
+
_flushInProgress = false;
|
|
56686
|
+
}
|
|
56687
|
+
}
|
|
56688
|
+
|
|
56689
|
+
// src/auto_save/save_message_tool.ts
|
|
56690
|
+
var DEVICE_NAME3 = os5.hostname();
|
|
56291
56691
|
function registerSaveMessage(server) {
|
|
56292
56692
|
server.registerTool(
|
|
56293
56693
|
"save_message",
|
|
@@ -56320,6 +56720,14 @@ subagent \uCEE8\uD14D\uC2A4\uD2B8\uB77C\uBA74 subagent=true + subagent_model + s
|
|
|
56320
56720
|
async (args) => {
|
|
56321
56721
|
const userId = await getDefaultUserId();
|
|
56322
56722
|
const id = resolveAgentIdentity(server, args);
|
|
56723
|
+
if (id.agent_platform === "gemini-cli-mcp-client" && isCaptureArmed()) {
|
|
56724
|
+
return {
|
|
56725
|
+
content: [{
|
|
56726
|
+
type: "text",
|
|
56727
|
+
text: JSON.stringify({ stored: false, skipped: "passive capture active" }, null, 2)
|
|
56728
|
+
}]
|
|
56729
|
+
};
|
|
56730
|
+
}
|
|
56323
56731
|
const agentModel = args.role === "user" ? null : id.agent_model;
|
|
56324
56732
|
const inserted = await insertRawMemory({
|
|
56325
56733
|
user_id: userId,
|
|
@@ -56329,7 +56737,8 @@ subagent \uCEE8\uD14D\uC2A4\uD2B8\uB77C\uBA74 subagent=true + subagent_model + s
|
|
|
56329
56737
|
subagent_model: id.subagent_model,
|
|
56330
56738
|
subagent_role: id.subagent_role,
|
|
56331
56739
|
role: args.role,
|
|
56332
|
-
message: args.message
|
|
56740
|
+
message: args.message,
|
|
56741
|
+
device_name: DEVICE_NAME3
|
|
56333
56742
|
// tag/embed NULL — Cold Path가 background 처리 (Hot Path latency 보장)
|
|
56334
56743
|
});
|
|
56335
56744
|
return {
|
|
@@ -56357,13 +56766,143 @@ function registerTools(server) {
|
|
|
56357
56766
|
|
|
56358
56767
|
// src/cold_path/worker.ts
|
|
56359
56768
|
init_db();
|
|
56769
|
+
|
|
56770
|
+
// src/cold_path/dtag_promoter.ts
|
|
56771
|
+
init_db();
|
|
56772
|
+
function envInt(name, fallback) {
|
|
56773
|
+
const raw = process.env[name];
|
|
56774
|
+
if (!raw) return fallback;
|
|
56775
|
+
const n = Number(raw);
|
|
56776
|
+
return Number.isFinite(n) && n >= 1 ? n : fallback;
|
|
56777
|
+
}
|
|
56778
|
+
var CLUSTER_SYSTEM = `You are a keyword clustering assistant for a personal memory system.
|
|
56779
|
+
|
|
56780
|
+
Given a list of d_tags (short hyphenated keywords) with their occurrence counts,
|
|
56781
|
+
group semantically similar tags that refer to the same project or topic.
|
|
56782
|
+
|
|
56783
|
+
OUTPUT strict JSON array:
|
|
56784
|
+
[
|
|
56785
|
+
{ "canonical": "<best-slug>", "members": ["<tag1>", "<tag2>", ...] },
|
|
56786
|
+
...
|
|
56787
|
+
]
|
|
56788
|
+
|
|
56789
|
+
Rules:
|
|
56790
|
+
- canonical must be one of the input tags (pick the most descriptive one) or a clean slug if none fit
|
|
56791
|
+
- canonical must be lowercase, hyphenated (e.g. "yt-signal-finder")
|
|
56792
|
+
- Only group tags that clearly refer to the same project/topic
|
|
56793
|
+
- Tags with no similar counterparts become their own single-member cluster
|
|
56794
|
+
- Do NOT merge unrelated topics just because they share one word`;
|
|
56795
|
+
async function clusterDTags(tags) {
|
|
56796
|
+
if (tags.length === 0) return [];
|
|
56797
|
+
const tagList = tags.map((t) => `${t.tag} (${t.cnt}x)`).join(", ");
|
|
56798
|
+
const userPrompt = `Cluster these d_tags by project/topic:
|
|
56799
|
+
${tagList}`;
|
|
56800
|
+
let raw;
|
|
56801
|
+
try {
|
|
56802
|
+
raw = await callRole("clusterer", {
|
|
56803
|
+
system: CLUSTER_SYSTEM,
|
|
56804
|
+
user: userPrompt,
|
|
56805
|
+
responseFormat: "json",
|
|
56806
|
+
maxTokens: 512
|
|
56807
|
+
});
|
|
56808
|
+
} catch (err2) {
|
|
56809
|
+
console.error("\u26A0\uFE0F [DTagPromoter] clusterer call failed, falling back to no clustering:", err2);
|
|
56810
|
+
return tags.map((t) => ({ canonical: t.tag, members: [t.tag], total: t.cnt }));
|
|
56811
|
+
}
|
|
56812
|
+
let parsed;
|
|
56813
|
+
try {
|
|
56814
|
+
parsed = JSON.parse(raw);
|
|
56815
|
+
if (!Array.isArray(parsed)) throw new Error("not an array");
|
|
56816
|
+
} catch {
|
|
56817
|
+
console.error("\u26A0\uFE0F [DTagPromoter] clusterer returned invalid JSON, falling back:", raw.slice(0, 200));
|
|
56818
|
+
return tags.map((t) => ({ canonical: t.tag, members: [t.tag], total: t.cnt }));
|
|
56819
|
+
}
|
|
56820
|
+
const freqMap = new Map(tags.map((t) => [t.tag, t.cnt]));
|
|
56821
|
+
return parsed.map((c) => {
|
|
56822
|
+
const canonical = String(c.canonical || "").toLowerCase().trim().replace(/\s+/g, "-");
|
|
56823
|
+
const members = Array.isArray(c.members) ? c.members.map((m) => String(m).toLowerCase().trim()).filter(Boolean) : [canonical];
|
|
56824
|
+
const total = members.reduce((sum, m) => sum + (freqMap.get(m) ?? 0), 0);
|
|
56825
|
+
return { canonical, members, total };
|
|
56826
|
+
});
|
|
56827
|
+
}
|
|
56828
|
+
async function runDtagPromotion() {
|
|
56829
|
+
const minCount = envInt("DTAG_PROMOTE_MIN_COUNT", 10);
|
|
56830
|
+
const windowDays = envInt("DTAG_PROMOTE_WINDOW_DAYS", 30);
|
|
56831
|
+
const userId = await getDefaultUserId();
|
|
56832
|
+
const freqResult = await db.query(
|
|
56833
|
+
`SELECT unnest(d_tag) AS tag, COUNT(*)::int AS cnt
|
|
56834
|
+
FROM memory
|
|
56835
|
+
WHERE user_id = $1
|
|
56836
|
+
AND tag_processed = TRUE
|
|
56837
|
+
AND is_active = TRUE
|
|
56838
|
+
AND created_at >= NOW() - ($2 || ' days')::INTERVAL
|
|
56839
|
+
GROUP BY tag
|
|
56840
|
+
HAVING COUNT(*) >= 2
|
|
56841
|
+
ORDER BY cnt DESC
|
|
56842
|
+
LIMIT 50`,
|
|
56843
|
+
[userId, String(windowDays)]
|
|
56844
|
+
);
|
|
56845
|
+
if (freqResult.rows.length === 0) return { promoted: [], retrotagged: 0 };
|
|
56846
|
+
const tags = freqResult.rows.map((r) => ({
|
|
56847
|
+
tag: String(r.tag).toLowerCase().trim(),
|
|
56848
|
+
cnt: Number(r.cnt)
|
|
56849
|
+
})).filter((t) => t.tag.length > 0);
|
|
56850
|
+
const clusters = await clusterDTags(tags);
|
|
56851
|
+
const toPromote = clusters.filter((c) => c.total >= minCount);
|
|
56852
|
+
if (toPromote.length === 0) return { promoted: [], retrotagged: 0 };
|
|
56853
|
+
const summary = { promoted: [], retrotagged: 0 };
|
|
56854
|
+
for (const cluster of toPromote) {
|
|
56855
|
+
if (!cluster.canonical) continue;
|
|
56856
|
+
const upsert = await db.query(
|
|
56857
|
+
`INSERT INTO project_tags (name)
|
|
56858
|
+
VALUES ($1)
|
|
56859
|
+
ON CONFLICT (name) DO NOTHING
|
|
56860
|
+
RETURNING id`,
|
|
56861
|
+
[cluster.canonical]
|
|
56862
|
+
);
|
|
56863
|
+
let pTagId;
|
|
56864
|
+
if (upsert.rows.length > 0) {
|
|
56865
|
+
pTagId = Number(upsert.rows[0].id);
|
|
56866
|
+
summary.promoted.push(cluster.canonical);
|
|
56867
|
+
console.error(`\u{1F3F7}\uFE0F [DTagPromoter] promoted "${cluster.canonical}" (total=${cluster.total}, members=${cluster.members.join(", ")})`);
|
|
56868
|
+
invalidateCandidateCache();
|
|
56869
|
+
} else {
|
|
56870
|
+
const existing = await db.query(
|
|
56871
|
+
`SELECT id FROM project_tags WHERE name = $1 LIMIT 1`,
|
|
56872
|
+
[cluster.canonical]
|
|
56873
|
+
);
|
|
56874
|
+
if (existing.rows.length === 0) continue;
|
|
56875
|
+
pTagId = Number(existing.rows[0].id);
|
|
56876
|
+
}
|
|
56877
|
+
for (const memberTag of cluster.members) {
|
|
56878
|
+
const updated = await db.query(
|
|
56879
|
+
`UPDATE memory
|
|
56880
|
+
SET p_tag_id = $1, updated_at = NOW()
|
|
56881
|
+
WHERE user_id = $2
|
|
56882
|
+
AND tag_processed = TRUE
|
|
56883
|
+
AND p_tag_id IS NULL
|
|
56884
|
+
AND $3 = ANY(d_tag)
|
|
56885
|
+
RETURNING id`,
|
|
56886
|
+
[pTagId, userId, memberTag]
|
|
56887
|
+
);
|
|
56888
|
+
summary.retrotagged += updated.rows.length;
|
|
56889
|
+
}
|
|
56890
|
+
if (summary.retrotagged > 0) {
|
|
56891
|
+
console.error(`\u{1F3F7}\uFE0F [DTagPromoter] retrotagged ${summary.retrotagged} rows with "${cluster.canonical}"`);
|
|
56892
|
+
}
|
|
56893
|
+
}
|
|
56894
|
+
return summary;
|
|
56895
|
+
}
|
|
56896
|
+
|
|
56897
|
+
// src/cold_path/worker.ts
|
|
56360
56898
|
var intervalTimer = null;
|
|
56361
56899
|
var warmupTimer = null;
|
|
56362
56900
|
var running = false;
|
|
56901
|
+
var tickCount = 0;
|
|
56363
56902
|
var DEFAULT_INTERVAL_SEC = 60;
|
|
56364
56903
|
var DEFAULT_BATCH = 5;
|
|
56365
56904
|
var DEFAULT_WARMUP_SEC = 30;
|
|
56366
|
-
function
|
|
56905
|
+
function envInt2(name, fallback) {
|
|
56367
56906
|
const raw = process.env[name];
|
|
56368
56907
|
if (!raw) return fallback;
|
|
56369
56908
|
const n = Number(raw);
|
|
@@ -56481,7 +57020,7 @@ async function recordError(client2, rowId, err2) {
|
|
|
56481
57020
|
async function tick() {
|
|
56482
57021
|
if (running) return 0;
|
|
56483
57022
|
running = true;
|
|
56484
|
-
const batchSize =
|
|
57023
|
+
const batchSize = envInt2("COLD_PATH_BATCH_SIZE", DEFAULT_BATCH);
|
|
56485
57024
|
const client2 = await db.getClient();
|
|
56486
57025
|
try {
|
|
56487
57026
|
await client2.query("BEGIN");
|
|
@@ -56515,19 +57054,35 @@ async function tick() {
|
|
|
56515
57054
|
running = false;
|
|
56516
57055
|
}
|
|
56517
57056
|
}
|
|
57057
|
+
var DTAG_PROMOTE_EVERY_N_TICKS = 10;
|
|
57058
|
+
async function maybeRunPromotion() {
|
|
57059
|
+
if (process.env.DTAG_PROMOTE_ENABLED === "false") return;
|
|
57060
|
+
tickCount++;
|
|
57061
|
+
if (tickCount % DTAG_PROMOTE_EVERY_N_TICKS !== 0) return;
|
|
57062
|
+
try {
|
|
57063
|
+
const result = await runDtagPromotion();
|
|
57064
|
+
if (result.promoted.length > 0 || result.retrotagged > 0) {
|
|
57065
|
+
console.error(`\u{1F3F7}\uFE0F [DTagPromoter] promoted=${result.promoted.length} tags, retrotagged=${result.retrotagged} rows`);
|
|
57066
|
+
}
|
|
57067
|
+
} catch (err2) {
|
|
57068
|
+
console.error("\u26A0\uFE0F [DTagPromoter] promotion error (non-blocking):", err2);
|
|
57069
|
+
}
|
|
57070
|
+
}
|
|
56518
57071
|
function startColdPathWorker() {
|
|
56519
57072
|
if (process.env.COLD_PATH_ENABLED === "false") {
|
|
56520
57073
|
console.error("\u{1F535} [ColdPath] disabled (COLD_PATH_ENABLED=false)");
|
|
56521
57074
|
return;
|
|
56522
57075
|
}
|
|
56523
|
-
const intervalSec =
|
|
56524
|
-
const warmupSec =
|
|
57076
|
+
const intervalSec = envInt2("COLD_PATH_INTERVAL_SEC", DEFAULT_INTERVAL_SEC);
|
|
57077
|
+
const warmupSec = envInt2("COLD_PATH_WARMUP_SEC", DEFAULT_WARMUP_SEC);
|
|
56525
57078
|
console.error(`\u{1F535} [ColdPath] starting \u2014 warmup ${warmupSec}s, interval ${intervalSec}s`);
|
|
56526
57079
|
warmupTimer = setTimeout(() => {
|
|
56527
57080
|
intervalTimer = setInterval(() => {
|
|
56528
57081
|
tick().catch((err2) => console.error("\u274C [ColdPath] unhandled tick error:", err2));
|
|
57082
|
+
maybeRunPromotion().catch((err2) => console.error("\u274C [DTagPromoter] unhandled error:", err2));
|
|
56529
57083
|
}, intervalSec * 1e3);
|
|
56530
57084
|
tick().catch((err2) => console.error("\u274C [ColdPath] unhandled first tick:", err2));
|
|
57085
|
+
maybeRunPromotion().catch((err2) => console.error("\u274C [DTagPromoter] unhandled error:", err2));
|
|
56531
57086
|
}, warmupSec * 1e3);
|
|
56532
57087
|
}
|
|
56533
57088
|
function stopColdPathWorker() {
|
|
@@ -56556,36 +57111,37 @@ async function drainColdPath(maxTicks = 12) {
|
|
|
56556
57111
|
}
|
|
56557
57112
|
|
|
56558
57113
|
// src/auto_save/jsonl_capture.ts
|
|
56559
|
-
import * as
|
|
56560
|
-
import * as
|
|
56561
|
-
import * as
|
|
56562
|
-
var
|
|
56563
|
-
var
|
|
56564
|
-
var
|
|
56565
|
-
var
|
|
57114
|
+
import * as fs3 from "node:fs";
|
|
57115
|
+
import * as path4 from "node:path";
|
|
57116
|
+
import * as os6 from "node:os";
|
|
57117
|
+
var CLIENT_PLATFORM2 = "claude-code";
|
|
57118
|
+
var DEVICE_NAME4 = os6.hostname();
|
|
57119
|
+
var PROJECTS_ROOT = path4.join(os6.homedir(), ".claude", "projects");
|
|
57120
|
+
var FLUSH_DEBOUNCE_MS2 = 200;
|
|
57121
|
+
var _state2 = null;
|
|
56566
57122
|
var _watcher = null;
|
|
56567
|
-
var
|
|
56568
|
-
var
|
|
56569
|
-
var
|
|
57123
|
+
var _flushInProgress2 = false;
|
|
57124
|
+
var _flushPending2 = false;
|
|
57125
|
+
var _flushDebounceTimer2 = null;
|
|
56570
57126
|
function findProjectDir(cwd) {
|
|
56571
57127
|
let subdirs;
|
|
56572
57128
|
try {
|
|
56573
|
-
subdirs =
|
|
57129
|
+
subdirs = fs3.readdirSync(PROJECTS_ROOT, { withFileTypes: true });
|
|
56574
57130
|
} catch {
|
|
56575
57131
|
return null;
|
|
56576
57132
|
}
|
|
56577
57133
|
for (const subdir of subdirs) {
|
|
56578
57134
|
if (!subdir.isDirectory()) continue;
|
|
56579
|
-
const dirPath =
|
|
57135
|
+
const dirPath = path4.join(PROJECTS_ROOT, subdir.name);
|
|
56580
57136
|
let files;
|
|
56581
57137
|
try {
|
|
56582
|
-
files =
|
|
57138
|
+
files = fs3.readdirSync(dirPath, { withFileTypes: true });
|
|
56583
57139
|
} catch {
|
|
56584
57140
|
continue;
|
|
56585
57141
|
}
|
|
56586
57142
|
const jsonlFile = files.find((f) => f.isFile() && f.name.endsWith(".jsonl"));
|
|
56587
57143
|
if (!jsonlFile) continue;
|
|
56588
|
-
const foundCwd = readCwdFromJsonl(
|
|
57144
|
+
const foundCwd = readCwdFromJsonl(path4.join(dirPath, jsonlFile.name));
|
|
56589
57145
|
if (foundCwd === cwd) return dirPath;
|
|
56590
57146
|
}
|
|
56591
57147
|
return null;
|
|
@@ -56593,13 +57149,13 @@ function findProjectDir(cwd) {
|
|
|
56593
57149
|
function readCwdFromJsonl(jsonlPath) {
|
|
56594
57150
|
let fd;
|
|
56595
57151
|
try {
|
|
56596
|
-
fd =
|
|
57152
|
+
fd = fs3.openSync(jsonlPath, "r");
|
|
56597
57153
|
} catch {
|
|
56598
57154
|
return null;
|
|
56599
57155
|
}
|
|
56600
57156
|
try {
|
|
56601
57157
|
const buf = Buffer.alloc(2048);
|
|
56602
|
-
const bytesRead =
|
|
57158
|
+
const bytesRead = fs3.readSync(fd, buf, 0, 2048, 0);
|
|
56603
57159
|
const raw = buf.slice(0, bytesRead).toString("utf-8");
|
|
56604
57160
|
for (const line of raw.split("\n")) {
|
|
56605
57161
|
if (!line.trim()) continue;
|
|
@@ -56611,35 +57167,35 @@ function readCwdFromJsonl(jsonlPath) {
|
|
|
56611
57167
|
}
|
|
56612
57168
|
return null;
|
|
56613
57169
|
} finally {
|
|
56614
|
-
|
|
57170
|
+
fs3.closeSync(fd);
|
|
56615
57171
|
}
|
|
56616
57172
|
}
|
|
56617
|
-
function
|
|
56618
|
-
|
|
57173
|
+
function captureSessionStart2(cwd) {
|
|
57174
|
+
_state2 = { cwd, projectDir: null, files: /* @__PURE__ */ new Map() };
|
|
56619
57175
|
const projectDir = findProjectDir(cwd);
|
|
56620
57176
|
if (!projectDir) {
|
|
56621
57177
|
return;
|
|
56622
57178
|
}
|
|
56623
|
-
|
|
57179
|
+
_state2.projectDir = projectDir;
|
|
56624
57180
|
let entries;
|
|
56625
57181
|
try {
|
|
56626
|
-
entries =
|
|
57182
|
+
entries = fs3.readdirSync(projectDir, { withFileTypes: true });
|
|
56627
57183
|
} catch {
|
|
56628
57184
|
return;
|
|
56629
57185
|
}
|
|
56630
57186
|
for (const entry of entries) {
|
|
56631
57187
|
if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
|
|
56632
|
-
const jsonlPath =
|
|
57188
|
+
const jsonlPath = path4.join(projectDir, entry.name);
|
|
56633
57189
|
let stat;
|
|
56634
57190
|
try {
|
|
56635
|
-
stat =
|
|
57191
|
+
stat = fs3.statSync(jsonlPath);
|
|
56636
57192
|
} catch {
|
|
56637
57193
|
continue;
|
|
56638
57194
|
}
|
|
56639
57195
|
const sessionId = entry.name.replace(/\.jsonl$/, "");
|
|
56640
|
-
|
|
57196
|
+
_state2.files.set(jsonlPath, { cursorBytes: stat.size, sessionId, contentSeen: /* @__PURE__ */ new Set() });
|
|
56641
57197
|
}
|
|
56642
|
-
console.error(`\u{1F4DD} [JSONL] capture armed: ${
|
|
57198
|
+
console.error(`\u{1F4DD} [JSONL] capture armed: ${_state2.files.size} jsonl(s) in ${path4.basename(projectDir)}/`);
|
|
56643
57199
|
armDirWatcher();
|
|
56644
57200
|
}
|
|
56645
57201
|
function parseEntry(line) {
|
|
@@ -56678,24 +57234,24 @@ function parseEntry(line) {
|
|
|
56678
57234
|
agent_model
|
|
56679
57235
|
};
|
|
56680
57236
|
}
|
|
56681
|
-
async function
|
|
56682
|
-
if (!
|
|
57237
|
+
async function flushDeltaForFile2(jsonlPath, fileState) {
|
|
57238
|
+
if (!fs3.existsSync(jsonlPath)) return { inserted: 0, skipped: 0, dedup: 0 };
|
|
56683
57239
|
let stat;
|
|
56684
57240
|
try {
|
|
56685
|
-
stat =
|
|
57241
|
+
stat = fs3.statSync(jsonlPath);
|
|
56686
57242
|
} catch {
|
|
56687
57243
|
return { inserted: 0, skipped: 0, dedup: 0 };
|
|
56688
57244
|
}
|
|
56689
57245
|
if (stat.size <= fileState.cursorBytes) return { inserted: 0, skipped: 0, dedup: 0 };
|
|
56690
57246
|
const length = stat.size - fileState.cursorBytes;
|
|
56691
|
-
const fd =
|
|
57247
|
+
const fd = fs3.openSync(jsonlPath, "r");
|
|
56692
57248
|
let raw;
|
|
56693
57249
|
try {
|
|
56694
57250
|
const buf = Buffer.alloc(length);
|
|
56695
|
-
|
|
57251
|
+
fs3.readSync(fd, buf, 0, length, fileState.cursorBytes);
|
|
56696
57252
|
raw = buf.toString("utf-8");
|
|
56697
57253
|
} finally {
|
|
56698
|
-
|
|
57254
|
+
fs3.closeSync(fd);
|
|
56699
57255
|
}
|
|
56700
57256
|
const lastNl = raw.lastIndexOf("\n");
|
|
56701
57257
|
if (lastNl === -1) return { inserted: 0, skipped: 0, dedup: 0 };
|
|
@@ -56713,16 +57269,23 @@ async function flushDeltaForFile(jsonlPath, fileState) {
|
|
|
56713
57269
|
skipped++;
|
|
56714
57270
|
continue;
|
|
56715
57271
|
}
|
|
57272
|
+
const ck = `${parsed.role}::${parsed.message}`;
|
|
57273
|
+
if (fileState.contentSeen.has(ck)) {
|
|
57274
|
+
dedup++;
|
|
57275
|
+
continue;
|
|
57276
|
+
}
|
|
57277
|
+
fileState.contentSeen.add(ck);
|
|
56716
57278
|
const externalUuid = `claude-code:${fileState.sessionId}:${parsed.uuid}`;
|
|
56717
57279
|
const agentModel = parsed.role === "user" ? null : parsed.agent_model ?? "unknown";
|
|
56718
57280
|
try {
|
|
56719
57281
|
const result = await insertRawMemory({
|
|
56720
57282
|
user_id: userId,
|
|
56721
|
-
agent_platform:
|
|
57283
|
+
agent_platform: CLIENT_PLATFORM2,
|
|
56722
57284
|
agent_model: agentModel,
|
|
56723
57285
|
role: parsed.role,
|
|
56724
57286
|
message: parsed.message,
|
|
56725
|
-
external_uuid: externalUuid
|
|
57287
|
+
external_uuid: externalUuid,
|
|
57288
|
+
device_name: DEVICE_NAME4
|
|
56726
57289
|
});
|
|
56727
57290
|
if (result.inserted) inserted++;
|
|
56728
57291
|
else dedup++;
|
|
@@ -56734,71 +57297,71 @@ async function flushDeltaForFile(jsonlPath, fileState) {
|
|
|
56734
57297
|
fileState.cursorBytes = newCursor;
|
|
56735
57298
|
return { inserted, skipped, dedup };
|
|
56736
57299
|
}
|
|
56737
|
-
async function
|
|
56738
|
-
if (!
|
|
56739
|
-
const projectDir =
|
|
56740
|
-
if (!
|
|
57300
|
+
async function flushAllFiles2() {
|
|
57301
|
+
if (!_state2 || !_state2.projectDir) return { inserted: 0, skipped: 0, dedup: 0 };
|
|
57302
|
+
const projectDir = _state2.projectDir;
|
|
57303
|
+
if (!fs3.existsSync(projectDir)) return { inserted: 0, skipped: 0, dedup: 0 };
|
|
56741
57304
|
let entries;
|
|
56742
57305
|
try {
|
|
56743
|
-
entries =
|
|
57306
|
+
entries = fs3.readdirSync(projectDir, { withFileTypes: true });
|
|
56744
57307
|
} catch {
|
|
56745
57308
|
return { inserted: 0, skipped: 0, dedup: 0 };
|
|
56746
57309
|
}
|
|
56747
57310
|
let totalI = 0, totalS = 0, totalD = 0;
|
|
56748
57311
|
for (const entry of entries) {
|
|
56749
57312
|
if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
|
|
56750
|
-
const jsonlPath =
|
|
56751
|
-
let fileState =
|
|
57313
|
+
const jsonlPath = path4.join(projectDir, entry.name);
|
|
57314
|
+
let fileState = _state2.files.get(jsonlPath);
|
|
56752
57315
|
if (!fileState) {
|
|
56753
57316
|
const sessionId = entry.name.replace(/\.jsonl$/, "");
|
|
56754
|
-
fileState = { cursorBytes: 0, sessionId };
|
|
56755
|
-
|
|
57317
|
+
fileState = { cursorBytes: 0, sessionId, contentSeen: /* @__PURE__ */ new Set() };
|
|
57318
|
+
_state2.files.set(jsonlPath, fileState);
|
|
56756
57319
|
console.error(`\u{1F4DD} [JSONL] new session detected: ${entry.name}`);
|
|
56757
57320
|
}
|
|
56758
|
-
const r = await
|
|
57321
|
+
const r = await flushDeltaForFile2(jsonlPath, fileState);
|
|
56759
57322
|
totalI += r.inserted;
|
|
56760
57323
|
totalS += r.skipped;
|
|
56761
57324
|
totalD += r.dedup;
|
|
56762
57325
|
}
|
|
56763
57326
|
return { inserted: totalI, skipped: totalS, dedup: totalD };
|
|
56764
57327
|
}
|
|
56765
|
-
async function
|
|
56766
|
-
if (
|
|
56767
|
-
|
|
57328
|
+
async function flushWithMutex2() {
|
|
57329
|
+
if (_flushInProgress2) {
|
|
57330
|
+
_flushPending2 = true;
|
|
56768
57331
|
return;
|
|
56769
57332
|
}
|
|
56770
|
-
|
|
57333
|
+
_flushInProgress2 = true;
|
|
56771
57334
|
try {
|
|
56772
|
-
const r = await
|
|
57335
|
+
const r = await flushAllFiles2();
|
|
56773
57336
|
if (r.inserted > 0 || r.skipped > 0 || r.dedup > 0) {
|
|
56774
57337
|
console.error(`\u{1F4DD} [JSONL] live flush: inserted=${r.inserted}, dedup=${r.dedup}, skipped=${r.skipped}`);
|
|
56775
57338
|
}
|
|
56776
57339
|
} catch (err2) {
|
|
56777
57340
|
console.error("\u26A0\uFE0F [JSONL] live flush error:", err2);
|
|
56778
57341
|
} finally {
|
|
56779
|
-
|
|
56780
|
-
if (
|
|
56781
|
-
|
|
57342
|
+
_flushInProgress2 = false;
|
|
57343
|
+
if (_flushPending2) {
|
|
57344
|
+
_flushPending2 = false;
|
|
56782
57345
|
setImmediate(() => {
|
|
56783
|
-
void
|
|
57346
|
+
void flushWithMutex2();
|
|
56784
57347
|
});
|
|
56785
57348
|
}
|
|
56786
57349
|
}
|
|
56787
57350
|
}
|
|
56788
|
-
function
|
|
56789
|
-
if (
|
|
56790
|
-
|
|
56791
|
-
|
|
56792
|
-
void
|
|
56793
|
-
},
|
|
57351
|
+
function scheduleFlush2() {
|
|
57352
|
+
if (_flushDebounceTimer2) clearTimeout(_flushDebounceTimer2);
|
|
57353
|
+
_flushDebounceTimer2 = setTimeout(() => {
|
|
57354
|
+
_flushDebounceTimer2 = null;
|
|
57355
|
+
void flushWithMutex2();
|
|
57356
|
+
}, FLUSH_DEBOUNCE_MS2);
|
|
56794
57357
|
}
|
|
56795
57358
|
function armDirWatcher() {
|
|
56796
|
-
if (!
|
|
57359
|
+
if (!_state2 || !_state2.projectDir) return;
|
|
56797
57360
|
if (_watcher) return;
|
|
56798
57361
|
try {
|
|
56799
|
-
_watcher =
|
|
57362
|
+
_watcher = fs3.watch(_state2.projectDir, (_eventType, filename) => {
|
|
56800
57363
|
if (filename && !filename.endsWith(".jsonl")) return;
|
|
56801
|
-
|
|
57364
|
+
scheduleFlush2();
|
|
56802
57365
|
});
|
|
56803
57366
|
console.error(`\u{1F4DD} [JSONL] dir watcher armed`);
|
|
56804
57367
|
} catch (err2) {
|
|
@@ -56806,9 +57369,9 @@ function armDirWatcher() {
|
|
|
56806
57369
|
}
|
|
56807
57370
|
}
|
|
56808
57371
|
function disarmDirWatcher() {
|
|
56809
|
-
if (
|
|
56810
|
-
clearTimeout(
|
|
56811
|
-
|
|
57372
|
+
if (_flushDebounceTimer2) {
|
|
57373
|
+
clearTimeout(_flushDebounceTimer2);
|
|
57374
|
+
_flushDebounceTimer2 = null;
|
|
56812
57375
|
}
|
|
56813
57376
|
if (_watcher) {
|
|
56814
57377
|
try {
|
|
@@ -56818,85 +57381,88 @@ function disarmDirWatcher() {
|
|
|
56818
57381
|
_watcher = null;
|
|
56819
57382
|
}
|
|
56820
57383
|
}
|
|
56821
|
-
async function
|
|
57384
|
+
async function captureSessionEnd2() {
|
|
56822
57385
|
disarmDirWatcher();
|
|
56823
57386
|
const waitStart = Date.now();
|
|
56824
|
-
while (
|
|
57387
|
+
while (_flushInProgress2 && Date.now() - waitStart < 2e3) {
|
|
56825
57388
|
await new Promise((r) => setTimeout(r, 50));
|
|
56826
57389
|
}
|
|
56827
|
-
if (!
|
|
57390
|
+
if (!_state2 || !_state2.projectDir) {
|
|
56828
57391
|
return { inserted: 0, skipped: 0, error: "session not armed" };
|
|
56829
57392
|
}
|
|
56830
|
-
|
|
57393
|
+
_flushInProgress2 = true;
|
|
56831
57394
|
try {
|
|
56832
|
-
const r = await
|
|
57395
|
+
const r = await flushAllFiles2();
|
|
56833
57396
|
if (r.inserted > 0 || r.skipped > 0 || r.dedup > 0) {
|
|
56834
57397
|
console.error(`\u{1F4DD} [JSONL] final flush: inserted=${r.inserted}, dedup=${r.dedup}, skipped=${r.skipped}`);
|
|
56835
57398
|
}
|
|
56836
57399
|
return { inserted: r.inserted, skipped: r.skipped };
|
|
56837
57400
|
} finally {
|
|
56838
|
-
|
|
57401
|
+
_flushInProgress2 = false;
|
|
56839
57402
|
}
|
|
56840
57403
|
}
|
|
56841
57404
|
|
|
56842
57405
|
// src/auto_save/codex_capture.ts
|
|
56843
|
-
import * as
|
|
56844
|
-
import * as
|
|
56845
|
-
import * as
|
|
56846
|
-
|
|
56847
|
-
var
|
|
56848
|
-
var
|
|
56849
|
-
var
|
|
56850
|
-
var
|
|
56851
|
-
var
|
|
56852
|
-
|
|
56853
|
-
|
|
56854
|
-
|
|
56855
|
-
|
|
56856
|
-
|
|
56857
|
-
|
|
56858
|
-
|
|
56859
|
-
|
|
56860
|
-
|
|
56861
|
-
|
|
56862
|
-
|
|
56863
|
-
|
|
57406
|
+
import * as fs4 from "node:fs";
|
|
57407
|
+
import * as path5 from "node:path";
|
|
57408
|
+
import * as os7 from "node:os";
|
|
57409
|
+
import { spawnSync } from "node:child_process";
|
|
57410
|
+
var DEVICE_NAME5 = os7.hostname();
|
|
57411
|
+
var SESSIONS_ROOT = path5.join(os7.homedir(), ".codex", "sessions");
|
|
57412
|
+
var STATE_DB = path5.join(os7.homedir(), ".codex", "state_5.sqlite");
|
|
57413
|
+
var FLUSH_DEBOUNCE_MS3 = 200;
|
|
57414
|
+
var POLL_INTERVAL_MS2 = 3e3;
|
|
57415
|
+
function sourceToPlatform(source) {
|
|
57416
|
+
switch (source) {
|
|
57417
|
+
case "cli":
|
|
57418
|
+
return "codex-cli";
|
|
57419
|
+
case "vscode":
|
|
57420
|
+
return "codex-desktop";
|
|
57421
|
+
case "mcp":
|
|
57422
|
+
return "codex-mcp";
|
|
57423
|
+
case "exec":
|
|
57424
|
+
return "codex-exec";
|
|
57425
|
+
default:
|
|
57426
|
+
return "codex-mcp-client";
|
|
56864
57427
|
}
|
|
57428
|
+
}
|
|
57429
|
+
function lookupPlatform(sessionId) {
|
|
56865
57430
|
try {
|
|
56866
|
-
const
|
|
56867
|
-
|
|
56868
|
-
|
|
56869
|
-
|
|
56870
|
-
|
|
56871
|
-
|
|
56872
|
-
|
|
56873
|
-
if (entry.type !== "session_meta") return null;
|
|
56874
|
-
const sid = entry.payload?.id;
|
|
56875
|
-
const cwd = entry.payload?.cwd;
|
|
56876
|
-
if (typeof sid !== "string" || typeof cwd !== "string") return null;
|
|
56877
|
-
return { sessionId: sid, cwd };
|
|
57431
|
+
const res = spawnSync(
|
|
57432
|
+
"sqlite3",
|
|
57433
|
+
[STATE_DB, `SELECT source FROM threads WHERE id='${sessionId}' LIMIT 1;`],
|
|
57434
|
+
{ encoding: "utf-8", timeout: 500 }
|
|
57435
|
+
);
|
|
57436
|
+
const source = (res.stdout ?? "").trim();
|
|
57437
|
+
if (source) return sourceToPlatform(source);
|
|
56878
57438
|
} catch {
|
|
56879
|
-
return null;
|
|
56880
|
-
} finally {
|
|
56881
|
-
try {
|
|
56882
|
-
fs3.closeSync(fd);
|
|
56883
|
-
} catch {
|
|
56884
|
-
}
|
|
56885
57439
|
}
|
|
57440
|
+
return "codex-mcp-client";
|
|
57441
|
+
}
|
|
57442
|
+
var _state3 = null;
|
|
57443
|
+
var SERVER_START_MS = Date.now();
|
|
57444
|
+
var _watcher2 = null;
|
|
57445
|
+
var _pollTimer2 = null;
|
|
57446
|
+
var _flushInProgress3 = false;
|
|
57447
|
+
var _flushPending3 = false;
|
|
57448
|
+
var _flushDebounceTimer3 = null;
|
|
57449
|
+
function extractSessionId(filename) {
|
|
57450
|
+
const m = filename.match(/^rollout-.+?-([0-9a-f-]{36})\.jsonl$/);
|
|
57451
|
+
return m ? m[1] : null;
|
|
56886
57452
|
}
|
|
56887
57453
|
function* walkRollouts(root) {
|
|
56888
|
-
if (!
|
|
57454
|
+
if (!fs4.existsSync(root)) return;
|
|
56889
57455
|
const stack = [root];
|
|
56890
57456
|
while (stack.length) {
|
|
56891
57457
|
const dir = stack.pop();
|
|
56892
57458
|
let entries;
|
|
56893
57459
|
try {
|
|
56894
|
-
entries =
|
|
57460
|
+
entries = fs4.readdirSync(dir, { withFileTypes: true });
|
|
56895
57461
|
} catch {
|
|
56896
57462
|
continue;
|
|
56897
57463
|
}
|
|
56898
57464
|
for (const e of entries) {
|
|
56899
|
-
const p =
|
|
57465
|
+
const p = path5.join(dir, e.name);
|
|
56900
57466
|
if (e.isDirectory()) {
|
|
56901
57467
|
stack.push(p);
|
|
56902
57468
|
} else if (e.isFile() && e.name.startsWith("rollout-") && e.name.endsWith(".jsonl")) {
|
|
@@ -56905,43 +57471,34 @@ function* walkRollouts(root) {
|
|
|
56905
57471
|
}
|
|
56906
57472
|
}
|
|
56907
57473
|
}
|
|
56908
|
-
function
|
|
56909
|
-
const
|
|
56910
|
-
|
|
56911
|
-
_state2 = {
|
|
56912
|
-
cwd,
|
|
56913
|
-
cwdNormalized,
|
|
57474
|
+
function captureSessionStart3(_cwd) {
|
|
57475
|
+
const rootExists = fs4.existsSync(SESSIONS_ROOT);
|
|
57476
|
+
_state3 = {
|
|
56914
57477
|
rootExists,
|
|
56915
57478
|
files: /* @__PURE__ */ new Map()
|
|
56916
57479
|
};
|
|
56917
57480
|
if (!rootExists) {
|
|
56918
57481
|
return;
|
|
56919
57482
|
}
|
|
56920
|
-
let count = 0
|
|
57483
|
+
let count = 0;
|
|
56921
57484
|
for (const filePath of walkRollouts(SESSIONS_ROOT)) {
|
|
56922
57485
|
let stat;
|
|
56923
57486
|
try {
|
|
56924
|
-
stat =
|
|
57487
|
+
stat = fs4.statSync(filePath);
|
|
56925
57488
|
} catch {
|
|
56926
57489
|
continue;
|
|
56927
57490
|
}
|
|
56928
|
-
const sessionId = extractSessionId(
|
|
57491
|
+
const sessionId = extractSessionId(path5.basename(filePath));
|
|
56929
57492
|
if (!sessionId) continue;
|
|
56930
|
-
|
|
56931
|
-
let cwdMatched = null;
|
|
56932
|
-
if (meta) {
|
|
56933
|
-
cwdMatched = path4.resolve(meta.cwd) === cwdNormalized;
|
|
56934
|
-
}
|
|
56935
|
-
_state2.files.set(filePath, {
|
|
57493
|
+
_state3.files.set(filePath, {
|
|
56936
57494
|
cursorBytes: stat.size,
|
|
56937
57495
|
sessionId,
|
|
56938
|
-
|
|
57496
|
+
agentPlatform: lookupPlatform(sessionId),
|
|
56939
57497
|
currentModel: null
|
|
56940
57498
|
});
|
|
56941
57499
|
count++;
|
|
56942
|
-
if (cwdMatched) matched++;
|
|
56943
57500
|
}
|
|
56944
|
-
console.error(`\u{1F4DD} [Codex] capture armed: ${count} rollout(s)
|
|
57501
|
+
console.error(`\u{1F4DD} [Codex] capture armed: ${count} rollout(s)`);
|
|
56945
57502
|
armDirWatcher2();
|
|
56946
57503
|
}
|
|
56947
57504
|
function parseEntry2(line, byteOffset) {
|
|
@@ -56965,6 +57522,9 @@ function parseEntry2(line, byteOffset) {
|
|
|
56965
57522
|
}
|
|
56966
57523
|
const message = typeof payload.message === "string" ? payload.message.trim() : "";
|
|
56967
57524
|
if (!message) return null;
|
|
57525
|
+
if (message.startsWith("[external_agent_tool_call:") || message.startsWith("[external_agent_tool_call_response:")) {
|
|
57526
|
+
return null;
|
|
57527
|
+
}
|
|
56968
57528
|
return { byteOffset, role, message };
|
|
56969
57529
|
}
|
|
56970
57530
|
function extractModelFromTurnContext(line) {
|
|
@@ -56977,35 +57537,24 @@ function extractModelFromTurnContext(line) {
|
|
|
56977
57537
|
return null;
|
|
56978
57538
|
}
|
|
56979
57539
|
}
|
|
56980
|
-
function
|
|
56981
|
-
if (
|
|
56982
|
-
try {
|
|
56983
|
-
const entry = JSON.parse(line);
|
|
56984
|
-
if (entry.type !== "session_meta") return;
|
|
56985
|
-
const cwd = entry.payload?.cwd;
|
|
56986
|
-
if (typeof cwd !== "string") return;
|
|
56987
|
-
fileState.cwdMatched = path4.resolve(cwd) === cwdNormalized;
|
|
56988
|
-
} catch {
|
|
56989
|
-
}
|
|
56990
|
-
}
|
|
56991
|
-
async function flushDeltaForFile2(filePath, fileState, cwdNormalized) {
|
|
56992
|
-
if (!fs3.existsSync(filePath)) return { inserted: 0, skipped: 0, dedup: 0 };
|
|
57540
|
+
async function flushDeltaForFile3(filePath, fileState) {
|
|
57541
|
+
if (!fs4.existsSync(filePath)) return { inserted: 0, skipped: 0, dedup: 0 };
|
|
56993
57542
|
let stat;
|
|
56994
57543
|
try {
|
|
56995
|
-
stat =
|
|
57544
|
+
stat = fs4.statSync(filePath);
|
|
56996
57545
|
} catch {
|
|
56997
57546
|
return { inserted: 0, skipped: 0, dedup: 0 };
|
|
56998
57547
|
}
|
|
56999
57548
|
if (stat.size <= fileState.cursorBytes) return { inserted: 0, skipped: 0, dedup: 0 };
|
|
57000
57549
|
const length = stat.size - fileState.cursorBytes;
|
|
57001
|
-
const fd =
|
|
57550
|
+
const fd = fs4.openSync(filePath, "r");
|
|
57002
57551
|
let raw;
|
|
57003
57552
|
try {
|
|
57004
57553
|
const buf = Buffer.alloc(length);
|
|
57005
|
-
|
|
57554
|
+
fs4.readSync(fd, buf, 0, length, fileState.cursorBytes);
|
|
57006
57555
|
raw = buf.toString("utf-8");
|
|
57007
57556
|
} finally {
|
|
57008
|
-
|
|
57557
|
+
fs4.closeSync(fd);
|
|
57009
57558
|
}
|
|
57010
57559
|
const lastNl = raw.lastIndexOf("\n");
|
|
57011
57560
|
if (lastNl === -1) return { inserted: 0, skipped: 0, dedup: 0 };
|
|
@@ -57023,17 +57572,12 @@ async function flushDeltaForFile2(filePath, fileState, cwdNormalized) {
|
|
|
57023
57572
|
lineStartInRaw += Buffer.byteLength(line, "utf-8") + 1;
|
|
57024
57573
|
continue;
|
|
57025
57574
|
}
|
|
57026
|
-
applySessionMetaIfPresent(trimmed, fileState, cwdNormalized);
|
|
57027
57575
|
const m = extractModelFromTurnContext(trimmed);
|
|
57028
57576
|
if (m !== null) {
|
|
57029
57577
|
fileState.currentModel = m;
|
|
57030
57578
|
lineStartInRaw += Buffer.byteLength(line, "utf-8") + 1;
|
|
57031
57579
|
continue;
|
|
57032
57580
|
}
|
|
57033
|
-
if (fileState.cwdMatched === false) {
|
|
57034
|
-
lineStartInRaw += Buffer.byteLength(line, "utf-8") + 1;
|
|
57035
|
-
continue;
|
|
57036
|
-
}
|
|
57037
57581
|
const byteOffset = startBase + lineStartInRaw;
|
|
57038
57582
|
const parsed = parseEntry2(trimmed, byteOffset);
|
|
57039
57583
|
if (!parsed) {
|
|
@@ -57041,21 +57585,17 @@ async function flushDeltaForFile2(filePath, fileState, cwdNormalized) {
|
|
|
57041
57585
|
lineStartInRaw += Buffer.byteLength(line, "utf-8") + 1;
|
|
57042
57586
|
continue;
|
|
57043
57587
|
}
|
|
57044
|
-
if (fileState.cwdMatched !== true) {
|
|
57045
|
-
skipped++;
|
|
57046
|
-
lineStartInRaw += Buffer.byteLength(line, "utf-8") + 1;
|
|
57047
|
-
continue;
|
|
57048
|
-
}
|
|
57049
57588
|
const externalUuid = `codex:${fileState.sessionId}:${parsed.byteOffset}`;
|
|
57050
57589
|
const agentModel = parsed.role === "user" ? null : fileState.currentModel ?? "unknown";
|
|
57051
57590
|
try {
|
|
57052
57591
|
const result = await insertRawMemory({
|
|
57053
57592
|
user_id: userId,
|
|
57054
|
-
agent_platform:
|
|
57593
|
+
agent_platform: fileState.agentPlatform,
|
|
57055
57594
|
agent_model: agentModel,
|
|
57056
57595
|
role: parsed.role,
|
|
57057
57596
|
message: parsed.message,
|
|
57058
|
-
external_uuid: externalUuid
|
|
57597
|
+
external_uuid: externalUuid,
|
|
57598
|
+
device_name: DEVICE_NAME5
|
|
57059
57599
|
});
|
|
57060
57600
|
if (result.inserted) inserted++;
|
|
57061
57601
|
else dedup++;
|
|
@@ -57068,247 +57608,42 @@ async function flushDeltaForFile2(filePath, fileState, cwdNormalized) {
|
|
|
57068
57608
|
fileState.cursorBytes = newCursor;
|
|
57069
57609
|
return { inserted, skipped, dedup };
|
|
57070
57610
|
}
|
|
57071
|
-
async function
|
|
57072
|
-
if (!
|
|
57073
|
-
const cwdNormalized = _state2.cwdNormalized;
|
|
57611
|
+
async function flushAllFiles3() {
|
|
57612
|
+
if (!_state3 || !_state3.rootExists) return { inserted: 0, skipped: 0, dedup: 0 };
|
|
57074
57613
|
let totalI = 0, totalS = 0, totalD = 0;
|
|
57075
57614
|
for (const filePath of walkRollouts(SESSIONS_ROOT)) {
|
|
57076
|
-
let fileState =
|
|
57615
|
+
let fileState = _state3.files.get(filePath);
|
|
57077
57616
|
if (!fileState) {
|
|
57078
|
-
const sessionId = extractSessionId(
|
|
57617
|
+
const sessionId = extractSessionId(path5.basename(filePath));
|
|
57079
57618
|
if (!sessionId) continue;
|
|
57619
|
+
let stat = null;
|
|
57620
|
+
try {
|
|
57621
|
+
stat = fs4.statSync(filePath);
|
|
57622
|
+
} catch {
|
|
57623
|
+
}
|
|
57624
|
+
const fileBirthMs = stat ? stat.birthtimeMs || stat.ctimeMs : SERVER_START_MS;
|
|
57625
|
+
const isPreExisting = fileBirthMs < SERVER_START_MS - 5e3;
|
|
57626
|
+
const initialCursor = isPreExisting ? stat?.size ?? 0 : 0;
|
|
57627
|
+
if (isPreExisting) {
|
|
57628
|
+
console.error(`\u{1F4DD} [Codex] pre-existing rollout skipped (cursor=${initialCursor}): ${path5.basename(filePath)}`);
|
|
57629
|
+
} else {
|
|
57630
|
+
console.error(`\u{1F4DD} [Codex] new rollout detected: ${path5.basename(filePath)}`);
|
|
57631
|
+
}
|
|
57080
57632
|
fileState = {
|
|
57081
|
-
cursorBytes:
|
|
57633
|
+
cursorBytes: initialCursor,
|
|
57082
57634
|
sessionId,
|
|
57083
|
-
|
|
57635
|
+
agentPlatform: lookupPlatform(sessionId),
|
|
57084
57636
|
currentModel: null
|
|
57085
57637
|
};
|
|
57086
|
-
|
|
57087
|
-
console.error(`\u{1F4DD} [Codex] new rollout detected: ${path4.basename(filePath)}`);
|
|
57638
|
+
_state3.files.set(filePath, fileState);
|
|
57088
57639
|
}
|
|
57089
|
-
const r = await
|
|
57640
|
+
const r = await flushDeltaForFile3(filePath, fileState);
|
|
57090
57641
|
totalI += r.inserted;
|
|
57091
57642
|
totalS += r.skipped;
|
|
57092
57643
|
totalD += r.dedup;
|
|
57093
57644
|
}
|
|
57094
57645
|
return { inserted: totalI, skipped: totalS, dedup: totalD };
|
|
57095
57646
|
}
|
|
57096
|
-
async function flushWithMutex2() {
|
|
57097
|
-
if (_flushInProgress2) {
|
|
57098
|
-
_flushPending2 = true;
|
|
57099
|
-
return;
|
|
57100
|
-
}
|
|
57101
|
-
_flushInProgress2 = true;
|
|
57102
|
-
try {
|
|
57103
|
-
const r = await flushAllFiles2();
|
|
57104
|
-
if (r.inserted > 0 || r.skipped > 0 || r.dedup > 0) {
|
|
57105
|
-
console.error(`\u{1F4DD} [Codex] live flush: inserted=${r.inserted}, dedup=${r.dedup}, skipped=${r.skipped}`);
|
|
57106
|
-
}
|
|
57107
|
-
} catch (err2) {
|
|
57108
|
-
console.error("\u26A0\uFE0F [Codex] live flush error:", err2);
|
|
57109
|
-
} finally {
|
|
57110
|
-
_flushInProgress2 = false;
|
|
57111
|
-
if (_flushPending2) {
|
|
57112
|
-
_flushPending2 = false;
|
|
57113
|
-
setImmediate(() => {
|
|
57114
|
-
void flushWithMutex2();
|
|
57115
|
-
});
|
|
57116
|
-
}
|
|
57117
|
-
}
|
|
57118
|
-
}
|
|
57119
|
-
function scheduleFlush2() {
|
|
57120
|
-
if (_flushDebounceTimer2) clearTimeout(_flushDebounceTimer2);
|
|
57121
|
-
_flushDebounceTimer2 = setTimeout(() => {
|
|
57122
|
-
_flushDebounceTimer2 = null;
|
|
57123
|
-
void flushWithMutex2();
|
|
57124
|
-
}, FLUSH_DEBOUNCE_MS2);
|
|
57125
|
-
}
|
|
57126
|
-
function armDirWatcher2() {
|
|
57127
|
-
if (!_state2 || !_state2.rootExists) return;
|
|
57128
|
-
if (_watcher2) return;
|
|
57129
|
-
try {
|
|
57130
|
-
_watcher2 = fs3.watch(SESSIONS_ROOT, { recursive: true }, (_evt, filename) => {
|
|
57131
|
-
if (!filename) return;
|
|
57132
|
-
const base = path4.basename(filename);
|
|
57133
|
-
if (!base.startsWith("rollout-") || !base.endsWith(".jsonl")) return;
|
|
57134
|
-
scheduleFlush2();
|
|
57135
|
-
});
|
|
57136
|
-
console.error(`\u{1F4DD} [Codex] dir watcher armed (recursive)`);
|
|
57137
|
-
} catch (err2) {
|
|
57138
|
-
console.error("\u26A0\uFE0F [Codex] fs.watch failed (shutdown-only flush \uD3F4\uBC31):", err2);
|
|
57139
|
-
}
|
|
57140
|
-
}
|
|
57141
|
-
function disarmDirWatcher2() {
|
|
57142
|
-
if (_flushDebounceTimer2) {
|
|
57143
|
-
clearTimeout(_flushDebounceTimer2);
|
|
57144
|
-
_flushDebounceTimer2 = null;
|
|
57145
|
-
}
|
|
57146
|
-
if (_watcher2) {
|
|
57147
|
-
try {
|
|
57148
|
-
_watcher2.close();
|
|
57149
|
-
} catch {
|
|
57150
|
-
}
|
|
57151
|
-
_watcher2 = null;
|
|
57152
|
-
}
|
|
57153
|
-
}
|
|
57154
|
-
async function captureSessionEnd2() {
|
|
57155
|
-
disarmDirWatcher2();
|
|
57156
|
-
const waitStart = Date.now();
|
|
57157
|
-
while (_flushInProgress2 && Date.now() - waitStart < 2e3) {
|
|
57158
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
57159
|
-
}
|
|
57160
|
-
if (!_state2 || !_state2.rootExists) {
|
|
57161
|
-
return { inserted: 0, skipped: 0, error: "session not armed" };
|
|
57162
|
-
}
|
|
57163
|
-
_flushInProgress2 = true;
|
|
57164
|
-
try {
|
|
57165
|
-
const r = await flushAllFiles2();
|
|
57166
|
-
if (r.inserted > 0 || r.skipped > 0 || r.dedup > 0) {
|
|
57167
|
-
console.error(`\u{1F4DD} [Codex] final flush: inserted=${r.inserted}, dedup=${r.dedup}, skipped=${r.skipped}`);
|
|
57168
|
-
}
|
|
57169
|
-
return { inserted: r.inserted, skipped: r.skipped };
|
|
57170
|
-
} finally {
|
|
57171
|
-
_flushInProgress2 = false;
|
|
57172
|
-
}
|
|
57173
|
-
}
|
|
57174
|
-
|
|
57175
|
-
// src/auto_save/gemini_capture.ts
|
|
57176
|
-
import * as fs4 from "node:fs";
|
|
57177
|
-
import * as path5 from "node:path";
|
|
57178
|
-
import * as os4 from "node:os";
|
|
57179
|
-
import * as crypto2 from "node:crypto";
|
|
57180
|
-
var CLIENT_PLATFORM3 = "gemini-cli-mcp-client";
|
|
57181
|
-
var GEMINI_TMP_ROOT = path5.join(os4.homedir(), ".gemini", "tmp");
|
|
57182
|
-
var FLUSH_DEBOUNCE_MS3 = 200;
|
|
57183
|
-
var PARSE_RETRY_MS = 80;
|
|
57184
|
-
var _state3 = null;
|
|
57185
|
-
var _watchers = [];
|
|
57186
|
-
var _flushInProgress3 = false;
|
|
57187
|
-
var _flushPending3 = false;
|
|
57188
|
-
var _flushDebounceTimer3 = null;
|
|
57189
|
-
function extractShortId(filename) {
|
|
57190
|
-
const m = filename.match(/^session-.+?-([0-9a-f]+)\.json$/);
|
|
57191
|
-
return m ? m[1] : filename.replace(/\.json$/, "");
|
|
57192
|
-
}
|
|
57193
|
-
function deriveChatsDirCandidates(cwd) {
|
|
57194
|
-
const cwdHash = crypto2.createHash("sha256").update(cwd).digest("hex");
|
|
57195
|
-
const candidates = [
|
|
57196
|
-
path5.join(GEMINI_TMP_ROOT, cwdHash, "chats"),
|
|
57197
|
-
path5.join(GEMINI_TMP_ROOT, path5.basename(cwd), "chats")
|
|
57198
|
-
];
|
|
57199
|
-
return candidates.filter((d) => fs4.existsSync(d));
|
|
57200
|
-
}
|
|
57201
|
-
async function readSessionFile(filePath) {
|
|
57202
|
-
for (let attempt = 0; attempt < 2; attempt++) {
|
|
57203
|
-
try {
|
|
57204
|
-
const raw = fs4.readFileSync(filePath, "utf-8");
|
|
57205
|
-
if (!raw.trim()) return null;
|
|
57206
|
-
return JSON.parse(raw);
|
|
57207
|
-
} catch {
|
|
57208
|
-
if (attempt === 0) {
|
|
57209
|
-
await new Promise((r) => setTimeout(r, PARSE_RETRY_MS));
|
|
57210
|
-
continue;
|
|
57211
|
-
}
|
|
57212
|
-
return null;
|
|
57213
|
-
}
|
|
57214
|
-
}
|
|
57215
|
-
return null;
|
|
57216
|
-
}
|
|
57217
|
-
function parseMessage(msg, fallbackId) {
|
|
57218
|
-
let role;
|
|
57219
|
-
if (msg.type === "user") role = "user";
|
|
57220
|
-
else if (msg.type === "gemini") role = "assistant";
|
|
57221
|
-
else return null;
|
|
57222
|
-
let text = "";
|
|
57223
|
-
const c = msg.content;
|
|
57224
|
-
if (typeof c === "string") {
|
|
57225
|
-
text = c;
|
|
57226
|
-
} else if (Array.isArray(c)) {
|
|
57227
|
-
for (const block of c) {
|
|
57228
|
-
if (block && typeof block === "object" && typeof block.text === "string") {
|
|
57229
|
-
text += (text ? "\n" : "") + block.text;
|
|
57230
|
-
}
|
|
57231
|
-
}
|
|
57232
|
-
}
|
|
57233
|
-
text = text.trim();
|
|
57234
|
-
if (!text) return null;
|
|
57235
|
-
const msgId = typeof msg.id === "string" && msg.id ? msg.id : fallbackId;
|
|
57236
|
-
const agent_model = role === "assistant" && typeof msg.model === "string" ? msg.model : null;
|
|
57237
|
-
return { msgId, role, message: text, agent_model };
|
|
57238
|
-
}
|
|
57239
|
-
async function flushDeltaForFile3(filePath, fileState) {
|
|
57240
|
-
const session = await readSessionFile(filePath);
|
|
57241
|
-
if (!session || !Array.isArray(session.messages)) {
|
|
57242
|
-
return { inserted: 0, skipped: 0, dedup: 0 };
|
|
57243
|
-
}
|
|
57244
|
-
const messages = session.messages;
|
|
57245
|
-
if (messages.length <= fileState.cursorIndex) {
|
|
57246
|
-
return { inserted: 0, skipped: 0, dedup: 0 };
|
|
57247
|
-
}
|
|
57248
|
-
if (typeof session.sessionId === "string" && session.sessionId) {
|
|
57249
|
-
fileState.sessionId = session.sessionId;
|
|
57250
|
-
}
|
|
57251
|
-
const userId = await getDefaultUserId();
|
|
57252
|
-
let inserted = 0, skipped = 0, dedup = 0;
|
|
57253
|
-
for (let i = fileState.cursorIndex; i < messages.length; i++) {
|
|
57254
|
-
const msg = messages[i];
|
|
57255
|
-
const fallbackId = `${fileState.sessionId}-${i}`;
|
|
57256
|
-
const parsed = parseMessage(msg, fallbackId);
|
|
57257
|
-
if (!parsed) {
|
|
57258
|
-
skipped++;
|
|
57259
|
-
continue;
|
|
57260
|
-
}
|
|
57261
|
-
const externalUuid = `gemini:${fileState.sessionId}:${parsed.msgId}`;
|
|
57262
|
-
const agentModel = parsed.role === "user" ? null : parsed.agent_model ?? "unknown";
|
|
57263
|
-
try {
|
|
57264
|
-
const result = await insertRawMemory({
|
|
57265
|
-
user_id: userId,
|
|
57266
|
-
agent_platform: CLIENT_PLATFORM3,
|
|
57267
|
-
agent_model: agentModel,
|
|
57268
|
-
role: parsed.role,
|
|
57269
|
-
message: parsed.message,
|
|
57270
|
-
external_uuid: externalUuid
|
|
57271
|
-
});
|
|
57272
|
-
if (result.inserted) inserted++;
|
|
57273
|
-
else dedup++;
|
|
57274
|
-
} catch (err2) {
|
|
57275
|
-
console.error(`\u26A0\uFE0F [Gemini] insert failed at index ${i}:`, err2);
|
|
57276
|
-
skipped++;
|
|
57277
|
-
}
|
|
57278
|
-
}
|
|
57279
|
-
fileState.cursorIndex = messages.length;
|
|
57280
|
-
return { inserted, skipped, dedup };
|
|
57281
|
-
}
|
|
57282
|
-
async function flushAllFiles3() {
|
|
57283
|
-
if (!_state3) return { inserted: 0, skipped: 0, dedup: 0 };
|
|
57284
|
-
let totalI = 0, totalS = 0, totalD = 0;
|
|
57285
|
-
for (const dir of _state3.chatsDirs) {
|
|
57286
|
-
if (!fs4.existsSync(dir)) continue;
|
|
57287
|
-
let entries;
|
|
57288
|
-
try {
|
|
57289
|
-
entries = fs4.readdirSync(dir, { withFileTypes: true });
|
|
57290
|
-
} catch {
|
|
57291
|
-
continue;
|
|
57292
|
-
}
|
|
57293
|
-
for (const entry of entries) {
|
|
57294
|
-
if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
|
|
57295
|
-
if (!entry.name.startsWith("session-")) continue;
|
|
57296
|
-
const filePath = path5.join(dir, entry.name);
|
|
57297
|
-
let fileState = _state3.files.get(filePath);
|
|
57298
|
-
if (!fileState) {
|
|
57299
|
-
const sessionId = extractShortId(entry.name);
|
|
57300
|
-
fileState = { cursorIndex: 0, sessionId };
|
|
57301
|
-
_state3.files.set(filePath, fileState);
|
|
57302
|
-
console.error(`\u{1F4DD} [Gemini] new session detected: ${entry.name}`);
|
|
57303
|
-
}
|
|
57304
|
-
const r = await flushDeltaForFile3(filePath, fileState);
|
|
57305
|
-
totalI += r.inserted;
|
|
57306
|
-
totalS += r.skipped;
|
|
57307
|
-
totalD += r.dedup;
|
|
57308
|
-
}
|
|
57309
|
-
}
|
|
57310
|
-
return { inserted: totalI, skipped: totalS, dedup: totalD };
|
|
57311
|
-
}
|
|
57312
57647
|
async function flushWithMutex3() {
|
|
57313
57648
|
if (_flushInProgress3) {
|
|
57314
57649
|
_flushPending3 = true;
|
|
@@ -57318,10 +57653,10 @@ async function flushWithMutex3() {
|
|
|
57318
57653
|
try {
|
|
57319
57654
|
const r = await flushAllFiles3();
|
|
57320
57655
|
if (r.inserted > 0 || r.skipped > 0 || r.dedup > 0) {
|
|
57321
|
-
console.error(`\u{1F4DD} [
|
|
57656
|
+
console.error(`\u{1F4DD} [Codex] live flush: inserted=${r.inserted}, dedup=${r.dedup}, skipped=${r.skipped}`);
|
|
57322
57657
|
}
|
|
57323
57658
|
} catch (err2) {
|
|
57324
|
-
console.error("\u26A0\uFE0F [
|
|
57659
|
+
console.error("\u26A0\uFE0F [Codex] live flush error:", err2);
|
|
57325
57660
|
} finally {
|
|
57326
57661
|
_flushInProgress3 = false;
|
|
57327
57662
|
if (_flushPending3) {
|
|
@@ -57339,87 +57674,55 @@ function scheduleFlush3() {
|
|
|
57339
57674
|
void flushWithMutex3();
|
|
57340
57675
|
}, FLUSH_DEBOUNCE_MS3);
|
|
57341
57676
|
}
|
|
57342
|
-
function
|
|
57343
|
-
|
|
57344
|
-
|
|
57345
|
-
|
|
57346
|
-
|
|
57347
|
-
|
|
57348
|
-
|
|
57349
|
-
|
|
57350
|
-
|
|
57351
|
-
|
|
57352
|
-
|
|
57353
|
-
|
|
57354
|
-
|
|
57355
|
-
}
|
|
57356
|
-
for (const entry of entries) {
|
|
57357
|
-
if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
|
|
57358
|
-
if (!entry.name.startsWith("session-")) continue;
|
|
57359
|
-
const filePath = path5.join(dir, entry.name);
|
|
57360
|
-
let session = null;
|
|
57361
|
-
try {
|
|
57362
|
-
const raw = fs4.readFileSync(filePath, "utf-8");
|
|
57363
|
-
session = JSON.parse(raw);
|
|
57364
|
-
} catch {
|
|
57365
|
-
session = null;
|
|
57366
|
-
}
|
|
57367
|
-
const sessionId = session && typeof session.sessionId === "string" && session.sessionId || extractShortId(entry.name);
|
|
57368
|
-
const cursorIndex = session && Array.isArray(session.messages) ? session.messages.length : 0;
|
|
57369
|
-
_state3.files.set(filePath, { cursorIndex, sessionId });
|
|
57370
|
-
count++;
|
|
57371
|
-
}
|
|
57372
|
-
}
|
|
57373
|
-
console.error(
|
|
57374
|
-
`\u{1F4DD} [Gemini] capture armed: ${count} session(s) across ${chatsDirs.length} chats dir(s)`
|
|
57375
|
-
);
|
|
57376
|
-
armDirWatchers();
|
|
57377
|
-
}
|
|
57378
|
-
function armDirWatchers() {
|
|
57379
|
-
if (!_state3) return;
|
|
57380
|
-
if (_watchers.length > 0) return;
|
|
57381
|
-
for (const dir of _state3.chatsDirs) {
|
|
57382
|
-
try {
|
|
57383
|
-
const w = fs4.watch(dir, (_evt, filename) => {
|
|
57384
|
-
if (filename && !filename.endsWith(".json")) return;
|
|
57385
|
-
scheduleFlush3();
|
|
57386
|
-
});
|
|
57387
|
-
_watchers.push(w);
|
|
57388
|
-
} catch (err2) {
|
|
57389
|
-
console.error(`\u26A0\uFE0F [Gemini] fs.watch ${dir} failed:`, err2);
|
|
57390
|
-
}
|
|
57391
|
-
}
|
|
57392
|
-
if (_watchers.length > 0) {
|
|
57393
|
-
console.error(`\u{1F4DD} [Gemini] dir watcher(s) armed: ${_watchers.length}`);
|
|
57677
|
+
function armDirWatcher2() {
|
|
57678
|
+
if (!_state3 || !_state3.rootExists) return;
|
|
57679
|
+
if (_watcher2) return;
|
|
57680
|
+
try {
|
|
57681
|
+
_watcher2 = fs4.watch(SESSIONS_ROOT, { recursive: true }, (_evt, filename) => {
|
|
57682
|
+
if (!filename) return;
|
|
57683
|
+
const base = path5.basename(filename);
|
|
57684
|
+
if (!base.startsWith("rollout-") || !base.endsWith(".jsonl")) return;
|
|
57685
|
+
scheduleFlush3();
|
|
57686
|
+
});
|
|
57687
|
+
console.error(`\u{1F4DD} [Codex] dir watcher armed (recursive)`);
|
|
57688
|
+
} catch (err2) {
|
|
57689
|
+
console.error("\u26A0\uFE0F [Codex] fs.watch failed \u2014 polling only:", err2);
|
|
57394
57690
|
}
|
|
57691
|
+
_pollTimer2 = setInterval(() => {
|
|
57692
|
+
void flushWithMutex3();
|
|
57693
|
+
}, POLL_INTERVAL_MS2);
|
|
57395
57694
|
}
|
|
57396
|
-
function
|
|
57695
|
+
function disarmDirWatcher2() {
|
|
57397
57696
|
if (_flushDebounceTimer3) {
|
|
57398
57697
|
clearTimeout(_flushDebounceTimer3);
|
|
57399
57698
|
_flushDebounceTimer3 = null;
|
|
57400
57699
|
}
|
|
57401
|
-
|
|
57700
|
+
if (_pollTimer2) {
|
|
57701
|
+
clearInterval(_pollTimer2);
|
|
57702
|
+
_pollTimer2 = null;
|
|
57703
|
+
}
|
|
57704
|
+
if (_watcher2) {
|
|
57402
57705
|
try {
|
|
57403
|
-
|
|
57706
|
+
_watcher2.close();
|
|
57404
57707
|
} catch {
|
|
57405
57708
|
}
|
|
57709
|
+
_watcher2 = null;
|
|
57406
57710
|
}
|
|
57407
|
-
_watchers.length = 0;
|
|
57408
57711
|
}
|
|
57409
57712
|
async function captureSessionEnd3() {
|
|
57410
|
-
|
|
57713
|
+
disarmDirWatcher2();
|
|
57411
57714
|
const waitStart = Date.now();
|
|
57412
57715
|
while (_flushInProgress3 && Date.now() - waitStart < 2e3) {
|
|
57413
57716
|
await new Promise((r) => setTimeout(r, 50));
|
|
57414
57717
|
}
|
|
57415
|
-
if (!_state3 || _state3.
|
|
57718
|
+
if (!_state3 || !_state3.rootExists) {
|
|
57416
57719
|
return { inserted: 0, skipped: 0, error: "session not armed" };
|
|
57417
57720
|
}
|
|
57418
57721
|
_flushInProgress3 = true;
|
|
57419
57722
|
try {
|
|
57420
57723
|
const r = await flushAllFiles3();
|
|
57421
57724
|
if (r.inserted > 0 || r.skipped > 0 || r.dedup > 0) {
|
|
57422
|
-
console.error(`\u{1F4DD} [
|
|
57725
|
+
console.error(`\u{1F4DD} [Codex] final flush: inserted=${r.inserted}, dedup=${r.dedup}, skipped=${r.skipped}`);
|
|
57423
57726
|
}
|
|
57424
57727
|
return { inserted: r.inserted, skipped: r.skipped };
|
|
57425
57728
|
} finally {
|
|
@@ -57503,11 +57806,11 @@ async function shutdown(reason) {
|
|
|
57503
57806
|
try {
|
|
57504
57807
|
await Promise.race([
|
|
57505
57808
|
Promise.allSettled([
|
|
57506
|
-
captureSessionEnd(),
|
|
57507
57809
|
captureSessionEnd2(),
|
|
57508
|
-
captureSessionEnd3()
|
|
57810
|
+
captureSessionEnd3(),
|
|
57811
|
+
captureSessionEnd()
|
|
57509
57812
|
]),
|
|
57510
|
-
new Promise((
|
|
57813
|
+
new Promise((resolve) => setTimeout(resolve, 3e3))
|
|
57511
57814
|
]);
|
|
57512
57815
|
} catch (err2) {
|
|
57513
57816
|
console.error("\u{1F4DD} capture error (non-blocking):", err2);
|
|
@@ -57519,7 +57822,7 @@ async function shutdown(reason) {
|
|
|
57519
57822
|
try {
|
|
57520
57823
|
await Promise.race([
|
|
57521
57824
|
drainColdPath(),
|
|
57522
|
-
new Promise((
|
|
57825
|
+
new Promise((resolve) => setTimeout(resolve, 2e4))
|
|
57523
57826
|
]);
|
|
57524
57827
|
} catch (err2) {
|
|
57525
57828
|
console.error("\u{1F535} [ColdPath] drain error (non-blocking):", err2);
|
|
@@ -57527,7 +57830,7 @@ async function shutdown(reason) {
|
|
|
57527
57830
|
try {
|
|
57528
57831
|
await Promise.race([
|
|
57529
57832
|
db.close(),
|
|
57530
|
-
new Promise((
|
|
57833
|
+
new Promise((resolve) => setTimeout(resolve, 3e3))
|
|
57531
57834
|
]);
|
|
57532
57835
|
} catch (err2) {
|
|
57533
57836
|
console.error("Error during shutdown:", err2);
|
|
@@ -57592,7 +57895,7 @@ Configuration is loaded from (first hit wins):
|
|
|
57592
57895
|
Required settings:
|
|
57593
57896
|
DATABASE_URL=postgres://user:pass@host:5432/db?sslmode=require (or DB_HOST + DB_USER + DB_PASS + DB_NAME)
|
|
57594
57897
|
OPENAI_API_KEY=sk-... (embedding text-embedding-3-large)
|
|
57595
|
-
|
|
57898
|
+
XAI_API_KEY=... (Cold Path tagger/librarian \u2014 grok-4-1-fast-non-reasoning default)`);
|
|
57596
57899
|
}
|
|
57597
57900
|
async function runMcpServer() {
|
|
57598
57901
|
const instructions = await buildInstructions();
|
|
@@ -57611,9 +57914,9 @@ async function runMcpServer() {
|
|
|
57611
57914
|
registerTools(server);
|
|
57612
57915
|
installShutdownHandlers();
|
|
57613
57916
|
startParentWatchdog();
|
|
57614
|
-
captureSessionStart(process.cwd());
|
|
57615
57917
|
captureSessionStart2(process.cwd());
|
|
57616
57918
|
captureSessionStart3(process.cwd());
|
|
57919
|
+
captureSessionStart(process.cwd());
|
|
57617
57920
|
console.error("\u{1F680} Starting Memory MCP Server...");
|
|
57618
57921
|
if (process.env.SSH_ENABLED === "true" && !process.env.SSH_KEY_PATH) {
|
|
57619
57922
|
throw new Error("\u274C SSH_KEY_PATH is required when SSH is enabled");
|