blyuecode 0.3.5 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -21,20 +21,89 @@ export function loadSession(cwd = process.cwd()) {
21
21
  return null;
22
22
  }
23
23
  }
24
+ const MAX_SESSION_BYTES = Number(process.env.BLYUE_MAX_SESSION_BYTES) || 5 * 1024 * 1024; // 5 MB per session
25
+ const MAX_DIR_BYTES = Number(process.env.BLYUE_MAX_SESSIONS_DIR_BYTES) || 50 * 1024 * 1024; // 50 MB total
26
+ /**
27
+ * Trim the in-memory messages array so a session file never grows past
28
+ * MAX_SESSION_BYTES. Drops oldest non-system messages first, keeping a
29
+ * marker so the user (and the model) knows history was clipped.
30
+ */
31
+ function trimToBudget(messages) {
32
+ const size = (arr) => Buffer.byteLength(JSON.stringify(arr, null, 2), 'utf8');
33
+ if (size(messages) <= MAX_SESSION_BYTES)
34
+ return messages;
35
+ // Keep the most recent N messages, dropping from the front. Stop as
36
+ // soon as we're under budget; always keep at least the last 6.
37
+ let arr = messages.slice();
38
+ while (arr.length > 6 && size(arr) > MAX_SESSION_BYTES) {
39
+ arr.shift();
40
+ }
41
+ if (size(arr) > MAX_SESSION_BYTES) {
42
+ // Even the tail is too big — hard-truncate the largest text fields.
43
+ arr = arr.map((m) => {
44
+ const c = m.content;
45
+ if (typeof c === 'string' && c.length > 8000) {
46
+ return { ...m, content: c.slice(0, 2000) + '\n[truncated]\n' + c.slice(-4000) };
47
+ }
48
+ return m;
49
+ });
50
+ }
51
+ return [
52
+ { role: 'system', content: '[session trimmed by BLYUE to fit size limit]' },
53
+ ...arr,
54
+ ];
55
+ }
56
+ /**
57
+ * Evict oldest session files (never the active one) until the sessions
58
+ * directory is under MAX_DIR_BYTES.
59
+ */
60
+ function pruneDirectory(activeFile) {
61
+ if (!fs.existsSync(SESSIONS_DIR))
62
+ return;
63
+ const entries = fs.readdirSync(SESSIONS_DIR).filter((f) => f.endsWith('.json'));
64
+ let total = 0;
65
+ const stats = [];
66
+ for (const f of entries) {
67
+ const full = path.join(SESSIONS_DIR, f);
68
+ try {
69
+ const st = fs.statSync(full);
70
+ total += st.size;
71
+ stats.push({ file: f, size: st.size, mtime: st.mtimeMs });
72
+ }
73
+ catch { }
74
+ }
75
+ if (total <= MAX_DIR_BYTES)
76
+ return;
77
+ stats.sort((a, b) => a.mtime - b.mtime); // oldest first
78
+ for (const { file, size } of stats) {
79
+ if (total <= MAX_DIR_BYTES)
80
+ break;
81
+ if (path.join(SESSIONS_DIR, file) === activeFile)
82
+ continue;
83
+ try {
84
+ fs.unlinkSync(path.join(SESSIONS_DIR, file));
85
+ total -= size;
86
+ }
87
+ catch { }
88
+ }
89
+ }
24
90
  export function saveSession(messages, cwd = process.cwd()) {
25
91
  const file = sessionFile(cwd);
92
+ const trimmed = trimToBudget(messages);
26
93
  const data = {
27
94
  project: cwd,
28
95
  updatedAt: Date.now(),
29
- messages,
96
+ messages: trimmed,
30
97
  };
31
98
  fs.writeFileSync(file, JSON.stringify(data, null, 2), { mode: 0o600 });
99
+ pruneDirectory(file);
32
100
  }
33
101
  export function clearSession(cwd = process.cwd()) {
34
102
  const file = sessionFile(cwd);
35
103
  if (fs.existsSync(file))
36
104
  fs.unlinkSync(file);
37
105
  }
106
+ export { MAX_SESSION_BYTES, MAX_DIR_BYTES };
38
107
  export function listSessions() {
39
108
  if (!fs.existsSync(SESSIONS_DIR))
40
109
  return [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blyuecode",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "description": "BLYUE Code — a coding agent by Lumen",
5
5
  "type": "module",
6
6
  "license": "MIT",