rewriteHistoryImages function
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)];
}