rewriteHistoryImages function

List<Message> rewriteHistoryImages(
  1. List<Message> messages, {
  2. int? maxPerRequest,
  3. void onDrop(
    1. int index,
    2. String keyPreview
    )?,
})

Rewrites the OUTGOING request payload (never the transcript): image blocks become [Image N] refs, unique originals ride once as carriers, the current user message rides in place, and the per-request cap drops by priority (current > newest > older) reporting every drop.

Returns the SAME list instance when there is nothing to rewrite (no images and no [Image N] mentions), so image-free sessions pay nothing.

Implementation

List<Message> rewriteHistoryImages(
  List<Message> messages, {
  int? maxPerRequest,
  void Function(int index, String keyPreview)? onDrop,
}) {
  var hasImages = false;
  for (final message in messages) {
    if (_imagesOf(message).isNotEmpty) {
      hasImages = true;
      break;
    }
  }
  if (!hasImages && !messages.any(_mentionsImageRef)) return messages;

  // F2 pre-pass: when the window carries a renumbering boundary, history
  // citations may name a different image than their number does now —
  // degrade AUTHORED text mentions before assembly. Block rewrites,
  // carrier labels and the current message are generated content-keyed
  // (or provably fresh) and must survive.
  final currentIdx0 = _lastUserMessageIndex(messages);
  final degraded = messages.any(_isRenumberingBoundary)
      ? [
          for (var i = 0; i < messages.length; i++)
            i == currentIdx0
                ? messages[i]
                : _resolveMentions(messages[i], const <int>{}, trusted: false),
        ]
      : messages;
  final cap = maxPerRequest ?? imageRegistryConfig.maxPerRequest;
  final entryByKey = <String, ImageRegistryEntry>{};
  final occurrences = <String, List<int>>{};
  _scanImages(degraded, entryByKey, occurrences);
  final currentIdx = _lastUserMessageIndex(degraded);
  final riding = _selectRiding(
    degraded,
    entryByKey,
    occurrences,
    currentIdx,
    cap,
    onDrop,
  );
  final carriers = _planCarriers(degraded, entryByKey, occurrences, riding);

  // Emit: rewritten history (image blocks → refs/notes), the current
  // message in place with fresh `[Image N]` labels (F3), carriers at
  // their anchors.
  final out = _emitRewritten(degraded, currentIdx, riding, carriers);

  // Mention pass (I4): every remaining `[Image N]` in text that does not
  // resolve to a riding index becomes the unavailable note. Authored
  // history citations were already degraded by the pre-pass; what
  // survives here is generated content-keyed text.
  final ridingIndexes = riding.indexByKey.values.toSet();
  return [for (final message in out) _resolveMentions(message, ridingIndexes)];
}