@yeaft/webchat-agent 0.1.949 → 0.1.950
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/package.json +1 -1
- package/yeaft/cli.js +11 -2
- package/yeaft/debug-trace.js +58 -3
- package/yeaft/session.js +7 -5
package/package.json
CHANGED
package/yeaft/cli.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Features:
|
|
9
9
|
* --dry-run "prompt" — Assemble system prompt + messages, don't call LLM
|
|
10
|
-
* --trace stats|recent|search <keyword
|
|
10
|
+
* --trace stats|recent|search <keyword>|tools|compact — Query/maintain debug.db
|
|
11
11
|
* -i / --interactive — REPL mode with / commands
|
|
12
12
|
* <prompt> — One-shot query (Phase 1: engine.query)
|
|
13
13
|
* --skip-mcp — Skip MCP server connections (faster startup)
|
|
@@ -173,8 +173,17 @@ function handleTraceQuery(args, config) {
|
|
|
173
173
|
}
|
|
174
174
|
break;
|
|
175
175
|
}
|
|
176
|
+
case 'compact': {
|
|
177
|
+
const s = trace.stats();
|
|
178
|
+
console.log(`Compacting debug database (${(s.dbSizeBytes / 1048576).toFixed(1)} MB, ${s.turnCount} turns)...`);
|
|
179
|
+
console.log('This rebuilds the file and may take a while on a large DB. Do not interrupt.');
|
|
180
|
+
const { before, after } = trace.compact();
|
|
181
|
+
const saved = Math.max(0, before - after);
|
|
182
|
+
console.log(`Done. ${(before / 1048576).toFixed(1)} MB → ${(after / 1048576).toFixed(1)} MB (reclaimed ${(saved / 1048576).toFixed(1)} MB).`);
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
176
185
|
default:
|
|
177
|
-
throw new Error(`Unknown trace command: ${args.trace}. Available: stats, recent, search <keyword>, tools [name]`);
|
|
186
|
+
throw new Error(`Unknown trace command: ${args.trace}. Available: stats, recent, search <keyword>, tools [name], compact`);
|
|
178
187
|
}
|
|
179
188
|
} finally {
|
|
180
189
|
trace.close();
|
package/yeaft/debug-trace.js
CHANGED
|
@@ -176,6 +176,14 @@ export class DebugTrace {
|
|
|
176
176
|
constructor(dbPath) {
|
|
177
177
|
this.#dbPath = dbPath;
|
|
178
178
|
this.#db = new DatabaseSync(dbPath);
|
|
179
|
+
// INCREMENTAL auto-vacuum lets cleanup() return freed pages to the OS via
|
|
180
|
+
// `PRAGMA incremental_vacuum` instead of leaving the file at its historical
|
|
181
|
+
// peak. SQLite only honours an auto_vacuum *change* before the first table
|
|
182
|
+
// is created (a fresh DB) — on a pre-existing store it is a silent no-op, so
|
|
183
|
+
// existing databases keep their default `auto_vacuum=NONE` and are
|
|
184
|
+
// unaffected (they only shrink under a manual compact()/VACUUM). Databases
|
|
185
|
+
// created from this version on are self-trimming. Must precede SCHEMA.
|
|
186
|
+
this.#db.exec('PRAGMA auto_vacuum = INCREMENTAL');
|
|
179
187
|
this.#db.exec('PRAGMA journal_mode = WAL');
|
|
180
188
|
this.#db.exec('PRAGMA foreign_keys = ON');
|
|
181
189
|
this.#db.exec(SCHEMA);
|
|
@@ -608,11 +616,24 @@ export class DebugTrace {
|
|
|
608
616
|
// ─── Maintenance ─────────────────────────────────────────────
|
|
609
617
|
|
|
610
618
|
/**
|
|
611
|
-
* Delete data older than retentionDays
|
|
612
|
-
*
|
|
619
|
+
* Delete trajectory data older than retentionDays, then mark the freed pages
|
|
620
|
+
* reclaimable.
|
|
621
|
+
*
|
|
622
|
+
* The always-on trace stamps every turn with the cumulative request/response
|
|
623
|
+
* snapshot, so each long-session row is MB-scale and the file grows fast
|
|
624
|
+
* (a real deployment hit 5GB in 15 days). A plain DELETE marks pages free but
|
|
625
|
+
* leaves the file at its peak size; `PRAGMA incremental_vacuum` moves those
|
|
626
|
+
* pages onto the freelist for return to the OS — but only when the DB was
|
|
627
|
+
* created with `auto_vacuum=INCREMENTAL` (see constructor). On a legacy
|
|
628
|
+
* `auto_vacuum=NONE` store the vacuum is a harmless no-op, so this is safe to
|
|
629
|
+
* call unconditionally. Note: in WAL mode the on-disk file truncates at the
|
|
630
|
+
* next checkpoint (the running agent's automatic checkpoints handle this), so
|
|
631
|
+
* the page_count drops here but the file size catches up shortly after.
|
|
632
|
+
*
|
|
633
|
+
* @param {number} [retentionDays=10]
|
|
613
634
|
* @returns {{ deletedTurns: number, deletedTools: number, deletedEvents: number }}
|
|
614
635
|
*/
|
|
615
|
-
cleanup(retentionDays =
|
|
636
|
+
cleanup(retentionDays = 10) {
|
|
616
637
|
const cutoff = Date.now() - retentionDays * 24 * 60 * 60 * 1000;
|
|
617
638
|
const deletedTools = Number(this.#db.prepare(`
|
|
618
639
|
DELETE FROM trace_tools WHERE turn_id IN (
|
|
@@ -625,9 +646,42 @@ export class DebugTrace {
|
|
|
625
646
|
const deletedEvents = Number(this.#db.prepare(`
|
|
626
647
|
DELETE FROM trace_events WHERE created_at < ?
|
|
627
648
|
`).run(cutoff).changes);
|
|
649
|
+
// Reclaim freed pages (no-op on legacy auto_vacuum=NONE DBs). Wrapped so a
|
|
650
|
+
// vacuum failure can never mask a successful delete, but surfaced as a warn
|
|
651
|
+
// because this is the one operation the whole disk-growth fix relies on — a
|
|
652
|
+
// silent persistent failure would look exactly like "the fix works".
|
|
653
|
+
if (deletedTurns || deletedTools || deletedEvents) {
|
|
654
|
+
try { this.#db.exec('PRAGMA incremental_vacuum'); }
|
|
655
|
+
catch (err) { console.warn('[Yeaft] trace incremental_vacuum failed:', err?.message || err); }
|
|
656
|
+
}
|
|
628
657
|
return { deletedTurns, deletedTools, deletedEvents };
|
|
629
658
|
}
|
|
630
659
|
|
|
660
|
+
/**
|
|
661
|
+
* One-shot full compaction (VACUUM). Rebuilds the entire database file,
|
|
662
|
+
* reclaiming all free space AND converting a legacy `auto_vacuum=NONE` store
|
|
663
|
+
* to INCREMENTAL going forward. This is a HEAVY operation: it locks the DB
|
|
664
|
+
* and needs temporary scratch space up to the current file size, so it is
|
|
665
|
+
* NOT called automatically on session load — invoke it deliberately (e.g.
|
|
666
|
+
* from the `yeaft --trace` CLI) when an oversized legacy debug.db needs to be
|
|
667
|
+
* shrunk in place.
|
|
668
|
+
* @returns {{ before: number, after: number }} file size in bytes
|
|
669
|
+
*/
|
|
670
|
+
compact() {
|
|
671
|
+
let before = 0;
|
|
672
|
+
try { before = statSync(this.#dbPath).size; } catch { /* ignore */ }
|
|
673
|
+
this.#db.exec('PRAGMA auto_vacuum = INCREMENTAL');
|
|
674
|
+
this.#db.exec('VACUUM');
|
|
675
|
+
// In WAL mode VACUUM writes the rebuilt (smaller) DB into the -wal file;
|
|
676
|
+
// the main .db file does not shrink until a checkpoint folds the WAL back
|
|
677
|
+
// in. TRUNCATE checkpoints and resets the WAL so the on-disk size we report
|
|
678
|
+
// (and the user sees) reflects the reclaimed space immediately.
|
|
679
|
+
try { this.#db.exec('PRAGMA wal_checkpoint(TRUNCATE)'); } catch { /* best-effort */ }
|
|
680
|
+
let after = 0;
|
|
681
|
+
try { after = statSync(this.#dbPath).size; } catch { /* ignore */ }
|
|
682
|
+
return { before, after };
|
|
683
|
+
}
|
|
684
|
+
|
|
631
685
|
/** Delete all trace data. */
|
|
632
686
|
purge() {
|
|
633
687
|
this.#db.exec('DELETE FROM trace_tools');
|
|
@@ -695,6 +749,7 @@ export class NullTrace {
|
|
|
695
749
|
search() { return []; }
|
|
696
750
|
stats() { return { turnCount: 0, toolCount: 0, eventCount: 0, dbSizeBytes: 0 }; }
|
|
697
751
|
cleanup() { return { deletedTurns: 0, deletedTools: 0, deletedEvents: 0 }; }
|
|
752
|
+
compact() { return { before: 0, after: 0 }; }
|
|
698
753
|
purge() {}
|
|
699
754
|
close() {}
|
|
700
755
|
fetchRecentDebugHistory() { return { loops: [], turns: [], dreamEvents: [] }; }
|
package/yeaft/session.js
CHANGED
|
@@ -231,11 +231,13 @@ export async function loadSession(options = {}) {
|
|
|
231
231
|
enabled: true,
|
|
232
232
|
dbPath: join(yeaftDir, 'debug.db'),
|
|
233
233
|
});
|
|
234
|
-
// Bound disk growth: prune trajectories older than
|
|
235
|
-
// Cheap (indexed DELETE), runs once per process start,
|
|
236
|
-
//
|
|
237
|
-
//
|
|
238
|
-
|
|
234
|
+
// Bound disk growth: prune trajectories older than 10 days on session load.
|
|
235
|
+
// Cheap (indexed DELETE + incremental_vacuum), runs once per process start,
|
|
236
|
+
// not per turn. The always-on store stamps every turn with the *cumulative*
|
|
237
|
+
// request/response (each long-session row is ~MB), so without a tight TTL the
|
|
238
|
+
// file balloons — a real deployment hit 5GB in 15 days. 10 days keeps enough
|
|
239
|
+
// history for debug-panel replay while capping the steady-state footprint.
|
|
240
|
+
try { trace.cleanup?.(10); } catch (err) {
|
|
239
241
|
console.warn('[Yeaft] trace.cleanup failed:', err?.message || err);
|
|
240
242
|
}
|
|
241
243
|
|