mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-14 10:39:32 +01:00
fix(edit-history): review round 1 — record-bound deletion, load-time bounds, older reads per source
- The history is deleted when the session's persisted record goes (forgetEditHistory): kill via cleanupSession (a pinned record is only demoted and keeps it), the persisted-only DELETE, and stale cleanup at startup. After remove() neither a sync in flight nor a later request writes the file again; writes are serialized. - load() re-bounds a damaged file: cursors, pending Bash calls (MAX_PENDING_BASH) and subagent labels (MAX_AGENTS, oldest unused dropped first). - Load older reads one block of every transcript, budget split between them, and only the files it asked for; a block short of budget reads its end. A line longer than the budget is skipped going back as well (floorMidLine). - Concurrent first callers share one store; a forward request folded with an older one still reads forward. - Images outside the workspace get no thumbnail (one attachment id per render); a click opens the shared preview, which registers once with notify: false. - Docs: per-edit diff cap, deletion rule, load-time bounds, thumbnails, follow triggers. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
95e2272f89
commit
dcdeaab98d
17 files changed
+508
-158
No files matched your search
+14
-8
@@ -841,6 +841,7 @@ export class WebServer extends EventEmitter {
|
||||
addSession: this.registerSessionWithLayout.bind(this),
|
||||
tabLayouts: this.tabLayouts,
|
||||
cleanupSession: this.cleanupSession.bind(this),
|
||||
forgetEditHistory: this.forgetEditHistory.bind(this),
|
||||
setupSessionListeners: this.setupSessionListeners.bind(this),
|
||||
persistSessionState: this.persistSessionState.bind(this),
|
||||
persistSessionStateNow: this._persistSessionStateNow.bind(this),
|
||||
@@ -1576,13 +1577,6 @@ export class WebServer extends EventEmitter {
|
||||
// Stop transcript watcher
|
||||
this.stopTranscriptWatcher(sessionId);
|
||||
|
||||
// Edit history: a killed session takes its stored history (diffs can hold secrets) with it
|
||||
if (killMux) {
|
||||
void this.editHistorySync.remove(sessionId).catch((err) => {
|
||||
console.warn(`[EditHistory] Removing history of ${sessionId} failed:`, getErrorMessage(err));
|
||||
});
|
||||
}
|
||||
|
||||
// Stop and remove run summary tracker
|
||||
const summaryTracker = this.runSummaryTrackers.get(sessionId);
|
||||
if (summaryTracker) {
|
||||
@@ -1703,7 +1697,8 @@ export class WebServer extends EventEmitter {
|
||||
// Only remove from state.json if we're also killing the mux session.
|
||||
// When killMux=false (server shutdown), preserve state for recovery.
|
||||
if (killMux) {
|
||||
this.store.demoteOrRemoveSession(sessionId);
|
||||
// The edit history goes with the record; a pinned session's demoted record keeps it.
|
||||
if (this.store.demoteOrRemoveSession(sessionId) !== 'preserved') this.forgetEditHistory(sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2509,10 +2504,21 @@ export class WebServer extends EventEmitter {
|
||||
const lifecycleLog = getLifecycleLog();
|
||||
for (const s of result.cleaned) {
|
||||
lifecycleLog.log({ event: 'stale_cleaned', sessionId: s.id, name: s.name });
|
||||
this.forgetEditHistory(s.id);
|
||||
}
|
||||
return result.count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a session's edit history (diffs can hold secrets) once its record is gone: deleted
|
||||
* live (`cleanupSession`), deleted as a record only (DELETE route), or cleaned as stale.
|
||||
*/
|
||||
private forgetEditHistory(sessionId: string): void {
|
||||
void this.editHistorySync.remove(sessionId).catch((err) => {
|
||||
console.warn(`[EditHistory] Removing history of ${sessionId} failed:`, getErrorMessage(err));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get lightweight state for SSE init - excludes full terminal buffers
|
||||
* to prevent browser freezes. Terminal buffers are fetched on-demand.
|
||||
|
||||
Reference in new issue
Block a user