Befunde aus dem Testbetrieb auf der Linux-VM behoben (#572)

- #578: Abgebrochene Laeufe (Schrittgrenze, Zeitueberschreitung, Abbruch,
  Ausnahme) tragen ihre bezahlten Token - vorher 0 in RunUsage, fuer das
  Budget unsichtbar.
- #580: WebFetch prueft die aufgeloeste Zieladresse beim Verbinden; ein
  erlaubter Name, der auf 127.0.0.1 aufloest, wird gesperrt.
- #575: Dateien entstehen unter Unix mit 0600 (AtomicFile, Protokolle,
  FileRW-Anhaengen, state.db), Protokollverzeichnisse mit 0700.
- #576: SIGTERM/SIGINT beenden auch mit Fenster geordnet.
- #593: OpenRouter-Statusdienst wird gestartet.
- #589: Auftragsnachricht nennt aktuelle Zeit, Zeitzone und Termin.
- #581: Unvollstaendiges Argument-JSON erreicht das Werkzeug nicht mehr;
  das Modell bekommt eine verstaendliche Rueckmeldung statt Parsertext.
- #591: Weboberflaeche liest die Felder, die API und Ereignisstrom liefern
  (Freigaben, Protokoll, Taskboard mit Termin, Werkzeug-Einstellungen,
  Tokennutzung); Ereignisstrom in camelCase.
- #592: Chat zeigt Nachrichten nicht mehr doppelt.
- #536 (teilweise): A2A-Antworten tragen Absender und Empfaenger, Parser
  wirft nicht mehr; Datums- und Volltextfilter in Agenten-Chats.
- deploy/publish.py: Packager-Pfad net10.0, Schalter --agent-file.
- Version 0.1.4.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-09-14 15:40:56 +02:00
co-authored by Claude Opus 5
parent e2567a466f
commit 6581fd0187
29 changed files with 1128 additions and 190 deletions
@@ -1,6 +1,7 @@
using System.Text;
using ClawdDotNet.Core.Config;
using ClawdDotNet.Core.Engine;
using ClawdDotNet.Core.Scheduling;
using ClawdDotNet.Core.State;
using ClawdDotNet.Core.Tools;
using Microsoft.Extensions.Logging;
@@ -28,6 +29,7 @@ public sealed class EngineTaskDispatcher : ITaskDispatcher
private readonly ToolRegistry _toolRegistry;
private readonly IStateStore _stateStore;
private readonly ILogger _logger;
private readonly TimeProvider _clock;
public EngineTaskDispatcher(
AgentEngine engine,
@@ -35,7 +37,8 @@ public sealed class EngineTaskDispatcher : ITaskDispatcher
string instanceId,
ToolRegistry toolRegistry,
IStateStore stateStore,
ILoggerFactory loggerFactory)
ILoggerFactory loggerFactory,
TimeProvider? clock = null)
{
_engine = engine;
_agents = agents;
@@ -43,6 +46,7 @@ public sealed class EngineTaskDispatcher : ITaskDispatcher
_toolRegistry = toolRegistry;
_stateStore = stateStore;
_logger = loggerFactory.CreateLogger("ClawdDotNet.Core.Tasks.Dispatcher");
_clock = clock ?? TimeProvider.System;
}
public async Task<bool> DispatchAsync(TaskItem task, CancellationToken ct)
@@ -148,11 +152,16 @@ public sealed class EngineTaskDispatcher : ITaskDispatcher
return agents.Count == 1 ? agents[0] : null;
}
private static string BuildMessage(TaskItem task)
internal string BuildMessage(TaskItem task)
{
var sb = new StringBuilder();
sb.AppendLine($"[Aufgabe {task.Id}] {task.Title}");
// #589: Weder Systemprompt noch Auftrag nannten bisher die Zeit. Fast jede
// wiederkehrende Aufgabe braucht sie (Dateiname je Tag, "Bericht ueber gestern"),
// und ein Modell ohne diese Zeile raet aus seinem Trainingsstand.
sb.AppendLine(Zeitangabe(task, _clock.GetUtcNow()));
if (!string.IsNullOrWhiteSpace(task.Body))
{
sb.AppendLine();
@@ -168,4 +177,33 @@ public sealed class EngineTaskDispatcher : ITaskDispatcher
return sb.ToString().TrimEnd();
}
/// <summary>
/// Aktuelle Zeit in der Zeitzone der Aufgabe (sonst der des Systems), dazu UTC und -
/// bei wiederkehrenden Aufgaben - der Termin selbst.
/// </summary>
internal static string Zeitangabe(TaskItem task, DateTimeOffset jetzt)
{
var zoneId = string.IsNullOrWhiteSpace(task.When?.TimeZone) ? TimeZones.LocalIanaId : task.When!.TimeZone;
TimeZoneInfo zone;
try
{
zone = TimeZoneInfo.FindSystemTimeZoneById(zoneId);
}
catch (Exception ex) when (ex is TimeZoneNotFoundException or InvalidTimeZoneException)
{
zone = TimeZoneInfo.Local;
zoneId = TimeZones.LocalIanaId;
}
var lokal = TimeZoneInfo.ConvertTime(jetzt, zone);
var invariant = System.Globalization.CultureInfo.InvariantCulture;
var zeile = string.Format(invariant,
"Aktuelle Zeit: {0:dd.MM.yyyy HH:mm} Uhr ({1}, UTC{0:zzz}); in UTC {2:yyyy-MM-ddTHH:mm}Z.",
lokal, zoneId, jetzt.UtcDateTime);
return task.When is { } when
? zeile + $" Termin der Aufgabe: {when.Kind.ToString().ToLowerInvariant()} \"{when.Value}\"."
: zeile;
}
}