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);
}