Review round 2: with the stored conversation tried before the launch id, a
respawn that resumed the launch conversation after a /clear was missed once
claude exited again: the cleared-to conversation still had a file and won,
so the edits made after the resume were never read. No fixed order of the
ids is right both after a /clear and after such a resume. Without a live
conversation the lookup now takes the most recently written transcript of
all known ids (stored, claudeSessionId, id); the live one still goes first.
Also from the review:
- the remote no-transcript note and the docs name login profiles too
(.profile, .bash_profile): sshd's `bash -c` reads neither, like .zshrc;
- a wiring guard pins that server.ts looks a remote transcript up with
remoteTranscriptIds and leaves the local lookup on the hook path;
- the browser tests undo the injected remote session in a finally.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Review findings on the remote edit history:
- A forward read may carry 13 MiB and "Load older" 29 MiB; the flat 30 s
read timeout cut those off below ~1 MiB/s. The timeout now adds the
transfer time at 256 KiB/s (remoteEditSourcesTimeoutMs). It ends a hung
read, it does not cut a slow one.
- The transcript lookup looked only under $HOME/.claude. It now uses an
absolute CLAUDE_CONFIG_DIR of the ssh command's environment, else
$HOME/.claude, for both the projects and the live sessions/<pid>.json.
A value set only in .zshrc stays invisible (the lookup is a plain sh);
documented, and the panel's remote no-transcript note says so instead
of promising the next prompt (a remote host sends no hooks).
- After a remote /clear claudeSessionId stays the launch id, so once the
remote claude exited the lookup fell back to the cleared conversation
and the panel flipped back. The sync now hands the lookup the stored
conversation, tried right after the live one (remoteTranscriptIds).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The edit history panel now covers remote claude sessions. The sync stays
path-based; its deps get the session and server.ts routes them to the local
reader or to two new ssh reads in remote-files.ts:
- remoteLocateClaudeTranscript: one round trip; the conversation the remote
pane's claude writes now (~/.claude/sessions/<pid>.json via the pane's
process tree, since remote hosts send no hooks), else claudeSessionId,
else the session id, newest $HOME/.claude/projects/*/<id>.jsonl.
- remoteReadClaudeEditSources: one round trip per read pass, with the local
reader's semantics (offset clamp, block end at offset+len, new files read
their tail, shared main-first budget, onlyRequested, 8 KiB meta cap).
So /clear catch-up, "Load older", cursors and the store LRU work remotely
unchanged. Reads use buildRemoteFileCommand + runWithRemoteSshLimit only:
a sleeping host is a connect timeout -> unavailable, stored entries kept.
Tests: both scripts run on a real sh against a temp HOME; the read is
compared byte for byte with readClaudeEditSources on 60 seeded random
transcripts. A transitive import guard pins that the edit-history read path
reaches neither remote-wake.ts nor session-routes.ts. Browser: a remote
session lists images without thumbnails, a click previews. Docs updated.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Forward: after a line longer than the budget, the next line is read from
its start instead of being skipped with it when it crosses the chunk end.
- Backward: the line before a skipped long line is read whole; a floor that
turned out to be a line start no longer stalls.
- A chunk the shared forward budget cut short is not applied (it read as one
over-long line) but read in a later pass, so a first open of a long main
transcript reaches the subagents in the same sync.
- Load older reads a full budget block of up to three transcripts per click,
taking turns, instead of splitting one budget (a normal Write longer than
the share was skipped for good).
- A subagent transcript that vanished loses its cursor, so Load older does not
stay on forever.
- POST /api/cleanup-state forgets the history of every record it drops; a
startup sweep deletes edit-history files no session or record owns and
stale tmp files of other processes.
- load() rebuilds entries from known fields (string diff lines, per-edit cap
re-applied, ids counted against the byte cap), bounds pending Bash fields
and agent ids, and does not parse a file over 32 MiB.
- No re-read after Codeman's own keep-alive turn.
- Tests: regression tests for each of the above (they fail on the previous
commit); the remove-during-write test now meets the write under way; the
sync and route tests remove their stores before the temp dir, so no late
save recreates it in /tmp.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
transcript:complete fires on a `result` line, and claude's interactive transcript has
none (checked against a live session: user/assistant/system turn_duration, no result),
so an open panel never re-read at the end of a turn. A background subagent writes after
the Agent tool end that started it, so its edits stayed missing until the next edit-
capable tool end or a manual refresh.
The panel now re-reads on its session's session:idle, through _onSessionIdle (the SSE
handler map holds one handler per event). Live: a backgrounded subagent's Write shows
up in the open panel at the end of the turn, without a click.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- 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>
Part 1 of #643 (local sessions only; remote follows as its own PR). An
opt-in, per-device header button opens a panel with the active session's
edits, newest first: every Edit/Write/MultiEdit/NotebookEdit of the main
agent and its subagents with its diff, plus images a Bash call rendered or
the agent sent. Remote, Docker and non-claude sessions answer `unsupported`.
- Reads only on request: `?sync=1` when the panel opens; while it is open on
that session it re-reads after an edit-capable `transcript:tool_end` or a
`transcript:complete` from the existing transcript watcher. No hook, timer,
second watcher or new SSE event; a wiring guard pins it.
- Main transcript: the path the transcript watcher follows (hook-reported),
else `<id>.jsonl` under the session's own CLAUDE_CONFIG_DIR override or
the server's config dir. A config dir set outside Codeman is found with
the next hook; until then the panel says so.
- Store: dataPath('edit-history')/<id>.json, 0600 via tmp+rename, capped at
500 entries and 4 MiB of diff text per session, deleted with the session,
blocked in isSensitivePath, never fed to /api/search. Every route goes
through findSessionOrFail with req.
- Images only through existing file surfaces: file-raw inside the
workspace, the attachment route with notify:false outside it.
- textContent rendering, bounded byte-offset reads, marker-class header
button, hidden on phones (locked in the mobile policy test).
Docs: Working-With-Files, Settings-Reference, architecture invariants, API
reference, CLAUDE.md key pattern.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>