namespace ClawdDotNet.Core.Tools; /// /// Wer an diesem Aufruf schon beteiligt ist — die Kette vom ersten Agenten bis zum /// gerade laufenden. /// /// Warum es die Kette gibt (B8). send_message ist ein synchroner /// Aufruf: A hält sein Gate, während es auf B wartet. Ruft B nun A, wartet B auf ein /// Gate, das A hält und erst freigibt, wenn B geantwortet hat. Das löste nur der /// Timeout des äußeren Laufs auf — bis dahin standen beide Agenten. /// /// Die Zusagen, auf denen alles Weitere aufsetzt: /// /// Ein Agent kommt in einer Kette höchstens einmal vor. Damit wartet niemand /// auf ein Gate, das er in derselben Kette schon hält — der Verklemmung ist die /// Grundlage entzogen, nicht nur die Wahrscheinlichkeit genommen. /// Eine Kette ist höchstens MaxDelegationDepth Schritte lang. Zyklen /// sind damit ausgeschlossen, eine lange Staffel A→B→C→D→… aber nicht; die /// kostet Zeit und Tokens, ohne dass jemand sie beauftragt hätte. /// Die Kette gehört zum Aufruf, nicht zum Agenten. Zwei unabhängige /// Läufe desselben Agenten behindern einander nicht. /// /// /// Unveränderlich: gibt eine neue Kette zurück. Ein Aufruf /// kann die Kette seines Aufrufers deshalb nicht verändern, und die Kette lässt sich /// gefahrlos über Fadengrenzen reichen. /// public sealed class DelegationChain { private readonly string[] _agents; private DelegationChain(string[] agents, string? runId = null, string? callerRunId = null) { _agents = agents; RunId = runId; CallerRunId = callerRunId; } /// /// #697: The run of the last agent in the chain — the one that is handing on. Stamped by /// the engine when it builds a tool's context, so a delegated run can name the run it /// came from. null outside a run. /// public string? RunId { get; } /// /// #697: The run of the agent before the last — the run a delegated run was started from. /// Carried over by , so the new run can record its origin. /// public string? CallerRunId { get; } /// #697: The same chain, stamped with the run of its last agent. public DelegationChain WithRun(string runId) => new(_agents, runId, CallerRunId); /// Der Anfang: ein Lauf, den niemand delegiert hat. public static DelegationChain Root(string agentId) => new([agentId]); /// Die Agenten in der Reihenfolge, in der sie beauftragt wurden. public IReadOnlyList Agents => _agents; /// /// Die Zahl der Weitergaben — nicht die der Agenten. Ein Lauf ohne Delegation hat /// die Tiefe 0, A→B die Tiefe 1. /// public int Depth => _agents.Length - 1; /// Ist dieser Agent in der Kette schon vertreten? public bool Contains(string agentId) => Array.IndexOf(_agents, agentId) >= 0; /// Die Kette mit einem weiteren Agenten am Ende. public DelegationChain Append(string agentId) => new([.. _agents, agentId], runId: null, callerRunId: RunId); // the new agent's run is not known yet /// Für Meldungen und Protokoll: A -> B -> C. public override string ToString() => string.Join(" -> ", _agents); }