feat(agent): harden loop recovery and permission hook lifecycle
Some checks failed
Release Gate / gate (push) Has been cancelled

This commit is contained in:
2026-04-03 19:24:08 +09:00
parent 0c3921feb5
commit 5de5c74040
8 changed files with 523 additions and 115 deletions

View File

@@ -580,12 +580,10 @@ public sealed class ChatSessionStateService
conversation.AgentRunHistory ??= new List<ChatAgentRunRecord>();
conversation.DraftQueueItems ??= new List<DraftQueueItem>();
var orderedEvents = conversation.ExecutionEvents
.OrderBy(evt => evt.Timestamp == default ? DateTime.MinValue : evt.Timestamp)
.ToList();
if (!conversation.ExecutionEvents.SequenceEqual(orderedEvents))
var normalizedEvents = NormalizeExecutionEventsForResume(conversation.ExecutionEvents);
if (!conversation.ExecutionEvents.SequenceEqual(normalizedEvents))
{
conversation.ExecutionEvents = orderedEvents;
conversation.ExecutionEvents = normalizedEvents;
changed = true;
}
@@ -640,6 +638,55 @@ public sealed class ChatSessionStateService
return delta <= TimeSpan.FromSeconds(2);
}
private static List<ChatExecutionEvent> NormalizeExecutionEventsForResume(IEnumerable<ChatExecutionEvent>? source)
{
if (source == null)
return new List<ChatExecutionEvent>();
var ordered = source
.Where(evt => evt != null)
.OrderBy(evt => evt.Timestamp == default ? DateTime.MinValue : evt.Timestamp)
.ThenBy(evt => evt.Iteration)
.ThenBy(evt => evt.ElapsedMs)
.ToList();
var result = new List<ChatExecutionEvent>(ordered.Count);
foreach (var evt in ordered)
{
if (result.Count == 0)
{
result.Add(evt);
continue;
}
var last = result[^1];
if (!IsNearDuplicateExecutionEvent(last, evt))
{
result.Add(evt);
continue;
}
last.Timestamp = evt.Timestamp;
last.ElapsedMs = Math.Max(last.ElapsedMs, evt.ElapsedMs);
last.InputTokens = Math.Max(last.InputTokens, evt.InputTokens);
last.OutputTokens = Math.Max(last.OutputTokens, evt.OutputTokens);
last.StepCurrent = Math.Max(last.StepCurrent, evt.StepCurrent);
last.StepTotal = Math.Max(last.StepTotal, evt.StepTotal);
last.Iteration = Math.Max(last.Iteration, evt.Iteration);
if (string.IsNullOrWhiteSpace(last.FilePath))
last.FilePath = evt.FilePath;
if (!string.IsNullOrWhiteSpace(evt.Summary) && evt.Summary.Length >= (last.Summary?.Length ?? 0))
last.Summary = evt.Summary;
if (evt.Steps is { Count: > 0 })
last.Steps = evt.Steps.ToList();
if (!string.IsNullOrWhiteSpace(evt.ToolInput))
last.ToolInput = evt.ToolInput;
last.Success = last.Success && evt.Success;
}
return result;
}
private void TouchConversation(ChatStorageService? storage, string tab)
{
var conv = EnsureCurrentConversation(tab);