Say how big a session is before it is imported
The import list reported a line count, which is the wrong axis: these transcripts embed screenshots as base64, so one line can be a megabyte. On this machine a 69 MB session has 3,427 lines while a 44 MB one has 6,792 — the number on the row said nothing about what continuing the session would cost, and size is the only thing there that predicts it. The session behind the 2026-08-29 incident was 65 MB across 13,000 lines, a line count that looks unremarkable. Shown beside the line count rather than instead of it, since a short file of long lines is exactly the expensive case. Not warned about and not marked: importing a large session is a choice somebody is entitled to make, and flagging it would be the interface nagging about a decision already taken. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VETa8afmpWaYezLCqJhDB8
This commit is contained in:
1 parent
362d436d4f
commit
d96bc041a7
4 files changed
+47
-2
No files matched your search
@@ -67,6 +67,19 @@ pub struct Importable {
|
||||
/// Epoch seconds, for ordering by "what I was last doing".
|
||||
pub modified: f64,
|
||||
pub lines: usize,
|
||||
/// Size of the file, in bytes.
|
||||
///
|
||||
/// Reported because it is the only thing on a row that predicts what
|
||||
/// continuing the session will cost, and lines do not: these
|
||||
/// transcripts embed screenshots as base64, so one line can be a
|
||||
/// megabyte. The session behind the 2026-08-29 incident was 65 MB
|
||||
/// across 13,000 lines, which is a line count that looks unremarkable.
|
||||
///
|
||||
/// Shown rather than warned about. Importing a large session is a
|
||||
/// choice somebody is entitled to make, and marking it would be the
|
||||
/// interface nagging about a decision already taken -- but they should
|
||||
/// be able to see what they are taking on.
|
||||
pub bytes: u64,
|
||||
/// Whether [`title`](Self::title) is a name somebody chose rather than
|
||||
/// something read out of the conversation. Sorted on, and worth the
|
||||
/// reader knowing: a name is a claim about what a session *is*, and a
|
||||
@@ -151,7 +164,8 @@ if [ -d "$HOME/.claude/sessions" ]; then
|
||||
fi
|
||||
for f in "$HOME"/.claude/projects/*/*.jsonl; do
|
||||
[ -f "$f" ] || continue
|
||||
printf '%s\t%s\t%s\t' "$(stat -c %Y "$f" 2>/dev/null || echo 0)" "$(wc -l < "$f")" "$f"
|
||||
printf '%s\t%s\t%s\t%s\t' "$(stat -c %Y "$f" 2>/dev/null || echo 0)" \
|
||||
"$(wc -l < "$f")" "$(stat -c %s "$f" 2>/dev/null || echo 0)" "$f"
|
||||
grep '"type":"custom-title"' "$f" 2>/dev/null | tail -1 | tr '\n' '\037'
|
||||
grep '"type":"user"' "$f" 2>/dev/null | grep -v '"tool_use_id"' | tail -12 | tr '\n' '\037'
|
||||
printf '\n'
|
||||
@@ -190,9 +204,10 @@ done
|
||||
|
||||
/// One line of [`list`]'s output, or nothing if it is not one.
|
||||
fn parse_row(line: &str) -> Option<Importable> {
|
||||
let mut fields = line.splitn(4, '\t');
|
||||
let mut fields = line.splitn(5, '\t');
|
||||
let modified: f64 = fields.next()?.trim().parse().ok()?;
|
||||
let lines: usize = fields.next()?.trim().parse().ok()?;
|
||||
let bytes: u64 = fields.next()?.trim().parse().ok()?;
|
||||
let path = fields.next()?.to_string();
|
||||
let id = path.rsplit('/').next()?.strip_suffix(".jsonl")?.to_string();
|
||||
|
||||
@@ -238,6 +253,7 @@ fn parse_row(line: &str) -> Option<Importable> {
|
||||
.unwrap_or_else(|| "(no messages)".to_string()),
|
||||
modified,
|
||||
lines,
|
||||
bytes,
|
||||
path,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user