Files
ClawdDotNet/tests/ClawdDotNet.Core.Tests/Infrastructure/DelegatingTool.cs
T
RichardandClaude Opus 5.5 837a634a22 Stufe 4c (#764): Kommentare in C#, WebUI-Skripten, CSS und HTML auf Englisch
Rund 2.600 Kommentarblöcke in 310 C#-Dateien übersetzt, Begründungen vollständig
erhalten: blockweise gleiche Anzahl wie vorher, kein Block unter 60 % seiner
Länge, der Code außerhalb der Kommentare per Tokenizer byte-identisch geprüft.
Zitate deutscher Oberflächentexte und Testdaten bleiben deutsch.

WebUI: Kommentare in JS/CSS/HTML übersetzt, JSDoc-@param an die in 4b
umbenannten Parameter angeglichen.

Dabei behoben:
- 4b hatte "tag" (HTML-Tag) als deutsches "Tag" gelesen und zu "dayValue"
  gemacht (makeElement, svg, Tagesübersicht) - zurück auf tag/day.
- Aufgaben "InProgress" bekamen die Plakettenart "laufend", für die es nie eine
  CSS-Klasse gab - jetzt "success" wie laufende Instanzen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 12:45:03 +02:00

84 lines
3.4 KiB
C#

using System.Collections.Concurrent;
using System.Text.Json;
using ClawdDotNet.Core.Tools;
namespace ClawdDotNet.Core.Tests.Infrastructure;
/// <summary>
/// A tool that commissions another agent — the replica of what
/// <c>AgentComm.send_message</c> and <c>AgentSpawn.spawn_agent</c> do.
///
/// <para>The replica is necessary because both tools lie in projects of their own that
/// <c>ClawdDotNet.Core.Tests</c> does not reference. All that matters anyway is the
/// one line in which they are the same here: the delegation chain of the own run
/// is passed on. A tool that suppresses it turns a cycle into a
/// new commission — exactly the case B8 describes.</para>
///
/// <para>Whom an agent commissions is in <paramref name="routes"/>: agentId → target.
/// Whoever does not appear there does not delegate and simply answers.</para>
/// </summary>
internal sealed class DelegatingTool : IAgentTool
{
private readonly IReadOnlyDictionary<string, string> _routes;
private readonly bool _spawn;
private readonly TimeSpan _delayBeforeHandoff;
public string Name => "TestTool";
public string Description => "Beauftragt einen anderen Agenten.";
public JsonElement InputSchema { get; } = JsonDocument.Parse("""
{ "type": "object", "properties": { "message": { "type": "string" } } }
""").RootElement.Clone();
/// <summary>What came back — caller, target and the result of the hand-off.</summary>
public ConcurrentBag<(string From, string To, bool Delivered, string? Error)> Handoffs { get; } = new();
/// <param name="delayBeforeHandoff">
/// Waiting time before handing off. The fake client answers synchronously; without
/// this pause a chat runs through completely before the next one even
/// begins, and two chains cannot overlap.
/// </param>
public DelegatingTool(
IReadOnlyDictionary<string, string> routes,
bool spawn = false,
TimeSpan delayBeforeHandoff = default)
{
_routes = routes;
_spawn = spawn;
_delayBeforeHandoff = delayBeforeHandoff;
}
public async Task<ToolResult> ExecuteAsync(
JsonElement input, AgentToolContext context, CancellationToken ct)
{
if (!_routes.TryGetValue(context.AgentId, out var target))
return ToolResult.Ok("nichts zu delegieren");
if (_delayBeforeHandoff > TimeSpan.Zero)
await Task.Delay(_delayBeforeHandoff, ct);
if (_spawn)
{
var spawned = await context.MessageRouter!.SpawnAgentAsync(
context.AgentId, target, "Auftrag", context.InstanceId, context.Delegation, ct);
Handoffs.Add((context.AgentId, target, spawned.Started, spawned.Error));
return spawned.Started
? ToolResult.Ok(spawned.FinalMessage ?? "")
: ToolResult.Ok(JsonSerializer.Serialize(new { spawned = false, error = spawned.Error }));
}
var result = await context.MessageRouter!.SendMessageAsync(
context.AgentId, target, "Frage", context.InstanceId, context.Delegation, ct);
Handoffs.Add((context.AgentId, target, result.Delivered, result.Error));
// Like the real tool: a rejection is not a throw, but a result that
// the agent can read and react to differently.
return result.Delivered
? ToolResult.Ok(result.Response ?? "")
: ToolResult.Ok(JsonSerializer.Serialize(new { delivered = false, error = result.Error }));
}
}