mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-06 15:39:41 +02:00
fix(web): keep the viewer's conversation anchor across a Codeman restart
start() reassigns _claudeSessionId to `resumeSessionId || id` on every launch, including the path that re-attaches to a mux session that outlived the restart. A pane whose CLI had moved on via /clear therefore came back pointing the response viewer at its pre-/clear transcript, and because Session.lastSubmitAt lived only in memory, the history correlation had nothing to correct it with until the user happened to type again — observed as hours of the eye showing a conversation the pane had long since left. Persist lastSubmitAt in SessionState, restore it in restoreMuxSessions(), and flush it when the viewer adopts (a /clear emits no completion event, which is the trigger that would otherwise have persisted it). Recovered panes now re-derive their live conversation on the viewer's first poll. Restoring a stale anchor is safe: the resolver already refuses a candidate transcript older than the one the pane is currently on, which is the shape of a respawn into a fresh conversation.
This commit is contained in:
@@ -499,6 +499,8 @@ export class Session extends EventEmitter {
|
||||
tmuxHistoryLimit?: number;
|
||||
/** Restored per-session attachment history. May include server-private external paths. */
|
||||
attachmentHistory?: SessionAttachmentHistoryItem[];
|
||||
/** Restored wall-clock ms of the pane's last Enter (see `lastSubmitAt`). */
|
||||
lastSubmitAt?: number;
|
||||
/** Remote execution metadata for sessions launched through SSH inside local tmux. */
|
||||
remote?: SessionRemote;
|
||||
/** Docker execution metadata for sessions launched inside a container via local tmux. */
|
||||
@@ -525,6 +527,12 @@ export class Session extends EventEmitter {
|
||||
this._lastActivityAt = this.createdAt;
|
||||
// Set claudeSessionId — when resuming, the Claude conversation ID is the resumed one.
|
||||
this._claudeSessionId = config.resumeSessionId || this.id;
|
||||
// Restored from state.json on boot recovery. start() resets _claudeSessionId
|
||||
// to the launch id even when re-attaching to a mux session whose CLI has
|
||||
// moved on (a `/clear` before the restart), so this anchor is what lets the
|
||||
// response viewer re-derive the live conversation without waiting for the
|
||||
// user to type again.
|
||||
this._lastSubmitAt = config.lastSubmitAt ?? 0;
|
||||
this._mux = config.mux || null;
|
||||
this._useMux = config.useMux ?? (this._mux !== null && this._mux.isAvailable());
|
||||
this._muxSession = config.muxSession || null;
|
||||
@@ -1119,6 +1127,7 @@ export class Session extends EventEmitter {
|
||||
// recovery can re-attach.
|
||||
respawnBlocked: this._respawnBlocked || undefined,
|
||||
attachmentHistory: this.attachmentHistory.length > 0 ? this.attachmentHistory : undefined,
|
||||
lastSubmitAt: this._lastSubmitAt || undefined,
|
||||
// envOverrides intentionally NOT on the public SessionState type — they must not
|
||||
// leak into SSE / GET /api/sessions broadcasts (schema allows OPENCODE_*, which
|
||||
// can carry secrets). For disk persistence, session-manager calls
|
||||
|
||||
Reference in New Issue
Block a user