session-steward 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -440,11 +440,13 @@ export function getCodexPaths(codexHomeInput, { refresh = false } = {}) {
440
440
  const archivedSessionsDirectory = path.join(codexHome, "archived_sessions");
441
441
  const sessionsDirectory = path.join(codexHome, "sessions");
442
442
  const resolution = resolveCodexDatabases(codexHome, { refresh });
443
- const { goals, logs, memories, state } = resolution.families;
443
+ const { goals, logs, memories, queue, state, threadHistory } = resolution.families;
444
444
  const stateDatabasePath = state.primary?.path ?? path.join(codexHome, "state_5.sqlite");
445
445
  const logsDatabasePath = logs.primary?.path ?? path.join(codexHome, "logs_2.sqlite");
446
446
  const memoryDatabasePath = memories.primary?.path ?? path.join(codexHome, "memories_1.sqlite");
447
447
  const goalsDatabasePath = goals.primary?.path ?? path.join(codexHome, "goals_1.sqlite");
448
+ const queueDatabasePath = queue.primary?.path ?? path.join(codexHome, "queue_1.sqlite");
449
+ const threadHistoryDatabasePath = threadHistory.primary?.path ?? path.join(codexHome, "thread_history_1.sqlite");
448
450
 
449
451
  return {
450
452
  archivedSessionsDirectory,
@@ -460,6 +462,8 @@ export function getCodexPaths(codexHomeInput, { refresh = false } = {}) {
460
462
  logsDatabasePaths: allValidDatabases(logs).map((database) => database.path),
461
463
  memoryDatabasePath,
462
464
  memoryDatabasePaths: allValidDatabases(memories).map((database) => database.path),
465
+ queueDatabasePath,
466
+ queueDatabasePaths: allValidDatabases(queue).map((database) => database.path),
463
467
  resolvedDatabases: databaseFamilySummary(resolution),
464
468
  sessionIndexPath: path.join(codexHome, "session_index.jsonl"),
465
469
  sessionsDirectory,
@@ -467,6 +471,8 @@ export function getCodexPaths(codexHomeInput, { refresh = false } = {}) {
467
471
  stateDatabases: allValidDatabases(state),
468
472
  stateDatabasePaths: allValidDatabases(state).map((database) => database.path),
469
473
  threadWriterLocksDirectory: path.join(codexHome, "thread-writer-locks"),
474
+ threadHistoryDatabasePath,
475
+ threadHistoryDatabasePaths: allValidDatabases(threadHistory).map((database) => database.path),
470
476
  transcriptDirectories: [sessionsDirectory, archivedSessionsDirectory],
471
477
  };
472
478
  }
@@ -569,12 +575,16 @@ export async function loadSessionStore({ codexHome }) {
569
575
  hasGoalsDatabase,
570
576
  hasLogsDatabase,
571
577
  hasMemoryDatabase,
578
+ hasQueueDatabase,
579
+ hasThreadHistoryDatabase,
572
580
  ] = await Promise.all([
573
581
  pathExists(paths.desktopStatePath),
574
582
  pathExists(paths.desktopStateBackupPath),
575
583
  pathExists(paths.goalsDatabasePath),
576
584
  pathExists(paths.logsDatabasePath),
577
585
  pathExists(paths.memoryDatabasePath),
586
+ pathExists(paths.queueDatabasePath),
587
+ pathExists(paths.threadHistoryDatabasePath),
578
588
  ]);
579
589
  const threadRowsById = new Map();
580
590
  const spawnEdgesByKey = new Map();
@@ -768,9 +778,12 @@ export async function loadSessionStore({ codexHome }) {
768
778
  hasGoalsDatabase,
769
779
  hasLogsDatabase,
770
780
  hasMemoryDatabase,
781
+ hasQueueDatabase,
782
+ hasThreadHistoryDatabase,
771
783
  goalsDatabasePath: paths.goalsDatabasePath,
772
784
  logsDatabasePath: paths.logsDatabasePath,
773
785
  memoryDatabasePath: paths.memoryDatabasePath,
786
+ queueDatabasePath: paths.queueDatabasePath,
774
787
  records,
775
788
  recordsById: new Map(records.map((record) => [record.id, record])),
776
789
  sessionIndexPath: paths.sessionIndexPath,
@@ -782,6 +795,9 @@ export async function loadSessionStore({ codexHome }) {
782
795
  resolvedDatabases: paths.resolvedDatabases,
783
796
  logsDatabasePaths: paths.logsDatabasePaths,
784
797
  memoryDatabasePaths: paths.memoryDatabasePaths,
798
+ queueDatabasePaths: paths.queueDatabasePaths,
799
+ threadHistoryDatabasePath: paths.threadHistoryDatabasePath,
800
+ threadHistoryDatabasePaths: paths.threadHistoryDatabasePaths,
785
801
  goalsDatabasePaths: paths.goalsDatabasePaths,
786
802
  transcriptHeaders,
787
803
  historyPath: paths.historyPath,
@@ -1217,12 +1233,16 @@ async function getStoreAvailability(paths) {
1217
1233
  hasGoalsDatabase,
1218
1234
  hasLogsDatabase,
1219
1235
  hasMemoryDatabase,
1236
+ hasQueueDatabase,
1237
+ hasThreadHistoryDatabase,
1220
1238
  ] = await Promise.all([
1221
1239
  pathExists(paths.desktopStatePath),
1222
1240
  pathExists(paths.desktopStateBackupPath),
1223
1241
  Promise.resolve(paths.goalsDatabasePaths.length > 0),
1224
1242
  Promise.resolve(paths.logsDatabasePaths.length > 0),
1225
1243
  Promise.resolve(paths.memoryDatabasePaths.length > 0),
1244
+ Promise.resolve(paths.queueDatabasePaths.length > 0),
1245
+ Promise.resolve(paths.threadHistoryDatabasePaths.length > 0),
1226
1246
  ]);
1227
1247
 
1228
1248
  return {
@@ -1231,6 +1251,8 @@ async function getStoreAvailability(paths) {
1231
1251
  hasGoalsDatabase,
1232
1252
  hasLogsDatabase,
1233
1253
  hasMemoryDatabase,
1254
+ hasQueueDatabase,
1255
+ hasThreadHistoryDatabase,
1234
1256
  };
1235
1257
  }
1236
1258
 
@@ -1485,6 +1507,7 @@ export async function loadDeletionStore({ codexHome, recordIds }) {
1485
1507
  historyPath: paths.historyPath,
1486
1508
  logsDatabasePath: paths.logsDatabasePath,
1487
1509
  memoryDatabasePath: paths.memoryDatabasePath,
1510
+ queueDatabasePath: paths.queueDatabasePath,
1488
1511
  records,
1489
1512
  recordsById: new Map(records.map((record) => [record.id, record])),
1490
1513
  sessionIndexPath: paths.sessionIndexPath,
@@ -1495,6 +1518,9 @@ export async function loadDeletionStore({ codexHome, recordIds }) {
1495
1518
  resolvedDatabases: paths.resolvedDatabases,
1496
1519
  logsDatabasePaths: paths.logsDatabasePaths,
1497
1520
  memoryDatabasePaths: paths.memoryDatabasePaths,
1521
+ queueDatabasePaths: paths.queueDatabasePaths,
1522
+ threadHistoryDatabasePath: paths.threadHistoryDatabasePath,
1523
+ threadHistoryDatabasePaths: paths.threadHistoryDatabasePaths,
1498
1524
  goalsDatabasePaths: paths.goalsDatabasePaths,
1499
1525
  transcriptHeaders: new Map(
1500
1526
  [...transcriptHeaders].filter(([id]) => relevantIds.has(id)),
@@ -1954,6 +1980,19 @@ export async function planSessionDeletion({ recordIds, store }) {
1954
1980
  .reduce((total, databasePath) => total + countRowsForIds(databasePath, "stage1_outputs", "thread_id", deletionIds), 0);
1955
1981
  const goalRowCount = (store.goalsDatabasePaths ?? [store.goalsDatabasePath].filter(Boolean))
1956
1982
  .reduce((total, databasePath) => total + countRowsForIds(databasePath, "thread_goals", "thread_id", deletionIds), 0);
1983
+ const queueRowCount = (store.queueDatabasePaths ?? [store.queueDatabasePath].filter(Boolean))
1984
+ .reduce((total, databasePath) => total + countRowsForIds(databasePath, "queued_items", "thread_id", deletionIds), 0);
1985
+ const queueRevisionRowCount = (store.queueDatabasePaths ?? [store.queueDatabasePath].filter(Boolean))
1986
+ .reduce((total, databasePath) => total + (inspectSqliteTable(databasePath, "queued_thread_revisions").exists
1987
+ ? countRowsForIds(databasePath, "queued_thread_revisions", "thread_id", deletionIds)
1988
+ : 0), 0);
1989
+ const threadHistoryRowCount = (store.threadHistoryDatabasePaths ?? [store.threadHistoryDatabasePath].filter(Boolean))
1990
+ .reduce((total, databasePath) => total + [
1991
+ "thread_items",
1992
+ "thread_turns",
1993
+ "thread_history_projection_state",
1994
+ ].reduce((databaseTotal, tableName) =>
1995
+ databaseTotal + countRowsForIds(databasePath, tableName, "thread_id", deletionIds), 0), 0);
1957
1996
  const stateTargets = (store.stateDatabases ?? [{ path: store.stateDatabasePath }]).map((database) => ({
1958
1997
  database,
1959
1998
  ids: findRowsForIds(database.path, "threads", "id", deletionIds).map((row) => String(row.id)),
@@ -1972,6 +2011,8 @@ export async function planSessionDeletion({ recordIds, store }) {
1972
2011
  ids: deletionIds,
1973
2012
  logRowCount,
1974
2013
  memoryRowCount,
2014
+ queueRowCount,
2015
+ queueRevisionRowCount,
1975
2016
  missingTranscriptPaths,
1976
2017
  newestLinkedActivityAtMs,
1977
2018
  records: selectedRecords,
@@ -1980,6 +2021,7 @@ export async function planSessionDeletion({ recordIds, store }) {
1980
2021
  stateTargets,
1981
2022
  transcriptBytes,
1982
2023
  transcriptFileCount,
2024
+ threadHistoryRowCount,
1983
2025
  transcriptPaths,
1984
2026
  };
1985
2027
  }
@@ -1991,6 +2033,8 @@ function getBackupSourcePaths({ plan, scope, store }) {
1991
2033
  const databasePaths = [
1992
2034
  ...(store.stateDatabasePaths ?? [store.stateDatabasePath]),
1993
2035
  ...(store.logsDatabasePaths ?? [store.logsDatabasePath].filter(Boolean)),
2036
+ ...(store.queueDatabasePaths ?? [store.queueDatabasePath].filter(Boolean)),
2037
+ ...(store.threadHistoryDatabasePaths ?? [store.threadHistoryDatabasePath].filter(Boolean)),
1994
2038
  ...(scope === "deep" ? (store.memoryDatabasePaths ?? [store.memoryDatabasePath].filter(Boolean)) : []),
1995
2039
  ...(scope === "deep" ? (store.goalsDatabasePaths ?? [store.goalsDatabasePath].filter(Boolean)) : []),
1996
2040
  ].filter(Boolean);
@@ -2033,6 +2077,9 @@ export async function fingerprintSessionDeletion({ plan, scope, store }) {
2033
2077
  plan.dynamicToolRowCount,
2034
2078
  plan.historyMatchCount,
2035
2079
  plan.logRowCount,
2080
+ plan.queueRowCount,
2081
+ plan.queueRevisionRowCount,
2082
+ plan.threadHistoryRowCount,
2036
2083
  plan.sessionIndexMatchCount,
2037
2084
  plan.spawnEdgeCount,
2038
2085
  ...(scope === "deep" ? [plan.goalRowCount, plan.memoryRowCount] : []),
@@ -2040,6 +2087,8 @@ export async function fingerprintSessionDeletion({ plan, scope, store }) {
2040
2087
  hash.update(`counts\0${counts.join("\0")}\0`);
2041
2088
  hash.update(`stores\0${[
2042
2089
  store.hasLogsDatabase,
2090
+ store.hasQueueDatabase,
2091
+ store.hasThreadHistoryDatabase,
2043
2092
  ...(scope === "deep" ? [
2044
2093
  store.hasDesktopState,
2045
2094
  store.hasDesktopStateBackup,
@@ -2079,6 +2128,17 @@ export async function fingerprintSessionDeletion({ plan, scope, store }) {
2079
2128
  for (const databasePath of store.logsDatabasePaths ?? [store.logsDatabasePath].filter(Boolean)) {
2080
2129
  fingerprintRowsForIds(hash, databasePath, "logs", "thread_id", plan.ids);
2081
2130
  }
2131
+ for (const databasePath of store.queueDatabasePaths ?? [store.queueDatabasePath].filter(Boolean)) {
2132
+ fingerprintRowsForIds(hash, databasePath, "queued_items", "thread_id", plan.ids);
2133
+ if (inspectSqliteTable(databasePath, "queued_thread_revisions").exists) {
2134
+ fingerprintRowsForIds(hash, databasePath, "queued_thread_revisions", "thread_id", plan.ids);
2135
+ }
2136
+ }
2137
+ for (const databasePath of store.threadHistoryDatabasePaths ?? [store.threadHistoryDatabasePath].filter(Boolean)) {
2138
+ fingerprintRowsForIds(hash, databasePath, "thread_items", "thread_id", plan.ids);
2139
+ fingerprintRowsForIds(hash, databasePath, "thread_turns", "thread_id", plan.ids);
2140
+ fingerprintRowsForIds(hash, databasePath, "thread_history_projection_state", "thread_id", plan.ids);
2141
+ }
2082
2142
  if (scope === "deep") {
2083
2143
  for (const databasePath of store.memoryDatabasePaths ?? [store.memoryDatabasePath].filter(Boolean)) {
2084
2144
  fingerprintRowsForIds(hash, databasePath, "stage1_outputs", "thread_id", plan.ids);
@@ -2292,6 +2352,8 @@ async function createOperationBackup({ onProgress, plan, scope, store }) {
2292
2352
  const snapshotCandidates = [
2293
2353
  ...(store.stateDatabasePaths ?? [store.stateDatabasePath]).map((databasePath) => [databasePath, path.basename(databasePath), true]),
2294
2354
  ...(store.logsDatabasePaths ?? [store.logsDatabasePath].filter(Boolean)).map((databasePath) => [databasePath, path.basename(databasePath), true]),
2355
+ ...(store.queueDatabasePaths ?? [store.queueDatabasePath].filter(Boolean)).map((databasePath) => [databasePath, path.basename(databasePath), true]),
2356
+ ...(store.threadHistoryDatabasePaths ?? [store.threadHistoryDatabasePath].filter(Boolean)).map((databasePath) => [databasePath, path.basename(databasePath), true]),
2295
2357
  ...(scope === "deep" ? (store.memoryDatabasePaths ?? [store.memoryDatabasePath].filter(Boolean)) : []).map((databasePath) => [databasePath, path.basename(databasePath), true]),
2296
2358
  ...(scope === "deep" ? (store.goalsDatabasePaths ?? [store.goalsDatabasePath].filter(Boolean)) : []).map((databasePath) => [databasePath, path.basename(databasePath), true]),
2297
2359
  ];
@@ -2467,6 +2529,25 @@ export async function executeSessionDeletion({
2467
2529
  executeTransaction(databasePath, deleteStatements("logs", "thread_id", plan.ids));
2468
2530
  }
2469
2531
  }
2532
+ if (store.hasQueueDatabase) {
2533
+ for (const databasePath of store.queueDatabasePaths ?? [store.queueDatabasePath]) {
2534
+ executeTransaction(databasePath, [
2535
+ ...deleteStatements("queued_items", "thread_id", plan.ids),
2536
+ ...(inspectSqliteTable(databasePath, "queued_thread_revisions").exists
2537
+ ? deleteStatements("queued_thread_revisions", "thread_id", plan.ids)
2538
+ : []),
2539
+ ]);
2540
+ }
2541
+ }
2542
+ if (store.hasThreadHistoryDatabase) {
2543
+ for (const databasePath of store.threadHistoryDatabasePaths ?? [store.threadHistoryDatabasePath]) {
2544
+ executeTransaction(databasePath, [
2545
+ ...deleteStatements("thread_items", "thread_id", plan.ids),
2546
+ ...deleteStatements("thread_turns", "thread_id", plan.ids),
2547
+ ...deleteStatements("thread_history_projection_state", "thread_id", plan.ids),
2548
+ ]);
2549
+ }
2550
+ }
2470
2551
  for (const { database, ids } of plan.stateTargets ?? (store.stateDatabases ?? [{ path: store.stateDatabasePath }]).map((database) => ({ database, ids: plan.ids }))) {
2471
2552
  const stateStatements = [];
2472
2553
  const schema = stateSchema(database);
@@ -2828,6 +2909,21 @@ export async function verifySessionDeletion({ plan, scope = "deep", store }) {
2828
2909
  ? (store.logsDatabasePaths ?? [store.logsDatabasePath]).flatMap((databasePath) =>
2829
2910
  findRowsForIds(databasePath, "logs", "thread_id", plan.ids, { limitOne: true }))
2830
2911
  : [];
2912
+ const remainingQueueRecords = store.hasQueueDatabase
2913
+ ? (store.queueDatabasePaths ?? [store.queueDatabasePath]).flatMap((databasePath) => [
2914
+ ...findRowsForIds(databasePath, "queued_items", "thread_id", plan.ids, { limitOne: true }),
2915
+ ...(inspectSqliteTable(databasePath, "queued_thread_revisions").exists
2916
+ ? findRowsForIds(databasePath, "queued_thread_revisions", "thread_id", plan.ids, { limitOne: true })
2917
+ : []),
2918
+ ])
2919
+ : [];
2920
+ const remainingThreadHistoryRecords = store.hasThreadHistoryDatabase
2921
+ ? (store.threadHistoryDatabasePaths ?? [store.threadHistoryDatabasePath]).flatMap((databasePath) => [
2922
+ ...findRowsForIds(databasePath, "thread_items", "thread_id", plan.ids, { limitOne: true }),
2923
+ ...findRowsForIds(databasePath, "thread_turns", "thread_id", plan.ids, { limitOne: true }),
2924
+ ...findRowsForIds(databasePath, "thread_history_projection_state", "thread_id", plan.ids, { limitOne: true }),
2925
+ ])
2926
+ : [];
2831
2927
  const [sessionIndexMatches, historyMatches] = await Promise.all([
2832
2928
  inspectJsonlMatches(
2833
2929
  store.sessionIndexPath,
@@ -2872,6 +2968,8 @@ export async function verifySessionDeletion({ plan, scope = "deep", store }) {
2872
2968
  remainingMemoryRecords.length === 0 &&
2873
2969
  remainingGoalRecords.length === 0 &&
2874
2970
  remainingLogRecords.length === 0 &&
2971
+ remainingQueueRecords.length === 0 &&
2972
+ remainingThreadHistoryRecords.length === 0 &&
2875
2973
  sessionIndexMatches.count === 0 &&
2876
2974
  historyMatches.count === 0 &&
2877
2975
  remainingTranscriptPaths.length === 0 &&
@@ -2883,6 +2981,8 @@ export async function verifySessionDeletion({ plan, scope = "deep", store }) {
2883
2981
  remainingHistoryEntryCount: historyMatches.count,
2884
2982
  remainingLogRecords,
2885
2983
  remainingMemoryRecords,
2984
+ remainingQueueRecords,
2985
+ remainingThreadHistoryRecords,
2886
2986
  remainingSessionIndexEntries,
2887
2987
  remainingSessionIndexEntryCount: sessionIndexMatches.count,
2888
2988
  remainingThreads,
@@ -206,6 +206,72 @@ export function createSessionEventCoverage({
206
206
  };
207
207
  }
208
208
 
209
+ export function createSessionEventSummary({ asks = 0, commands = 0, edits = 0 } = {}) {
210
+ return {
211
+ asks: requiredCount(asks, "summary.asks"),
212
+ commands: requiredCount(commands, "summary.commands"),
213
+ edits: requiredCount(edits, "summary.edits"),
214
+ };
215
+ }
216
+
217
+ // Ordered largest-concept-first so the bar and its legend agree
218
+ export const SESSION_EVENT_COMPOSITION_SEGMENTS = Object.freeze([
219
+ "toolOutput",
220
+ "largeRecords",
221
+ "compaction",
222
+ "attachments",
223
+ "messages",
224
+ "edits",
225
+ "reasoning",
226
+ "other",
227
+ ]);
228
+
229
+ export function createSessionEventComposition({
230
+ attachments = 0,
231
+ compaction = 0,
232
+ edits = 0,
233
+ largeRecords = 0,
234
+ messages = 0,
235
+ other = 0,
236
+ reasoning = 0,
237
+ toolOutput = 0,
238
+ total = 0,
239
+ } = {}) {
240
+ const composition = {
241
+ attachments: requiredCount(attachments, "composition.attachments"),
242
+ compaction: requiredCount(compaction, "composition.compaction"),
243
+ edits: requiredCount(edits, "composition.edits"),
244
+ largeRecords: requiredCount(largeRecords, "composition.largeRecords"),
245
+ messages: requiredCount(messages, "composition.messages"),
246
+ other: requiredCount(other, "composition.other"),
247
+ reasoning: requiredCount(reasoning, "composition.reasoning"),
248
+ toolOutput: requiredCount(toolOutput, "composition.toolOutput"),
249
+ total: requiredCount(total, "composition.total"),
250
+ };
251
+
252
+ const segments = SESSION_EVENT_COMPOSITION_SEGMENTS
253
+ .reduce((sum, segment) => sum + composition[segment], 0);
254
+
255
+ if (segments !== composition.total) {
256
+ throw new TypeError("Session event composition segments must add up to the transcript size.");
257
+ }
258
+
259
+ return composition;
260
+ }
261
+
262
+ export function finalizeSessionEventComposition(composition, transcriptBytes) {
263
+ const attributed = SESSION_EVENT_COMPOSITION_SEGMENTS
264
+ .filter((segment) => segment !== "other")
265
+ .reduce((sum, segment) => sum + (composition[segment] ?? 0), 0);
266
+ const total = Math.max(transcriptBytes ?? 0, attributed);
267
+
268
+ return createSessionEventComposition({
269
+ ...composition,
270
+ other: total - attributed,
271
+ total,
272
+ });
273
+ }
274
+
209
275
  export function sessionEventCoveragePercent(coverage) {
210
276
  const normalizedCoverage = createSessionEventCoverage(coverage);
211
277
  const considered = normalizedCoverage.total - normalizedCoverage.skipped;
@@ -246,13 +312,17 @@ export function createSessionEventHeader({
246
312
  }
247
313
 
248
314
  export function createSessionEventsResult({
315
+ composition,
249
316
  coverage,
250
317
  events = [],
251
318
  header,
252
319
  reason = null,
320
+ summary,
253
321
  window = {},
254
322
  } = {}) {
255
323
  const normalizedCoverage = createSessionEventCoverage(coverage);
324
+ const normalizedSummary = createSessionEventSummary(summary);
325
+ const normalizedComposition = createSessionEventComposition(composition);
256
326
 
257
327
  if (
258
328
  normalizedCoverage.recognized
@@ -298,6 +368,8 @@ export function createSessionEventsResult({
298
368
  events: [...events],
299
369
  header: createSessionEventHeader(header),
300
370
  reason,
371
+ composition: normalizedComposition,
372
+ summary: normalizedSummary,
301
373
  window: {
302
374
  complete,
303
375
  end,
@@ -41,7 +41,12 @@ export async function* readJsonlEntries(filePath) {
41
41
  }
42
42
 
43
43
  function appendLineBytes(state, bytes, maxLineBytes) {
44
- if (state.oversized || bytes.length === 0) return;
44
+ if (bytes.length === 0) return;
45
+
46
+ // Counted even once a line is oversized, because the largest records in a
47
+ // transcript are exactly the ones that stop being buffered.
48
+ state.totalBytes += bytes.length;
49
+ if (state.oversized) return;
45
50
 
46
51
  if (state.length + bytes.length > maxLineBytes) {
47
52
  state.length = 0;
@@ -69,17 +74,20 @@ function createLineState(maxLineBytes) {
69
74
  buffer: Buffer.allocUnsafe(Math.min(JSONL_STARTING_LINE_BYTES, maxLineBytes)),
70
75
  length: 0,
71
76
  oversized: false,
77
+ totalBytes: 0,
72
78
  };
73
79
  }
74
80
 
75
81
  function resetLineState(state) {
76
82
  state.length = 0;
77
83
  state.oversized = false;
84
+ state.totalBytes = 0;
78
85
  }
79
86
 
80
87
  function snapshotEntry(state, index) {
81
88
  if (state.oversized) {
82
89
  return {
90
+ bytes: state.totalBytes,
83
91
  index,
84
92
  oversized: true,
85
93
  parsed: null,
@@ -93,6 +101,7 @@ function snapshotEntry(state, index) {
93
101
 
94
102
  const entry = parseLine(bytes.toString("utf8"), index);
95
103
  return {
104
+ bytes: state.totalBytes,
96
105
  index: entry.index,
97
106
  oversized: false,
98
107
  parsed: entry.parsed,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "session-steward",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Codex and Claude Code session manager - browse, back up, and delete old sessions. Local browser UI + CLI.",
5
5
  "license": "MIT",
6
6
  "author": "Mallik Cheripally",