apply method

bool apply(
  1. ChatContentUpdate update, {
  2. Object? restoreId,
})

Implementation

bool apply(ChatContentUpdate update, {Object? restoreId}) {
  if (update.identity != identity) return false;
  if (restoreId != null && restoreId != _restoreId) return false;
  // The adapter must explicitly route replay; live data cannot mutate staging.
  if (_restoreId != null && restoreId == null) return false;
  final target = restoreId == null ? _messages : _staged!;
  if (update.kind == ChatContentKind.turnCompleted) {
    for (final message in target.whereType<ChatMessage>().where(
      (m) => m.turnId == update.turnId,
    )) {
      final chunk = _counters[message]?.finish();
      if (chunk != null) _append(message, chunk, target);
    }
    if (restoreId == null) revision++;
    return true;
  }
  final guard = ChatStructuredUpdateGuard(
    budget: inputBudget,
    resource: 'content update',
  );
  final metadata = guard.copyJsonValue(
    update.metadata,
    field: 'content metadata',
  );
  final copied = Map<String, Object?>.from(metadata as Map);
  final role = switch (update.kind) {
    ChatContentKind.user => ChatMessageRole.user,
    ChatContentKind.textDelta => ChatMessageRole.assistant,
    ChatContentKind.tool => ChatMessageRole.tool,
    _ => ChatMessageRole.status,
  };
  var index = -1;
  if (update.kind == ChatContentKind.tool) {
    final id = update.toolCallId;
    if (id == null || id.isEmpty) {
      throw ArgumentError('toolCallId is required');
    }
    copied['toolCallId'] = id;
    index = findChatToolMessage(target, id, turnId: update.turnId);
  } else if (update.kind == ChatContentKind.textDelta ||
      update.kind == ChatContentKind.thoughtDelta) {
    if (update.kind == ChatContentKind.thoughtDelta) {
      copied['kind'] = 'thought';
    }
    if (copied.keys.every((key) => key == 'kind') &&
        target.isNotEmpty &&
        target.last.turnId == update.turnId &&
        target.last.role == role &&
        target.last.metadata['kind'] == copied['kind']) {
      index = target.length - 1;
    }
  } else if (update.kind == ChatContentKind.plan) {
    copied['kind'] = 'plan';
  }
  final previous = index < 0 ? null : target[index];
  final isDelta =
      update.kind == ChatContentKind.textDelta ||
      update.kind == ChatContentKind.thoughtDelta;
  if (isDelta) {
    ChatMessage next;
    if (previous is ChatMessage) {
      next = previous;
    } else {
      next = ChatMessage(
        role: role,
        text: '',
        metadata: copied,
        inputBudget: inputBudget,
      );
      next.projectionClaimOwnership(_owner, update.turnId);
    }
    final counter = _counters.putIfAbsent(
      next,
      () => ChatUtf8LineBudgetCounter(
        maxBytes: update.kind == ChatContentKind.thoughtDelta
            ? inputBudget.maxThoughtTextBytes
            : inputBudget.maxMessageTextBytes,
        maxLines: inputBudget.maxMessageTextLines,
        resource: 'message text',
      ),
    );
    if (index < 0 && target.length >= inputBudget.maxCollectionItems) {
      _counters.remove(next);
      throw StateError('Content message budget exceeded');
    }
    final checkpoint = counter.checkpoint();
    try {
      _append(next, counter.append(update.text), target);
      counter.commit(checkpoint);
    } catch (_) {
      counter.rollback(checkpoint);
      if (index < 0) _counters.remove(next);
      rethrow;
    }
    if (index < 0) target.add(next);
    if (restoreId == null) revision++;
    return true;
  }
  final text = guard.copyString(
    update.text.trim().isEmpty ? previous?.text ?? '' : update.text,
    field: 'content text',
  );
  final next = ChatMessageData(
    role: role,
    text: text,
    turnId: update.turnId,
    timestamp: previous?.timestamp,
    revision: (previous?.revision ?? -1) + 1,
    metadata: mergeChatToolMetadata(previous?.metadata ?? const {}, copied),
  );
  _checkRetainedBudget(target, next, replaced: previous);
  if (index < 0) {
    if (target.length >= inputBudget.maxCollectionItems) {
      throw StateError('Content message budget exceeded');
    }
    target.add(next);
  } else {
    target[index] = next;
  }
  if (restoreId == null) revision++;
  return true;
}