mcp-agents-memory 0.9.2 → 0.9.5
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 +851 -541
- 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);
|
|
@@ -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 };
|
|
@@ -55331,7 +55336,8 @@ function inferProvider(modelName) {
|
|
|
55331
55336
|
}
|
|
55332
55337
|
var DEFAULTS = {
|
|
55333
55338
|
tagger: { provider: "xai", model_name: "grok-4-1-fast-non-reasoning" },
|
|
55334
|
-
librarian: { 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,50 +57111,58 @@ 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) {
|
|
57127
|
+
const slug = cwd.replace(/\//g, "-");
|
|
57128
|
+
const slugPath = path4.join(PROJECTS_ROOT, slug);
|
|
57129
|
+
try {
|
|
57130
|
+
if (fs3.statSync(slugPath).isDirectory()) return slugPath;
|
|
57131
|
+
} catch {
|
|
57132
|
+
}
|
|
56571
57133
|
let subdirs;
|
|
56572
57134
|
try {
|
|
56573
|
-
subdirs =
|
|
57135
|
+
subdirs = fs3.readdirSync(PROJECTS_ROOT, { withFileTypes: true });
|
|
56574
57136
|
} catch {
|
|
56575
57137
|
return null;
|
|
56576
57138
|
}
|
|
56577
57139
|
for (const subdir of subdirs) {
|
|
56578
57140
|
if (!subdir.isDirectory()) continue;
|
|
56579
|
-
const dirPath =
|
|
57141
|
+
const dirPath = path4.join(PROJECTS_ROOT, subdir.name);
|
|
56580
57142
|
let files;
|
|
56581
57143
|
try {
|
|
56582
|
-
files =
|
|
57144
|
+
files = fs3.readdirSync(dirPath, { withFileTypes: true });
|
|
56583
57145
|
} catch {
|
|
56584
57146
|
continue;
|
|
56585
57147
|
}
|
|
56586
|
-
const
|
|
56587
|
-
|
|
56588
|
-
|
|
56589
|
-
|
|
57148
|
+
for (const f of files) {
|
|
57149
|
+
if (!f.isFile() || !f.name.endsWith(".jsonl")) continue;
|
|
57150
|
+
const foundCwd = readCwdFromJsonl(path4.join(dirPath, f.name));
|
|
57151
|
+
if (foundCwd === cwd) return dirPath;
|
|
57152
|
+
}
|
|
56590
57153
|
}
|
|
56591
57154
|
return null;
|
|
56592
57155
|
}
|
|
56593
57156
|
function readCwdFromJsonl(jsonlPath) {
|
|
56594
57157
|
let fd;
|
|
56595
57158
|
try {
|
|
56596
|
-
fd =
|
|
57159
|
+
fd = fs3.openSync(jsonlPath, "r");
|
|
56597
57160
|
} catch {
|
|
56598
57161
|
return null;
|
|
56599
57162
|
}
|
|
56600
57163
|
try {
|
|
56601
57164
|
const buf = Buffer.alloc(2048);
|
|
56602
|
-
const bytesRead =
|
|
57165
|
+
const bytesRead = fs3.readSync(fd, buf, 0, 2048, 0);
|
|
56603
57166
|
const raw = buf.slice(0, bytesRead).toString("utf-8");
|
|
56604
57167
|
for (const line of raw.split("\n")) {
|
|
56605
57168
|
if (!line.trim()) continue;
|
|
@@ -56611,35 +57174,35 @@ function readCwdFromJsonl(jsonlPath) {
|
|
|
56611
57174
|
}
|
|
56612
57175
|
return null;
|
|
56613
57176
|
} finally {
|
|
56614
|
-
|
|
57177
|
+
fs3.closeSync(fd);
|
|
56615
57178
|
}
|
|
56616
57179
|
}
|
|
56617
|
-
function
|
|
56618
|
-
|
|
57180
|
+
function captureSessionStart2(cwd) {
|
|
57181
|
+
_state2 = { cwd, projectDir: null, files: /* @__PURE__ */ new Map() };
|
|
56619
57182
|
const projectDir = findProjectDir(cwd);
|
|
56620
57183
|
if (!projectDir) {
|
|
56621
57184
|
return;
|
|
56622
57185
|
}
|
|
56623
|
-
|
|
57186
|
+
_state2.projectDir = projectDir;
|
|
56624
57187
|
let entries;
|
|
56625
57188
|
try {
|
|
56626
|
-
entries =
|
|
57189
|
+
entries = fs3.readdirSync(projectDir, { withFileTypes: true });
|
|
56627
57190
|
} catch {
|
|
56628
57191
|
return;
|
|
56629
57192
|
}
|
|
56630
57193
|
for (const entry of entries) {
|
|
56631
57194
|
if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
|
|
56632
|
-
const jsonlPath =
|
|
57195
|
+
const jsonlPath = path4.join(projectDir, entry.name);
|
|
56633
57196
|
let stat;
|
|
56634
57197
|
try {
|
|
56635
|
-
stat =
|
|
57198
|
+
stat = fs3.statSync(jsonlPath);
|
|
56636
57199
|
} catch {
|
|
56637
57200
|
continue;
|
|
56638
57201
|
}
|
|
56639
57202
|
const sessionId = entry.name.replace(/\.jsonl$/, "");
|
|
56640
|
-
|
|
57203
|
+
_state2.files.set(jsonlPath, { cursorBytes: stat.size, sessionId, contentSeen: /* @__PURE__ */ new Set() });
|
|
56641
57204
|
}
|
|
56642
|
-
console.error(`\u{1F4DD} [JSONL] capture armed: ${
|
|
57205
|
+
console.error(`\u{1F4DD} [JSONL] capture armed: ${_state2.files.size} jsonl(s) in ${path4.basename(projectDir)}/`);
|
|
56643
57206
|
armDirWatcher();
|
|
56644
57207
|
}
|
|
56645
57208
|
function parseEntry(line) {
|
|
@@ -56678,24 +57241,24 @@ function parseEntry(line) {
|
|
|
56678
57241
|
agent_model
|
|
56679
57242
|
};
|
|
56680
57243
|
}
|
|
56681
|
-
async function
|
|
56682
|
-
if (!
|
|
57244
|
+
async function flushDeltaForFile2(jsonlPath, fileState) {
|
|
57245
|
+
if (!fs3.existsSync(jsonlPath)) return { inserted: 0, skipped: 0, dedup: 0 };
|
|
56683
57246
|
let stat;
|
|
56684
57247
|
try {
|
|
56685
|
-
stat =
|
|
57248
|
+
stat = fs3.statSync(jsonlPath);
|
|
56686
57249
|
} catch {
|
|
56687
57250
|
return { inserted: 0, skipped: 0, dedup: 0 };
|
|
56688
57251
|
}
|
|
56689
57252
|
if (stat.size <= fileState.cursorBytes) return { inserted: 0, skipped: 0, dedup: 0 };
|
|
56690
57253
|
const length = stat.size - fileState.cursorBytes;
|
|
56691
|
-
const fd =
|
|
57254
|
+
const fd = fs3.openSync(jsonlPath, "r");
|
|
56692
57255
|
let raw;
|
|
56693
57256
|
try {
|
|
56694
57257
|
const buf = Buffer.alloc(length);
|
|
56695
|
-
|
|
57258
|
+
fs3.readSync(fd, buf, 0, length, fileState.cursorBytes);
|
|
56696
57259
|
raw = buf.toString("utf-8");
|
|
56697
57260
|
} finally {
|
|
56698
|
-
|
|
57261
|
+
fs3.closeSync(fd);
|
|
56699
57262
|
}
|
|
56700
57263
|
const lastNl = raw.lastIndexOf("\n");
|
|
56701
57264
|
if (lastNl === -1) return { inserted: 0, skipped: 0, dedup: 0 };
|
|
@@ -56713,16 +57276,23 @@ async function flushDeltaForFile(jsonlPath, fileState) {
|
|
|
56713
57276
|
skipped++;
|
|
56714
57277
|
continue;
|
|
56715
57278
|
}
|
|
57279
|
+
const ck = `${parsed.role}::${parsed.message}`;
|
|
57280
|
+
if (fileState.contentSeen.has(ck)) {
|
|
57281
|
+
dedup++;
|
|
57282
|
+
continue;
|
|
57283
|
+
}
|
|
57284
|
+
fileState.contentSeen.add(ck);
|
|
56716
57285
|
const externalUuid = `claude-code:${fileState.sessionId}:${parsed.uuid}`;
|
|
56717
57286
|
const agentModel = parsed.role === "user" ? null : parsed.agent_model ?? "unknown";
|
|
56718
57287
|
try {
|
|
56719
57288
|
const result = await insertRawMemory({
|
|
56720
57289
|
user_id: userId,
|
|
56721
|
-
agent_platform:
|
|
57290
|
+
agent_platform: CLIENT_PLATFORM2,
|
|
56722
57291
|
agent_model: agentModel,
|
|
56723
57292
|
role: parsed.role,
|
|
56724
57293
|
message: parsed.message,
|
|
56725
|
-
external_uuid: externalUuid
|
|
57294
|
+
external_uuid: externalUuid,
|
|
57295
|
+
device_name: DEVICE_NAME4
|
|
56726
57296
|
});
|
|
56727
57297
|
if (result.inserted) inserted++;
|
|
56728
57298
|
else dedup++;
|
|
@@ -56734,71 +57304,71 @@ async function flushDeltaForFile(jsonlPath, fileState) {
|
|
|
56734
57304
|
fileState.cursorBytes = newCursor;
|
|
56735
57305
|
return { inserted, skipped, dedup };
|
|
56736
57306
|
}
|
|
56737
|
-
async function
|
|
56738
|
-
if (!
|
|
56739
|
-
const projectDir =
|
|
56740
|
-
if (!
|
|
57307
|
+
async function flushAllFiles2() {
|
|
57308
|
+
if (!_state2 || !_state2.projectDir) return { inserted: 0, skipped: 0, dedup: 0 };
|
|
57309
|
+
const projectDir = _state2.projectDir;
|
|
57310
|
+
if (!fs3.existsSync(projectDir)) return { inserted: 0, skipped: 0, dedup: 0 };
|
|
56741
57311
|
let entries;
|
|
56742
57312
|
try {
|
|
56743
|
-
entries =
|
|
57313
|
+
entries = fs3.readdirSync(projectDir, { withFileTypes: true });
|
|
56744
57314
|
} catch {
|
|
56745
57315
|
return { inserted: 0, skipped: 0, dedup: 0 };
|
|
56746
57316
|
}
|
|
56747
57317
|
let totalI = 0, totalS = 0, totalD = 0;
|
|
56748
57318
|
for (const entry of entries) {
|
|
56749
57319
|
if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
|
|
56750
|
-
const jsonlPath =
|
|
56751
|
-
let fileState =
|
|
57320
|
+
const jsonlPath = path4.join(projectDir, entry.name);
|
|
57321
|
+
let fileState = _state2.files.get(jsonlPath);
|
|
56752
57322
|
if (!fileState) {
|
|
56753
57323
|
const sessionId = entry.name.replace(/\.jsonl$/, "");
|
|
56754
|
-
fileState = { cursorBytes: 0, sessionId };
|
|
56755
|
-
|
|
57324
|
+
fileState = { cursorBytes: 0, sessionId, contentSeen: /* @__PURE__ */ new Set() };
|
|
57325
|
+
_state2.files.set(jsonlPath, fileState);
|
|
56756
57326
|
console.error(`\u{1F4DD} [JSONL] new session detected: ${entry.name}`);
|
|
56757
57327
|
}
|
|
56758
|
-
const r = await
|
|
57328
|
+
const r = await flushDeltaForFile2(jsonlPath, fileState);
|
|
56759
57329
|
totalI += r.inserted;
|
|
56760
57330
|
totalS += r.skipped;
|
|
56761
57331
|
totalD += r.dedup;
|
|
56762
57332
|
}
|
|
56763
57333
|
return { inserted: totalI, skipped: totalS, dedup: totalD };
|
|
56764
57334
|
}
|
|
56765
|
-
async function
|
|
56766
|
-
if (
|
|
56767
|
-
|
|
57335
|
+
async function flushWithMutex2() {
|
|
57336
|
+
if (_flushInProgress2) {
|
|
57337
|
+
_flushPending2 = true;
|
|
56768
57338
|
return;
|
|
56769
57339
|
}
|
|
56770
|
-
|
|
57340
|
+
_flushInProgress2 = true;
|
|
56771
57341
|
try {
|
|
56772
|
-
const r = await
|
|
57342
|
+
const r = await flushAllFiles2();
|
|
56773
57343
|
if (r.inserted > 0 || r.skipped > 0 || r.dedup > 0) {
|
|
56774
57344
|
console.error(`\u{1F4DD} [JSONL] live flush: inserted=${r.inserted}, dedup=${r.dedup}, skipped=${r.skipped}`);
|
|
56775
57345
|
}
|
|
56776
57346
|
} catch (err2) {
|
|
56777
57347
|
console.error("\u26A0\uFE0F [JSONL] live flush error:", err2);
|
|
56778
57348
|
} finally {
|
|
56779
|
-
|
|
56780
|
-
if (
|
|
56781
|
-
|
|
57349
|
+
_flushInProgress2 = false;
|
|
57350
|
+
if (_flushPending2) {
|
|
57351
|
+
_flushPending2 = false;
|
|
56782
57352
|
setImmediate(() => {
|
|
56783
|
-
void
|
|
57353
|
+
void flushWithMutex2();
|
|
56784
57354
|
});
|
|
56785
57355
|
}
|
|
56786
57356
|
}
|
|
56787
57357
|
}
|
|
56788
|
-
function
|
|
56789
|
-
if (
|
|
56790
|
-
|
|
56791
|
-
|
|
56792
|
-
void
|
|
56793
|
-
},
|
|
57358
|
+
function scheduleFlush2() {
|
|
57359
|
+
if (_flushDebounceTimer2) clearTimeout(_flushDebounceTimer2);
|
|
57360
|
+
_flushDebounceTimer2 = setTimeout(() => {
|
|
57361
|
+
_flushDebounceTimer2 = null;
|
|
57362
|
+
void flushWithMutex2();
|
|
57363
|
+
}, FLUSH_DEBOUNCE_MS2);
|
|
56794
57364
|
}
|
|
56795
57365
|
function armDirWatcher() {
|
|
56796
|
-
if (!
|
|
57366
|
+
if (!_state2 || !_state2.projectDir) return;
|
|
56797
57367
|
if (_watcher) return;
|
|
56798
57368
|
try {
|
|
56799
|
-
_watcher =
|
|
57369
|
+
_watcher = fs3.watch(_state2.projectDir, (_eventType, filename) => {
|
|
56800
57370
|
if (filename && !filename.endsWith(".jsonl")) return;
|
|
56801
|
-
|
|
57371
|
+
scheduleFlush2();
|
|
56802
57372
|
});
|
|
56803
57373
|
console.error(`\u{1F4DD} [JSONL] dir watcher armed`);
|
|
56804
57374
|
} catch (err2) {
|
|
@@ -56806,9 +57376,9 @@ function armDirWatcher() {
|
|
|
56806
57376
|
}
|
|
56807
57377
|
}
|
|
56808
57378
|
function disarmDirWatcher() {
|
|
56809
|
-
if (
|
|
56810
|
-
clearTimeout(
|
|
56811
|
-
|
|
57379
|
+
if (_flushDebounceTimer2) {
|
|
57380
|
+
clearTimeout(_flushDebounceTimer2);
|
|
57381
|
+
_flushDebounceTimer2 = null;
|
|
56812
57382
|
}
|
|
56813
57383
|
if (_watcher) {
|
|
56814
57384
|
try {
|
|
@@ -56818,85 +57388,88 @@ function disarmDirWatcher() {
|
|
|
56818
57388
|
_watcher = null;
|
|
56819
57389
|
}
|
|
56820
57390
|
}
|
|
56821
|
-
async function
|
|
57391
|
+
async function captureSessionEnd2() {
|
|
56822
57392
|
disarmDirWatcher();
|
|
56823
57393
|
const waitStart = Date.now();
|
|
56824
|
-
while (
|
|
57394
|
+
while (_flushInProgress2 && Date.now() - waitStart < 2e3) {
|
|
56825
57395
|
await new Promise((r) => setTimeout(r, 50));
|
|
56826
57396
|
}
|
|
56827
|
-
if (!
|
|
57397
|
+
if (!_state2 || !_state2.projectDir) {
|
|
56828
57398
|
return { inserted: 0, skipped: 0, error: "session not armed" };
|
|
56829
57399
|
}
|
|
56830
|
-
|
|
57400
|
+
_flushInProgress2 = true;
|
|
56831
57401
|
try {
|
|
56832
|
-
const r = await
|
|
57402
|
+
const r = await flushAllFiles2();
|
|
56833
57403
|
if (r.inserted > 0 || r.skipped > 0 || r.dedup > 0) {
|
|
56834
57404
|
console.error(`\u{1F4DD} [JSONL] final flush: inserted=${r.inserted}, dedup=${r.dedup}, skipped=${r.skipped}`);
|
|
56835
57405
|
}
|
|
56836
57406
|
return { inserted: r.inserted, skipped: r.skipped };
|
|
56837
57407
|
} finally {
|
|
56838
|
-
|
|
57408
|
+
_flushInProgress2 = false;
|
|
56839
57409
|
}
|
|
56840
57410
|
}
|
|
56841
57411
|
|
|
56842
57412
|
// 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
|
-
|
|
57413
|
+
import * as fs4 from "node:fs";
|
|
57414
|
+
import * as path5 from "node:path";
|
|
57415
|
+
import * as os7 from "node:os";
|
|
57416
|
+
import { spawnSync } from "node:child_process";
|
|
57417
|
+
var DEVICE_NAME5 = os7.hostname();
|
|
57418
|
+
var SESSIONS_ROOT = path5.join(os7.homedir(), ".codex", "sessions");
|
|
57419
|
+
var STATE_DB = path5.join(os7.homedir(), ".codex", "state_5.sqlite");
|
|
57420
|
+
var FLUSH_DEBOUNCE_MS3 = 200;
|
|
57421
|
+
var POLL_INTERVAL_MS2 = 3e3;
|
|
57422
|
+
function sourceToPlatform(source) {
|
|
57423
|
+
switch (source) {
|
|
57424
|
+
case "cli":
|
|
57425
|
+
return "codex-cli";
|
|
57426
|
+
case "vscode":
|
|
57427
|
+
return "codex-desktop";
|
|
57428
|
+
case "mcp":
|
|
57429
|
+
return "codex-mcp";
|
|
57430
|
+
case "exec":
|
|
57431
|
+
return "codex-exec";
|
|
57432
|
+
default:
|
|
57433
|
+
return "codex-mcp-client";
|
|
56864
57434
|
}
|
|
57435
|
+
}
|
|
57436
|
+
function lookupPlatform(sessionId) {
|
|
56865
57437
|
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 };
|
|
57438
|
+
const res = spawnSync(
|
|
57439
|
+
"sqlite3",
|
|
57440
|
+
[STATE_DB, `SELECT source FROM threads WHERE id='${sessionId}' LIMIT 1;`],
|
|
57441
|
+
{ encoding: "utf-8", timeout: 500 }
|
|
57442
|
+
);
|
|
57443
|
+
const source = (res.stdout ?? "").trim();
|
|
57444
|
+
if (source) return sourceToPlatform(source);
|
|
56878
57445
|
} catch {
|
|
56879
|
-
return null;
|
|
56880
|
-
} finally {
|
|
56881
|
-
try {
|
|
56882
|
-
fs3.closeSync(fd);
|
|
56883
|
-
} catch {
|
|
56884
|
-
}
|
|
56885
57446
|
}
|
|
57447
|
+
return "codex-mcp-client";
|
|
57448
|
+
}
|
|
57449
|
+
var _state3 = null;
|
|
57450
|
+
var SERVER_START_MS = Date.now();
|
|
57451
|
+
var _watcher2 = null;
|
|
57452
|
+
var _pollTimer2 = null;
|
|
57453
|
+
var _flushInProgress3 = false;
|
|
57454
|
+
var _flushPending3 = false;
|
|
57455
|
+
var _flushDebounceTimer3 = null;
|
|
57456
|
+
function extractSessionId(filename) {
|
|
57457
|
+
const m = filename.match(/^rollout-.+?-([0-9a-f-]{36})\.jsonl$/);
|
|
57458
|
+
return m ? m[1] : null;
|
|
56886
57459
|
}
|
|
56887
57460
|
function* walkRollouts(root) {
|
|
56888
|
-
if (!
|
|
57461
|
+
if (!fs4.existsSync(root)) return;
|
|
56889
57462
|
const stack = [root];
|
|
56890
57463
|
while (stack.length) {
|
|
56891
57464
|
const dir = stack.pop();
|
|
56892
57465
|
let entries;
|
|
56893
57466
|
try {
|
|
56894
|
-
entries =
|
|
57467
|
+
entries = fs4.readdirSync(dir, { withFileTypes: true });
|
|
56895
57468
|
} catch {
|
|
56896
57469
|
continue;
|
|
56897
57470
|
}
|
|
56898
57471
|
for (const e of entries) {
|
|
56899
|
-
const p =
|
|
57472
|
+
const p = path5.join(dir, e.name);
|
|
56900
57473
|
if (e.isDirectory()) {
|
|
56901
57474
|
stack.push(p);
|
|
56902
57475
|
} else if (e.isFile() && e.name.startsWith("rollout-") && e.name.endsWith(".jsonl")) {
|
|
@@ -56905,43 +57478,34 @@ function* walkRollouts(root) {
|
|
|
56905
57478
|
}
|
|
56906
57479
|
}
|
|
56907
57480
|
}
|
|
56908
|
-
function
|
|
56909
|
-
const
|
|
56910
|
-
|
|
56911
|
-
_state2 = {
|
|
56912
|
-
cwd,
|
|
56913
|
-
cwdNormalized,
|
|
57481
|
+
function captureSessionStart3(_cwd) {
|
|
57482
|
+
const rootExists = fs4.existsSync(SESSIONS_ROOT);
|
|
57483
|
+
_state3 = {
|
|
56914
57484
|
rootExists,
|
|
56915
57485
|
files: /* @__PURE__ */ new Map()
|
|
56916
57486
|
};
|
|
56917
57487
|
if (!rootExists) {
|
|
56918
57488
|
return;
|
|
56919
57489
|
}
|
|
56920
|
-
let count = 0
|
|
57490
|
+
let count = 0;
|
|
56921
57491
|
for (const filePath of walkRollouts(SESSIONS_ROOT)) {
|
|
56922
57492
|
let stat;
|
|
56923
57493
|
try {
|
|
56924
|
-
stat =
|
|
57494
|
+
stat = fs4.statSync(filePath);
|
|
56925
57495
|
} catch {
|
|
56926
57496
|
continue;
|
|
56927
57497
|
}
|
|
56928
|
-
const sessionId = extractSessionId(
|
|
57498
|
+
const sessionId = extractSessionId(path5.basename(filePath));
|
|
56929
57499
|
if (!sessionId) continue;
|
|
56930
|
-
|
|
56931
|
-
let cwdMatched = null;
|
|
56932
|
-
if (meta) {
|
|
56933
|
-
cwdMatched = path4.resolve(meta.cwd) === cwdNormalized;
|
|
56934
|
-
}
|
|
56935
|
-
_state2.files.set(filePath, {
|
|
57500
|
+
_state3.files.set(filePath, {
|
|
56936
57501
|
cursorBytes: stat.size,
|
|
56937
57502
|
sessionId,
|
|
56938
|
-
|
|
57503
|
+
agentPlatform: lookupPlatform(sessionId),
|
|
56939
57504
|
currentModel: null
|
|
56940
57505
|
});
|
|
56941
57506
|
count++;
|
|
56942
|
-
if (cwdMatched) matched++;
|
|
56943
57507
|
}
|
|
56944
|
-
console.error(`\u{1F4DD} [Codex] capture armed: ${count} rollout(s)
|
|
57508
|
+
console.error(`\u{1F4DD} [Codex] capture armed: ${count} rollout(s)`);
|
|
56945
57509
|
armDirWatcher2();
|
|
56946
57510
|
}
|
|
56947
57511
|
function parseEntry2(line, byteOffset) {
|
|
@@ -56965,6 +57529,9 @@ function parseEntry2(line, byteOffset) {
|
|
|
56965
57529
|
}
|
|
56966
57530
|
const message = typeof payload.message === "string" ? payload.message.trim() : "";
|
|
56967
57531
|
if (!message) return null;
|
|
57532
|
+
if (message.startsWith("[external_agent_tool_call:") || message.startsWith("[external_agent_tool_call_response:")) {
|
|
57533
|
+
return null;
|
|
57534
|
+
}
|
|
56968
57535
|
return { byteOffset, role, message };
|
|
56969
57536
|
}
|
|
56970
57537
|
function extractModelFromTurnContext(line) {
|
|
@@ -56977,35 +57544,24 @@ function extractModelFromTurnContext(line) {
|
|
|
56977
57544
|
return null;
|
|
56978
57545
|
}
|
|
56979
57546
|
}
|
|
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 };
|
|
57547
|
+
async function flushDeltaForFile3(filePath, fileState) {
|
|
57548
|
+
if (!fs4.existsSync(filePath)) return { inserted: 0, skipped: 0, dedup: 0 };
|
|
56993
57549
|
let stat;
|
|
56994
57550
|
try {
|
|
56995
|
-
stat =
|
|
57551
|
+
stat = fs4.statSync(filePath);
|
|
56996
57552
|
} catch {
|
|
56997
57553
|
return { inserted: 0, skipped: 0, dedup: 0 };
|
|
56998
57554
|
}
|
|
56999
57555
|
if (stat.size <= fileState.cursorBytes) return { inserted: 0, skipped: 0, dedup: 0 };
|
|
57000
57556
|
const length = stat.size - fileState.cursorBytes;
|
|
57001
|
-
const fd =
|
|
57557
|
+
const fd = fs4.openSync(filePath, "r");
|
|
57002
57558
|
let raw;
|
|
57003
57559
|
try {
|
|
57004
57560
|
const buf = Buffer.alloc(length);
|
|
57005
|
-
|
|
57561
|
+
fs4.readSync(fd, buf, 0, length, fileState.cursorBytes);
|
|
57006
57562
|
raw = buf.toString("utf-8");
|
|
57007
57563
|
} finally {
|
|
57008
|
-
|
|
57564
|
+
fs4.closeSync(fd);
|
|
57009
57565
|
}
|
|
57010
57566
|
const lastNl = raw.lastIndexOf("\n");
|
|
57011
57567
|
if (lastNl === -1) return { inserted: 0, skipped: 0, dedup: 0 };
|
|
@@ -57023,17 +57579,12 @@ async function flushDeltaForFile2(filePath, fileState, cwdNormalized) {
|
|
|
57023
57579
|
lineStartInRaw += Buffer.byteLength(line, "utf-8") + 1;
|
|
57024
57580
|
continue;
|
|
57025
57581
|
}
|
|
57026
|
-
applySessionMetaIfPresent(trimmed, fileState, cwdNormalized);
|
|
57027
57582
|
const m = extractModelFromTurnContext(trimmed);
|
|
57028
57583
|
if (m !== null) {
|
|
57029
57584
|
fileState.currentModel = m;
|
|
57030
57585
|
lineStartInRaw += Buffer.byteLength(line, "utf-8") + 1;
|
|
57031
57586
|
continue;
|
|
57032
57587
|
}
|
|
57033
|
-
if (fileState.cwdMatched === false) {
|
|
57034
|
-
lineStartInRaw += Buffer.byteLength(line, "utf-8") + 1;
|
|
57035
|
-
continue;
|
|
57036
|
-
}
|
|
57037
57588
|
const byteOffset = startBase + lineStartInRaw;
|
|
57038
57589
|
const parsed = parseEntry2(trimmed, byteOffset);
|
|
57039
57590
|
if (!parsed) {
|
|
@@ -57041,21 +57592,17 @@ async function flushDeltaForFile2(filePath, fileState, cwdNormalized) {
|
|
|
57041
57592
|
lineStartInRaw += Buffer.byteLength(line, "utf-8") + 1;
|
|
57042
57593
|
continue;
|
|
57043
57594
|
}
|
|
57044
|
-
if (fileState.cwdMatched !== true) {
|
|
57045
|
-
skipped++;
|
|
57046
|
-
lineStartInRaw += Buffer.byteLength(line, "utf-8") + 1;
|
|
57047
|
-
continue;
|
|
57048
|
-
}
|
|
57049
57595
|
const externalUuid = `codex:${fileState.sessionId}:${parsed.byteOffset}`;
|
|
57050
57596
|
const agentModel = parsed.role === "user" ? null : fileState.currentModel ?? "unknown";
|
|
57051
57597
|
try {
|
|
57052
57598
|
const result = await insertRawMemory({
|
|
57053
57599
|
user_id: userId,
|
|
57054
|
-
agent_platform:
|
|
57600
|
+
agent_platform: fileState.agentPlatform,
|
|
57055
57601
|
agent_model: agentModel,
|
|
57056
57602
|
role: parsed.role,
|
|
57057
57603
|
message: parsed.message,
|
|
57058
|
-
external_uuid: externalUuid
|
|
57604
|
+
external_uuid: externalUuid,
|
|
57605
|
+
device_name: DEVICE_NAME5
|
|
57059
57606
|
});
|
|
57060
57607
|
if (result.inserted) inserted++;
|
|
57061
57608
|
else dedup++;
|
|
@@ -57068,247 +57615,42 @@ async function flushDeltaForFile2(filePath, fileState, cwdNormalized) {
|
|
|
57068
57615
|
fileState.cursorBytes = newCursor;
|
|
57069
57616
|
return { inserted, skipped, dedup };
|
|
57070
57617
|
}
|
|
57071
|
-
async function
|
|
57072
|
-
if (!
|
|
57073
|
-
const cwdNormalized = _state2.cwdNormalized;
|
|
57618
|
+
async function flushAllFiles3() {
|
|
57619
|
+
if (!_state3 || !_state3.rootExists) return { inserted: 0, skipped: 0, dedup: 0 };
|
|
57074
57620
|
let totalI = 0, totalS = 0, totalD = 0;
|
|
57075
57621
|
for (const filePath of walkRollouts(SESSIONS_ROOT)) {
|
|
57076
|
-
let fileState =
|
|
57622
|
+
let fileState = _state3.files.get(filePath);
|
|
57077
57623
|
if (!fileState) {
|
|
57078
|
-
const sessionId = extractSessionId(
|
|
57624
|
+
const sessionId = extractSessionId(path5.basename(filePath));
|
|
57079
57625
|
if (!sessionId) continue;
|
|
57626
|
+
let stat = null;
|
|
57627
|
+
try {
|
|
57628
|
+
stat = fs4.statSync(filePath);
|
|
57629
|
+
} catch {
|
|
57630
|
+
}
|
|
57631
|
+
const fileBirthMs = stat ? stat.birthtimeMs || stat.ctimeMs : SERVER_START_MS;
|
|
57632
|
+
const isPreExisting = fileBirthMs < SERVER_START_MS - 5e3;
|
|
57633
|
+
const initialCursor = isPreExisting ? stat?.size ?? 0 : 0;
|
|
57634
|
+
if (isPreExisting) {
|
|
57635
|
+
console.error(`\u{1F4DD} [Codex] pre-existing rollout skipped (cursor=${initialCursor}): ${path5.basename(filePath)}`);
|
|
57636
|
+
} else {
|
|
57637
|
+
console.error(`\u{1F4DD} [Codex] new rollout detected: ${path5.basename(filePath)}`);
|
|
57638
|
+
}
|
|
57080
57639
|
fileState = {
|
|
57081
|
-
cursorBytes:
|
|
57640
|
+
cursorBytes: initialCursor,
|
|
57082
57641
|
sessionId,
|
|
57083
|
-
|
|
57642
|
+
agentPlatform: lookupPlatform(sessionId),
|
|
57084
57643
|
currentModel: null
|
|
57085
57644
|
};
|
|
57086
|
-
|
|
57087
|
-
console.error(`\u{1F4DD} [Codex] new rollout detected: ${path4.basename(filePath)}`);
|
|
57645
|
+
_state3.files.set(filePath, fileState);
|
|
57088
57646
|
}
|
|
57089
|
-
const r = await
|
|
57647
|
+
const r = await flushDeltaForFile3(filePath, fileState);
|
|
57090
57648
|
totalI += r.inserted;
|
|
57091
57649
|
totalS += r.skipped;
|
|
57092
57650
|
totalD += r.dedup;
|
|
57093
57651
|
}
|
|
57094
57652
|
return { inserted: totalI, skipped: totalS, dedup: totalD };
|
|
57095
57653
|
}
|
|
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
57654
|
async function flushWithMutex3() {
|
|
57313
57655
|
if (_flushInProgress3) {
|
|
57314
57656
|
_flushPending3 = true;
|
|
@@ -57318,10 +57660,10 @@ async function flushWithMutex3() {
|
|
|
57318
57660
|
try {
|
|
57319
57661
|
const r = await flushAllFiles3();
|
|
57320
57662
|
if (r.inserted > 0 || r.skipped > 0 || r.dedup > 0) {
|
|
57321
|
-
console.error(`\u{1F4DD} [
|
|
57663
|
+
console.error(`\u{1F4DD} [Codex] live flush: inserted=${r.inserted}, dedup=${r.dedup}, skipped=${r.skipped}`);
|
|
57322
57664
|
}
|
|
57323
57665
|
} catch (err2) {
|
|
57324
|
-
console.error("\u26A0\uFE0F [
|
|
57666
|
+
console.error("\u26A0\uFE0F [Codex] live flush error:", err2);
|
|
57325
57667
|
} finally {
|
|
57326
57668
|
_flushInProgress3 = false;
|
|
57327
57669
|
if (_flushPending3) {
|
|
@@ -57339,87 +57681,55 @@ function scheduleFlush3() {
|
|
|
57339
57681
|
void flushWithMutex3();
|
|
57340
57682
|
}, FLUSH_DEBOUNCE_MS3);
|
|
57341
57683
|
}
|
|
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}`);
|
|
57684
|
+
function armDirWatcher2() {
|
|
57685
|
+
if (!_state3 || !_state3.rootExists) return;
|
|
57686
|
+
if (_watcher2) return;
|
|
57687
|
+
try {
|
|
57688
|
+
_watcher2 = fs4.watch(SESSIONS_ROOT, { recursive: true }, (_evt, filename) => {
|
|
57689
|
+
if (!filename) return;
|
|
57690
|
+
const base = path5.basename(filename);
|
|
57691
|
+
if (!base.startsWith("rollout-") || !base.endsWith(".jsonl")) return;
|
|
57692
|
+
scheduleFlush3();
|
|
57693
|
+
});
|
|
57694
|
+
console.error(`\u{1F4DD} [Codex] dir watcher armed (recursive)`);
|
|
57695
|
+
} catch (err2) {
|
|
57696
|
+
console.error("\u26A0\uFE0F [Codex] fs.watch failed \u2014 polling only:", err2);
|
|
57394
57697
|
}
|
|
57698
|
+
_pollTimer2 = setInterval(() => {
|
|
57699
|
+
void flushWithMutex3();
|
|
57700
|
+
}, POLL_INTERVAL_MS2);
|
|
57395
57701
|
}
|
|
57396
|
-
function
|
|
57702
|
+
function disarmDirWatcher2() {
|
|
57397
57703
|
if (_flushDebounceTimer3) {
|
|
57398
57704
|
clearTimeout(_flushDebounceTimer3);
|
|
57399
57705
|
_flushDebounceTimer3 = null;
|
|
57400
57706
|
}
|
|
57401
|
-
|
|
57707
|
+
if (_pollTimer2) {
|
|
57708
|
+
clearInterval(_pollTimer2);
|
|
57709
|
+
_pollTimer2 = null;
|
|
57710
|
+
}
|
|
57711
|
+
if (_watcher2) {
|
|
57402
57712
|
try {
|
|
57403
|
-
|
|
57713
|
+
_watcher2.close();
|
|
57404
57714
|
} catch {
|
|
57405
57715
|
}
|
|
57716
|
+
_watcher2 = null;
|
|
57406
57717
|
}
|
|
57407
|
-
_watchers.length = 0;
|
|
57408
57718
|
}
|
|
57409
57719
|
async function captureSessionEnd3() {
|
|
57410
|
-
|
|
57720
|
+
disarmDirWatcher2();
|
|
57411
57721
|
const waitStart = Date.now();
|
|
57412
57722
|
while (_flushInProgress3 && Date.now() - waitStart < 2e3) {
|
|
57413
57723
|
await new Promise((r) => setTimeout(r, 50));
|
|
57414
57724
|
}
|
|
57415
|
-
if (!_state3 || _state3.
|
|
57725
|
+
if (!_state3 || !_state3.rootExists) {
|
|
57416
57726
|
return { inserted: 0, skipped: 0, error: "session not armed" };
|
|
57417
57727
|
}
|
|
57418
57728
|
_flushInProgress3 = true;
|
|
57419
57729
|
try {
|
|
57420
57730
|
const r = await flushAllFiles3();
|
|
57421
57731
|
if (r.inserted > 0 || r.skipped > 0 || r.dedup > 0) {
|
|
57422
|
-
console.error(`\u{1F4DD} [
|
|
57732
|
+
console.error(`\u{1F4DD} [Codex] final flush: inserted=${r.inserted}, dedup=${r.dedup}, skipped=${r.skipped}`);
|
|
57423
57733
|
}
|
|
57424
57734
|
return { inserted: r.inserted, skipped: r.skipped };
|
|
57425
57735
|
} finally {
|
|
@@ -57503,11 +57813,11 @@ async function shutdown(reason) {
|
|
|
57503
57813
|
try {
|
|
57504
57814
|
await Promise.race([
|
|
57505
57815
|
Promise.allSettled([
|
|
57506
|
-
captureSessionEnd(),
|
|
57507
57816
|
captureSessionEnd2(),
|
|
57508
|
-
captureSessionEnd3()
|
|
57817
|
+
captureSessionEnd3(),
|
|
57818
|
+
captureSessionEnd()
|
|
57509
57819
|
]),
|
|
57510
|
-
new Promise((
|
|
57820
|
+
new Promise((resolve) => setTimeout(resolve, 3e3))
|
|
57511
57821
|
]);
|
|
57512
57822
|
} catch (err2) {
|
|
57513
57823
|
console.error("\u{1F4DD} capture error (non-blocking):", err2);
|
|
@@ -57519,7 +57829,7 @@ async function shutdown(reason) {
|
|
|
57519
57829
|
try {
|
|
57520
57830
|
await Promise.race([
|
|
57521
57831
|
drainColdPath(),
|
|
57522
|
-
new Promise((
|
|
57832
|
+
new Promise((resolve) => setTimeout(resolve, 2e4))
|
|
57523
57833
|
]);
|
|
57524
57834
|
} catch (err2) {
|
|
57525
57835
|
console.error("\u{1F535} [ColdPath] drain error (non-blocking):", err2);
|
|
@@ -57527,7 +57837,7 @@ async function shutdown(reason) {
|
|
|
57527
57837
|
try {
|
|
57528
57838
|
await Promise.race([
|
|
57529
57839
|
db.close(),
|
|
57530
|
-
new Promise((
|
|
57840
|
+
new Promise((resolve) => setTimeout(resolve, 3e3))
|
|
57531
57841
|
]);
|
|
57532
57842
|
} catch (err2) {
|
|
57533
57843
|
console.error("Error during shutdown:", err2);
|
|
@@ -57611,9 +57921,9 @@ async function runMcpServer() {
|
|
|
57611
57921
|
registerTools(server);
|
|
57612
57922
|
installShutdownHandlers();
|
|
57613
57923
|
startParentWatchdog();
|
|
57614
|
-
captureSessionStart(process.cwd());
|
|
57615
57924
|
captureSessionStart2(process.cwd());
|
|
57616
57925
|
captureSessionStart3(process.cwd());
|
|
57926
|
+
captureSessionStart(process.cwd());
|
|
57617
57927
|
console.error("\u{1F680} Starting Memory MCP Server...");
|
|
57618
57928
|
if (process.env.SSH_ENABLED === "true" && !process.env.SSH_KEY_PATH) {
|
|
57619
57929
|
throw new Error("\u274C SSH_KEY_PATH is required when SSH is enabled");
|