perf: cheaper ingest — no re-parse of live hooked transcripts, delta hook writes, lighter pollers

- Session sync leaves a transcript alone while hooks feed its session live
  (active, newest event a hook's, touched within DASHBOARD_SYNC_LIVE_HOOK_MS,
  default 5 min) and catches it up once quiet; a hook-created session (which
  importSession skips anyway) is only snapshotted, never parsed.
- Hook writer keeps a per-transcript mark (applied after commit): an
  unchanged TranscriptCache result skips token/compaction/API-error/turn
  writes, a grown one walks only the entries after the last one handled.
- Codex usage: one stamp query per pass instead of one row per journal;
  between full passes (first + every 5 min) only the newest day folders and
  journals written in the last hour are statted; fork parents resolved from
  the table or a full walk.
- Cursor/Antigravity polls back off to 60 s after idle passes
  (server/lib/backoff-poll.js); lane sync runs only while another dashboard
  shares the data dir (checked every 30 s).
This commit is contained in:
2026-10-16 10:20:09 +07:00
parent ecbbb4dd8f
commit 5cb70d702b
15 changed files with 734 additions and 51 deletions
+18 -10
View File
@@ -425,6 +425,9 @@ function writeSession(f, conv, now) {
}
let running = null;
// Fresh sessions the last pass left for a later one to settle (the poll keeps
// its base pace while there are any).
let lastPending = 0;
/**
* One pass over every conversation file: a changed one is re-read, an
@@ -440,12 +443,16 @@ function ingestAntigravityUsage({
running = (async () => {
let read = 0;
let written = 0;
let pending = 0; // still-fresh sessions a later pass may have to settle
for (const f of conversationFiles()) {
try {
const seen = getSeen.get(f.path);
if (seen && seen.mtime_ms === f.mtime) {
const a = activity(f.id, f.mtime);
if (fresh(a, now)) continue;
if (fresh(a, now)) {
pending += 1;
continue;
}
const s = stmts.getSession.get(f.id);
if (s && s.tool === "antigravity" && s.status === "active") {
const at = new Date(a.last).toISOString();
@@ -486,6 +493,7 @@ function ingestAntigravityUsage({
}
await new Promise((r) => setImmediate(r));
}
lastPending = pending;
return { files: read, sessions: written };
})().finally(() => {
running = null;
@@ -504,16 +512,16 @@ function startAntigravityUsagePoll(broadcast, broadcastLane) {
if (!Number.isFinite(POLL_MS) || POLL_MS <= 0) return;
// Files an older build could not read get another try.
db.prepare("DELETE FROM antigravity_conversations WHERE session_id = ''").run();
const tick = () => {
ingestAntigravityUsage({ broadcast, broadcastLane })
.then((r) => {
// Slowed to once a minute after a pass that read nothing, wrote nothing and
// left no fresh session to settle; back to POLL_MS once one finds work.
require("./backoff-poll").startBackoffPoll(
() =>
ingestAntigravityUsage({ broadcast, broadcastLane }).then((r) => {
if (r.sessions > 0 && pricingSync.syncEnabled()) void pricingSync.syncPricing(db);
})
.catch(() => {});
};
tick();
const timer = setInterval(tick, POLL_MS);
if (timer.unref) timer.unref();
return r.files + r.sessions + lastPending > 0;
}),
{ baseMs: POLL_MS }
);
}
module.exports = {