@mindstudio-ai/remy 0.1.216 → 0.1.217
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/dist/headless.js +69 -17
- package/dist/index.js +72 -18
- package/package.json +1 -1
package/dist/headless.js
CHANGED
|
@@ -5723,8 +5723,8 @@ async function compactConversation(messages, apiConfig, system, tools2, model) {
|
|
|
5723
5723
|
log8.info("Compaction complete", { summaries: summaries.length });
|
|
5724
5724
|
return checkpointMessages;
|
|
5725
5725
|
}
|
|
5726
|
-
function findSafeInsertionPoint(messages) {
|
|
5727
|
-
let idx =
|
|
5726
|
+
function findSafeInsertionPoint(messages, fromIndex = messages.length) {
|
|
5727
|
+
let idx = fromIndex;
|
|
5728
5728
|
while (idx > 0) {
|
|
5729
5729
|
const msg = messages[idx - 1];
|
|
5730
5730
|
if (msg.role === "user" && msg.toolCallId) {
|
|
@@ -5733,7 +5733,7 @@ function findSafeInsertionPoint(messages) {
|
|
|
5733
5733
|
break;
|
|
5734
5734
|
}
|
|
5735
5735
|
}
|
|
5736
|
-
if (idx <
|
|
5736
|
+
if (idx < fromIndex && idx > 0) {
|
|
5737
5737
|
const msg = messages[idx - 1];
|
|
5738
5738
|
if (msg.role === "assistant" && Array.isArray(msg.content)) {
|
|
5739
5739
|
const hasToolUse = msg.content.some(
|
|
@@ -6279,6 +6279,8 @@ import path11 from "path";
|
|
|
6279
6279
|
var log12 = createLogger("session");
|
|
6280
6280
|
var SESSION_FILE = ".remy-session.json";
|
|
6281
6281
|
var ARCHIVE_DIR = ".logs/sessions";
|
|
6282
|
+
var ROTATE_THRESHOLD_BYTES = 32 * 1024 * 1024;
|
|
6283
|
+
var RETAIN_TAIL_BYTES = 16 * 1024 * 1024;
|
|
6282
6284
|
function loadSession(state) {
|
|
6283
6285
|
try {
|
|
6284
6286
|
const raw = fs22.readFileSync(SESSION_FILE, "utf-8");
|
|
@@ -6334,36 +6336,86 @@ function sanitizeMessages(messages) {
|
|
|
6334
6336
|
}
|
|
6335
6337
|
return result;
|
|
6336
6338
|
}
|
|
6339
|
+
function buildPayload(state) {
|
|
6340
|
+
const payload = { messages: state.messages };
|
|
6341
|
+
if (state.models && Object.keys(state.models).length > 0) {
|
|
6342
|
+
payload.models = state.models;
|
|
6343
|
+
}
|
|
6344
|
+
return payload;
|
|
6345
|
+
}
|
|
6346
|
+
function archiveMessages(messages, label, models) {
|
|
6347
|
+
fs22.mkdirSync(ARCHIVE_DIR, { recursive: true });
|
|
6348
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
6349
|
+
let dest = path11.join(ARCHIVE_DIR, `${label}-${ts}.json`);
|
|
6350
|
+
let n = 1;
|
|
6351
|
+
while (fs22.existsSync(dest)) {
|
|
6352
|
+
dest = path11.join(ARCHIVE_DIR, `${label}-${ts}-${n++}.json`);
|
|
6353
|
+
}
|
|
6354
|
+
const payload = { messages };
|
|
6355
|
+
if (models && Object.keys(models).length > 0) {
|
|
6356
|
+
payload.models = models;
|
|
6357
|
+
}
|
|
6358
|
+
fs22.writeFileSync(dest, JSON.stringify(payload), "utf-8");
|
|
6359
|
+
log12.info("Session archived", { label, dest, messageCount: messages.length });
|
|
6360
|
+
return dest;
|
|
6361
|
+
}
|
|
6362
|
+
function rotate(state) {
|
|
6363
|
+
const messages = state.messages;
|
|
6364
|
+
if (messages.length === 0) {
|
|
6365
|
+
return false;
|
|
6366
|
+
}
|
|
6367
|
+
let tailBytes = 0;
|
|
6368
|
+
let scrollbackStart = 0;
|
|
6369
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
6370
|
+
tailBytes += Buffer.byteLength(JSON.stringify(messages[i]), "utf-8") + 1;
|
|
6371
|
+
if (tailBytes >= RETAIN_TAIL_BYTES) {
|
|
6372
|
+
scrollbackStart = i;
|
|
6373
|
+
break;
|
|
6374
|
+
}
|
|
6375
|
+
}
|
|
6376
|
+
const checkpointIdx = findLastSummaryCheckpoint(messages, "conversation");
|
|
6377
|
+
let cut = checkpointIdx === -1 ? scrollbackStart : Math.min(scrollbackStart, checkpointIdx);
|
|
6378
|
+
cut = findSafeInsertionPoint(messages, cut);
|
|
6379
|
+
if (cut <= 0) {
|
|
6380
|
+
return false;
|
|
6381
|
+
}
|
|
6382
|
+
archiveMessages(messages.slice(0, cut), "rotated", state.models);
|
|
6383
|
+
state.messages = messages.slice(cut);
|
|
6384
|
+
log12.info("Session rotated", {
|
|
6385
|
+
archived: cut,
|
|
6386
|
+
retained: state.messages.length
|
|
6387
|
+
});
|
|
6388
|
+
return true;
|
|
6389
|
+
}
|
|
6337
6390
|
function saveSession(state) {
|
|
6338
6391
|
try {
|
|
6339
|
-
|
|
6340
|
-
if (
|
|
6341
|
-
|
|
6392
|
+
let serialized = JSON.stringify(buildPayload(state));
|
|
6393
|
+
if (Buffer.byteLength(serialized, "utf-8") > ROTATE_THRESHOLD_BYTES && rotate(state)) {
|
|
6394
|
+
serialized = JSON.stringify(buildPayload(state));
|
|
6342
6395
|
}
|
|
6343
|
-
fs22.writeFileSync(SESSION_FILE,
|
|
6396
|
+
fs22.writeFileSync(SESSION_FILE, serialized, "utf-8");
|
|
6344
6397
|
log12.info("Session saved", { messageCount: state.messages.length });
|
|
6345
6398
|
} catch (err) {
|
|
6346
6399
|
log12.warn("Session save failed", { error: err.message });
|
|
6347
6400
|
}
|
|
6348
6401
|
}
|
|
6349
6402
|
function clearSession(state) {
|
|
6403
|
+
try {
|
|
6404
|
+
if (state.messages.length > 0) {
|
|
6405
|
+
archiveMessages(state.messages, "cleared", state.models);
|
|
6406
|
+
}
|
|
6407
|
+
} catch (err) {
|
|
6408
|
+
log12.warn("Session archive on clear failed", { error: err.message });
|
|
6409
|
+
}
|
|
6350
6410
|
state.messages = [];
|
|
6351
6411
|
try {
|
|
6352
6412
|
if (fs22.existsSync(SESSION_FILE)) {
|
|
6353
|
-
fs22.
|
|
6354
|
-
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
6355
|
-
const dest = path11.join(ARCHIVE_DIR, `cleared-${ts}.json`);
|
|
6356
|
-
fs22.renameSync(SESSION_FILE, dest);
|
|
6357
|
-
log12.info("Session archived on clear", { dest });
|
|
6413
|
+
fs22.unlinkSync(SESSION_FILE);
|
|
6358
6414
|
}
|
|
6359
6415
|
} catch (err) {
|
|
6360
|
-
log12.warn("Session
|
|
6416
|
+
log12.warn("Session clear: could not remove live file", {
|
|
6361
6417
|
error: err.message
|
|
6362
6418
|
});
|
|
6363
|
-
try {
|
|
6364
|
-
fs22.unlinkSync(SESSION_FILE);
|
|
6365
|
-
} catch {
|
|
6366
|
-
}
|
|
6367
6419
|
}
|
|
6368
6420
|
}
|
|
6369
6421
|
|
package/dist/index.js
CHANGED
|
@@ -1584,8 +1584,8 @@ async function compactConversation(messages, apiConfig, system, tools2, model) {
|
|
|
1584
1584
|
log2.info("Compaction complete", { summaries: summaries.length });
|
|
1585
1585
|
return checkpointMessages;
|
|
1586
1586
|
}
|
|
1587
|
-
function findSafeInsertionPoint(messages) {
|
|
1588
|
-
let idx =
|
|
1587
|
+
function findSafeInsertionPoint(messages, fromIndex = messages.length) {
|
|
1588
|
+
let idx = fromIndex;
|
|
1589
1589
|
while (idx > 0) {
|
|
1590
1590
|
const msg = messages[idx - 1];
|
|
1591
1591
|
if (msg.role === "user" && msg.toolCallId) {
|
|
@@ -1594,7 +1594,7 @@ function findSafeInsertionPoint(messages) {
|
|
|
1594
1594
|
break;
|
|
1595
1595
|
}
|
|
1596
1596
|
}
|
|
1597
|
-
if (idx <
|
|
1597
|
+
if (idx < fromIndex && idx > 0) {
|
|
1598
1598
|
const msg = messages[idx - 1];
|
|
1599
1599
|
if (msg.role === "assistant" && Array.isArray(msg.content)) {
|
|
1600
1600
|
const hasToolUse = msg.content.some(
|
|
@@ -6706,46 +6706,100 @@ function sanitizeMessages(messages) {
|
|
|
6706
6706
|
}
|
|
6707
6707
|
return result;
|
|
6708
6708
|
}
|
|
6709
|
+
function buildPayload(state) {
|
|
6710
|
+
const payload = { messages: state.messages };
|
|
6711
|
+
if (state.models && Object.keys(state.models).length > 0) {
|
|
6712
|
+
payload.models = state.models;
|
|
6713
|
+
}
|
|
6714
|
+
return payload;
|
|
6715
|
+
}
|
|
6716
|
+
function archiveMessages(messages, label, models) {
|
|
6717
|
+
fs20.mkdirSync(ARCHIVE_DIR, { recursive: true });
|
|
6718
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
6719
|
+
let dest = path9.join(ARCHIVE_DIR, `${label}-${ts}.json`);
|
|
6720
|
+
let n = 1;
|
|
6721
|
+
while (fs20.existsSync(dest)) {
|
|
6722
|
+
dest = path9.join(ARCHIVE_DIR, `${label}-${ts}-${n++}.json`);
|
|
6723
|
+
}
|
|
6724
|
+
const payload = { messages };
|
|
6725
|
+
if (models && Object.keys(models).length > 0) {
|
|
6726
|
+
payload.models = models;
|
|
6727
|
+
}
|
|
6728
|
+
fs20.writeFileSync(dest, JSON.stringify(payload), "utf-8");
|
|
6729
|
+
log9.info("Session archived", { label, dest, messageCount: messages.length });
|
|
6730
|
+
return dest;
|
|
6731
|
+
}
|
|
6732
|
+
function rotate(state) {
|
|
6733
|
+
const messages = state.messages;
|
|
6734
|
+
if (messages.length === 0) {
|
|
6735
|
+
return false;
|
|
6736
|
+
}
|
|
6737
|
+
let tailBytes = 0;
|
|
6738
|
+
let scrollbackStart = 0;
|
|
6739
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
6740
|
+
tailBytes += Buffer.byteLength(JSON.stringify(messages[i]), "utf-8") + 1;
|
|
6741
|
+
if (tailBytes >= RETAIN_TAIL_BYTES) {
|
|
6742
|
+
scrollbackStart = i;
|
|
6743
|
+
break;
|
|
6744
|
+
}
|
|
6745
|
+
}
|
|
6746
|
+
const checkpointIdx = findLastSummaryCheckpoint(messages, "conversation");
|
|
6747
|
+
let cut = checkpointIdx === -1 ? scrollbackStart : Math.min(scrollbackStart, checkpointIdx);
|
|
6748
|
+
cut = findSafeInsertionPoint(messages, cut);
|
|
6749
|
+
if (cut <= 0) {
|
|
6750
|
+
return false;
|
|
6751
|
+
}
|
|
6752
|
+
archiveMessages(messages.slice(0, cut), "rotated", state.models);
|
|
6753
|
+
state.messages = messages.slice(cut);
|
|
6754
|
+
log9.info("Session rotated", {
|
|
6755
|
+
archived: cut,
|
|
6756
|
+
retained: state.messages.length
|
|
6757
|
+
});
|
|
6758
|
+
return true;
|
|
6759
|
+
}
|
|
6709
6760
|
function saveSession(state) {
|
|
6710
6761
|
try {
|
|
6711
|
-
|
|
6712
|
-
if (
|
|
6713
|
-
|
|
6762
|
+
let serialized = JSON.stringify(buildPayload(state));
|
|
6763
|
+
if (Buffer.byteLength(serialized, "utf-8") > ROTATE_THRESHOLD_BYTES && rotate(state)) {
|
|
6764
|
+
serialized = JSON.stringify(buildPayload(state));
|
|
6714
6765
|
}
|
|
6715
|
-
fs20.writeFileSync(SESSION_FILE,
|
|
6766
|
+
fs20.writeFileSync(SESSION_FILE, serialized, "utf-8");
|
|
6716
6767
|
log9.info("Session saved", { messageCount: state.messages.length });
|
|
6717
6768
|
} catch (err) {
|
|
6718
6769
|
log9.warn("Session save failed", { error: err.message });
|
|
6719
6770
|
}
|
|
6720
6771
|
}
|
|
6721
6772
|
function clearSession(state) {
|
|
6773
|
+
try {
|
|
6774
|
+
if (state.messages.length > 0) {
|
|
6775
|
+
archiveMessages(state.messages, "cleared", state.models);
|
|
6776
|
+
}
|
|
6777
|
+
} catch (err) {
|
|
6778
|
+
log9.warn("Session archive on clear failed", { error: err.message });
|
|
6779
|
+
}
|
|
6722
6780
|
state.messages = [];
|
|
6723
6781
|
try {
|
|
6724
6782
|
if (fs20.existsSync(SESSION_FILE)) {
|
|
6725
|
-
fs20.
|
|
6726
|
-
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
6727
|
-
const dest = path9.join(ARCHIVE_DIR, `cleared-${ts}.json`);
|
|
6728
|
-
fs20.renameSync(SESSION_FILE, dest);
|
|
6729
|
-
log9.info("Session archived on clear", { dest });
|
|
6783
|
+
fs20.unlinkSync(SESSION_FILE);
|
|
6730
6784
|
}
|
|
6731
6785
|
} catch (err) {
|
|
6732
|
-
log9.warn("Session
|
|
6786
|
+
log9.warn("Session clear: could not remove live file", {
|
|
6733
6787
|
error: err.message
|
|
6734
6788
|
});
|
|
6735
|
-
try {
|
|
6736
|
-
fs20.unlinkSync(SESSION_FILE);
|
|
6737
|
-
} catch {
|
|
6738
|
-
}
|
|
6739
6789
|
}
|
|
6740
6790
|
}
|
|
6741
|
-
var log9, SESSION_FILE, ARCHIVE_DIR;
|
|
6791
|
+
var log9, SESSION_FILE, ARCHIVE_DIR, ROTATE_THRESHOLD_BYTES, RETAIN_TAIL_BYTES;
|
|
6742
6792
|
var init_session = __esm({
|
|
6743
6793
|
"src/session.ts"() {
|
|
6744
6794
|
"use strict";
|
|
6745
6795
|
init_logger();
|
|
6796
|
+
init_compaction();
|
|
6797
|
+
init_cleanMessages();
|
|
6746
6798
|
log9 = createLogger("session");
|
|
6747
6799
|
SESSION_FILE = ".remy-session.json";
|
|
6748
6800
|
ARCHIVE_DIR = ".logs/sessions";
|
|
6801
|
+
ROTATE_THRESHOLD_BYTES = 32 * 1024 * 1024;
|
|
6802
|
+
RETAIN_TAIL_BYTES = 16 * 1024 * 1024;
|
|
6749
6803
|
}
|
|
6750
6804
|
});
|
|
6751
6805
|
|