diff options
| author | tanaka takashi <isazaka@gmail.com> | 2026-08-11 21:53:57 +0900 |
|---|---|---|
| committer | tanaka takashi <isazaka@gmail.com> | 2026-08-11 21:53:57 +0900 |
| commit | 7c9794f8d4d05074b96bb525d19db4ee1f6b3e95 (patch) | |
| tree | cb8961ee170d9f6874513e8b1a99b06855bb0707 /src/Agent.Core/Runtime/AgentRuntime.cs | |
| parent | 6c3458a5ec37a5b69957ad17c84de58984698f25 (diff) | |
Complete runtime diagnostics and verification coverage
Diffstat (limited to 'src/Agent.Core/Runtime/AgentRuntime.cs')
| -rw-r--r-- | src/Agent.Core/Runtime/AgentRuntime.cs | 50 |
1 files changed, 43 insertions, 7 deletions
diff --git a/src/Agent.Core/Runtime/AgentRuntime.cs b/src/Agent.Core/Runtime/AgentRuntime.cs index 8cf1ddb..bc9fc5e 100644 --- a/src/Agent.Core/Runtime/AgentRuntime.cs +++ b/src/Agent.Core/Runtime/AgentRuntime.cs @@ -75,8 +75,11 @@ public sealed class AgentRuntime(TaskRegistry registry, IOllamaClient ollama, Ag catch (Exception ex) when (ex is not (OutOfMemoryException or StackOverflowException or AccessViolationException)) { string? cleanupFailure = await EndTaskSafelyAsync(taskId, false, CancellationToken.None).ConfigureAwait(false); - string result = cleanupFailure is null ? ex.Message : $"{ex.Message} {cleanupFailure}"; registry.TryGet(taskId, null, out TaskSnapshot? failedSnapshot); + string baseResult = failedSnapshot?.LastErrorCode is not null and not "context_summary_failed" + ? failedSnapshot.LastError ?? ex.Message + : ex.Message; + string result = cleanupFailure is null ? baseResult : $"{baseResult} {cleanupFailure}"; string errorCode = cleanupFailure is not null ? "cleanup_failed" : failedSnapshot?.LastErrorCode ?? "execution_failed"; string errorMessage = cleanupFailure is not null ? result : failedSnapshot?.LastError ?? result; registry.UpdateDiagnostics(taskId, null, null, errorCode, errorMessage); @@ -122,11 +125,20 @@ public sealed class AgentRuntime(TaskRegistry registry, IOllamaClient ollama, Ag catch (PolicyDeniedException ex) { graph.SetState(next.Id, PlannerTaskState.Failed); - registry.UpdateDiagnostics(taskId, counters.Iterations, counters.Replans, "policy_denied", ex.Message); + string localizedDenial = LocalizePolicyDenial(ex); + registry.UpdateDiagnostics(taskId, counters.Iterations, counters.Replans, ex.ErrorCode, localizedDenial); if (!denyFingerprints.Add(ex.Fingerprint)) - throw new InvalidOperationException($"The same sandbox policy denial occurred again: {ex.Message}"); + { + string repeated = Message("runtime.policy_repeated", $"The same sandbox policy denial occurred again: {ex.Message}"); + registry.UpdateDiagnostics(taskId, counters.Iterations, counters.Replans, "policy.repeated", repeated); + throw new InvalidOperationException(repeated); + } if (counters.Replans >= maxReplans) - throw new InvalidOperationException($"Sandbox policy denial remained unresolved after {maxReplans} replans: {ex.Message}"); + { + string exhausted = Message("runtime.policy_replan_exhausted", $"Sandbox policy denial remained unresolved after {maxReplans} replans: {ex.Message}", maxReplans.ToString(System.Globalization.CultureInfo.InvariantCulture)); + registry.UpdateDiagnostics(taskId, counters.Iterations, counters.Replans, "policy.replan_exhausted", exhausted); + throw new InvalidOperationException(exhausted); + } string previousSignature = GraphSignature(graph); counters.Replans++; @@ -134,7 +146,11 @@ public sealed class AgentRuntime(TaskRegistry registry, IOllamaClient ollama, Ag graph = await _planner.ReplanAsync(taskId, task.SessionId, session, task.Request, task.Temperature, ex.Message, (phase, messages, result, error) => RecordLlmAttempt(task, counters, phase, messages, result, error), cancellationToken).ConfigureAwait(false); if (string.Equals(previousSignature, GraphSignature(graph), StringComparison.Ordinal)) - throw new InvalidOperationException("Replan did not change the incomplete TaskGraph."); + { + string unchanged = Message("runtime.replan_unchanged", "Replan did not change the incomplete TaskGraph."); + registry.UpdateDiagnostics(taskId, counters.Iterations, counters.Replans, "planning.replan_unchanged", unchanged); + throw new InvalidOperationException(unchanged); + } if (!registry.Transition(taskId, AgentTaskState.Running, "replan")) throw new InvalidOperationException("Task state could not return to running after replan."); UpdateTaskProgress(taskId, graph, null, counters); @@ -271,7 +287,9 @@ public sealed class AgentRuntime(TaskRegistry registry, IOllamaClient ollama, Ag _observer.ToolResult(callContext, call.Function.Name, toolResult); if (!toolResult.Success && !toolResult.RetryAllowed && !isMetadataTool) - throw new PolicyDeniedException(toolResult.FailureFingerprint ?? $"policy:{call.Function.Name}:{toolResult.Output}", toolResult.Output); + throw new PolicyDeniedException(toolResult.FailureFingerprint ?? $"policy:{call.Function.Name}:{toolResult.Output}", toolResult.Output, + toolResult.ErrorCode ?? "policy_denied", toolResult.ErrorCategory, toolResult.TargetPath, + toolResult.RequiredPermission, toolResult.Resolution); failureFingerprint = toolResult.FailureFingerprint ?? failureFingerprint; messages.Add(new OllamaMessage("tool", toolResult.Output, ToolName: call.Function.Name)); } @@ -454,9 +472,27 @@ public sealed class AgentRuntime(TaskRegistry registry, IOllamaClient ollama, Ag return detail is null ? locale.Get(key) : locale.Format(key, new Dictionary<string, object?> { ["message"] = detail }); } - private sealed class PolicyDeniedException(string fingerprint, string message) : Exception(message) + private sealed class PolicyDeniedException(string fingerprint, string message, string errorCode, string? errorCategory, + string? targetPath, string? requiredPermission, string? resolution) : Exception(message) { public string Fingerprint { get; } = fingerprint; + public string ErrorCode { get; } = errorCode; + public string? ErrorCategory { get; } = errorCategory; + public string? TargetPath { get; } = targetPath; + public string? RequiredPermission { get; } = requiredPermission; + public string? Resolution { get; } = resolution; + } + + private string LocalizePolicyDenial(PolicyDeniedException error) + { + if (locale is null) return error.Message; + return locale.Format("runtime.policy_denied", new Dictionary<string, object?> + { + ["code"] = error.ErrorCode, + ["path"] = error.TargetPath ?? "-", + ["permission"] = error.RequiredPermission ?? "-", + ["resolution"] = error.Resolution ?? "-" + }); } private IReadOnlyList<AgentSkill> ParseSelectedSkills(string content) |
