summaryrefslogtreecommitdiff
path: root/src/Agent.Core/Runtime/AgentRuntime.cs
diff options
context:
space:
mode:
authortanaka takashi <isazaka@gmail.com>2026-08-11 13:51:59 +0900
committertanaka takashi <isazaka@gmail.com>2026-08-11 13:51:59 +0900
commitb5ccecca01007f2a2459265df6d749fbfd31aa84 (patch)
treeffa633db5df1809b1c0adcfb35e66aa5b97f8c46 /src/Agent.Core/Runtime/AgentRuntime.cs
parent7aa4ff5d8403e4c1480faafce0af8d3549276c9f (diff)
Fix: AgentRuntime tool failure handling and PlannerService fallback graph persistence
AgentRuntime.ExecutePlannerTaskAsync: - Add PlannerValidationException and FileNotFoundException to the tool execution catch clause so that 'Completed tasks are immutable' and 'Approved path does not exist' errors are returned as tool results instead of propagating as unhandled task failures. - Treat task_update as a metadata tool: its failures no longer add to unresolvedToolFailures, so the LLM is not forced into an infinite retry loop when task_update receives invalid arguments. - Skip throwing InvalidOperationException for RetryAllowed=false failures originating from metadata tools (task_update), preventing unnecessary replan cycles caused by graph bookkeeping errors. - Improve the 'No tasks were executed' fallback message: when replan was attempted but no tasks could execute, report that the sandbox policy denied required operations. PlannerService.PlanAsync: - Persist fallback TaskGraph to session.TaskGraphs[taskId] in all three fallback paths (ChatStructuredAsync exception, empty/null plan, BuildTaskGraph exception) so that BeginTask's TryAdd does not create an empty graph that shadows the fallback.
Diffstat (limited to 'src/Agent.Core/Runtime/AgentRuntime.cs')
-rw-r--r--src/Agent.Core/Runtime/AgentRuntime.cs13
1 files changed, 9 insertions, 4 deletions
diff --git a/src/Agent.Core/Runtime/AgentRuntime.cs b/src/Agent.Core/Runtime/AgentRuntime.cs
index aa81486..0b261da 100644
--- a/src/Agent.Core/Runtime/AgentRuntime.cs
+++ b/src/Agent.Core/Runtime/AgentRuntime.cs
@@ -110,9 +110,12 @@ public sealed class AgentRuntime(TaskRegistry registry, IOllamaClient ollama, Ag
}
// All tasks completed — assemble final result.
+ // If replan was attempted but no tasks could execute, provide a meaningful message.
string finalContent = finalContents.Count > 0
? string.Join("\n\n", finalContents)
- : "No tasks were executed.";
+ : replanCount > 0
+ ? "The request could not be completed because the sandbox policy denied required operations."
+ : "No tasks were executed.";
string? cleanupFailure = await EndTaskSafelyAsync(taskId, true, CancellationToken.None).ConfigureAwait(false);
if (cleanupFailure is not null) throw new InvalidOperationException(cleanupFailure);
session.Append(new ConversationMessage("user", task.Request));
@@ -210,20 +213,22 @@ public sealed class AgentRuntime(TaskRegistry registry, IOllamaClient ollama, Ag
{
toolResult = new(false, ex.Message, false, $"{call.Function.Name}:{ex.GetType().Name}:{ex.Message}", RetryAllowed: false);
}
- catch (Exception ex) when (ex is ToolValidationException or ArgumentException)
+ catch (Exception ex) when (ex is ToolValidationException or ArgumentException or PlannerValidationException or FileNotFoundException)
{
toolResult = new(false, ex.Message, false, $"{call.Function.Name}:{ex.GetType().Name}:{ex.Message}");
}
externalEffectEvidence |= toolResult.Success && toolResult.ExternalEffect;
+ // task_update is a metadata tool for graph bookkeeping; its failures should not block completion.
+ bool isMetadataTool = call.Function.Name == "task_update";
if (toolResult.Success) unresolvedToolFailures.Remove(call.Function.Name);
- else unresolvedToolFailures[call.Function.Name] = toolResult.FailureFingerprint ?? toolResult.Output;
+ else if (!isMetadataTool) unresolvedToolFailures[call.Function.Name] = toolResult.FailureFingerprint ?? toolResult.Output;
// Record ExecutionResult in session for context summary.
session.AppendExecutionResult(new ExecutionResult(taskId, call.Function.Name, toolResult.Success, toolResult.Output));
_observer.ToolResult(taskId, call.Function.Name, toolResult);
- if (!toolResult.Success && !toolResult.RetryAllowed)
+ if (!toolResult.Success && !toolResult.RetryAllowed && !isMetadataTool)
throw new InvalidOperationException($"Tool '{call.Function.Name}' was denied by sandbox policy and will not be retried: {toolResult.Output}");
failureFingerprint = toolResult.FailureFingerprint ?? failureFingerprint;
messages.Add(new OllamaMessage("tool", toolResult.Output, ToolName: call.Function.Name));