findCutPoint function

CutPointResult findCutPoint(
  1. List<SessionRecord> entries,
  2. int startIndex,
  3. int endIndex,
  4. int keepRecentTokens,
)

Find the compaction cut point that keeps approximately keepRecentTokens recent tokens.

Ported from pi's findCutPoint: walks entries backwards accumulating estimated tokens until the budget is exhausted, then snaps the cut forward to the next valid cut point (never a tool result) and backwards over non-message records so configuration entries stay with the kept region.

Implementation

CutPointResult findCutPoint(
  List<SessionRecord> entries,
  int startIndex,
  int endIndex,
  int keepRecentTokens,
) {
  final cutPoints = _findValidCutPoints(entries, startIndex, endIndex);

  if (cutPoints.isEmpty) {
    return CutPointResult(
      firstKeptEntryIndex: startIndex,
      turnStartIndex: -1,
      isSplitTurn: false,
    );
  }
  var accumulatedTokens = 0;
  var cutIndex = cutPoints.first;

  for (var i = endIndex - 1; i >= startIndex; i--) {
    final entry = entries[i];
    if (entry is! MessageRecord) continue;
    accumulatedTokens += estimateTokens(entry.message);
    if (accumulatedTokens >= keepRecentTokens) {
      for (final cutPoint in cutPoints) {
        if (cutPoint >= i) {
          cutIndex = cutPoint;
          break;
        }
      }
      break;
    }
  }
  while (cutIndex > startIndex) {
    final prevEntry = entries[cutIndex - 1];
    if (prevEntry is CompactionRecord || prevEntry is MessageRecord) break;
    cutIndex--;
  }
  final cutEntry = entries[cutIndex];
  final isUserMessage =
      cutEntry is MessageRecord && cutEntry.message.role == 'user';
  final turnStartIndex = isUserMessage
      ? -1
      : findTurnStartIndex(entries, cutIndex, startIndex);

  return CutPointResult(
    firstKeptEntryIndex: cutIndex,
    turnStartIndex: turnStartIndex,
    isSplitTurn: !isUserMessage && turnStartIndex != -1,
  );
}