< Summary

Information
Class: NexusLabs.Needlr.AgentFramework.Generators.AgentFrameworkFunctionRegistryGenerator
Assembly: NexusLabs.Needlr.AgentFramework.Generators
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.AgentFramework.Generators/AgentFrameworkFunctionRegistryGenerator.cs
Line coverage
90%
Covered lines: 1110
Uncovered lines: 120
Coverable lines: 1230
Total lines: 2000
Line coverage: 90.2%
Branch coverage
72%
Covered branches: 365
Total branches: 505
Branch coverage: 72.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
Initialize(...)100%11100%
GetAgentFunctionTypeInfo(...)50%2283.33%
TryGetTypeInfo(...)82.89%777695.31%
GetAgentFunctionGroupEntries(...)78.57%141492.3%
GetNeedlrAiAgentTypeInfo(...)70.83%242497.14%
GetHandoffEntries(...)85.71%141492.3%
GetGroupChatEntries(...)80%101090.9%
GetSequenceEntries(...)83.33%121291.66%
GetTerminationConditionEntries(...)87.5%161695.23%
SerializeTypedConstant(...)11.11%821841.66%
GetDescriptionFromAttributes(...)90%1010100%
GetJsonSchemaType(...)54.05%3623738.09%
IsAccessibleFromGeneratedCode(...)37.5%14855.55%
GetFullyQualifiedName(...)100%11100%
SanitizeIdentifier(...)50%332271.42%
ExecuteAll(...)66.66%6695.94%
GenerateTopologyGraphSource(...)100%210%
GenerateMermaidDiagram(...)0%272160%
GetShortName(...)50%44100%
SanitizeMermaidId(...)0%2040%
EscapeMermaidLabel(...)100%210%
GenerateRegistrySource(...)100%22100%
GenerateGroupRegistrySource(...)100%44100%
GenerateAgentRegistrySource(...)100%22100%
GenerateHandoffTopologyRegistrySource(...)100%66100%
GenerateGroupChatRegistrySource(...)100%44100%
GenerateSequentialTopologyRegistrySource(...)100%44100%
GenerateWorkflowFactoryExtensionsSource(...)93.18%4444100%
GenerateAgentFactoryExtensionsSource(...)100%22100%
GenerateAgentTopologyConstantsSource(...)100%66100%
GenerateSyringeExtensionsSource(...)100%22100%
GenerateAIFunctionProviderSource(...)93.75%1616100%
AppendAIFunctionNestedClass(...)95%202095.45%
BuildJsonSchema(...)100%1010100%
BuildJsonSchemaTypeEntry(...)40%261046.15%
AppendParameterExtraction(...)40%251047.05%
AppendIntegerExtraction(...)50%241463.15%
AppendNumberExtraction(...)0%2040%
StripAgentSuffix(...)50%44100%
GroupNameToPascalCase(...)100%1212100%
GenerateBootstrapSource(...)100%11100%
GeneratePartialCompanionSource(...)100%44100%
ShortName()50%44100%
BuildToolsDocComment(...)100%2424100%
.ctor(...)100%11100%
get_TypeName()100%11100%
get_AssemblyName()100%210%
get_IsStatic()100%11100%
get_Methods()100%11100%
.ctor(...)100%11100%
get_TypeName()100%11100%
get_GroupName()100%11100%
.ctor(...)100%11100%
get_TypeName()100%11100%
get_ClassName()100%11100%
get_NamespaceName()100%11100%
get_IsPartial()100%11100%
get_FunctionGroupNames()100%11100%
get_ExplicitFunctionTypeFQNs()100%11100%
get_HasExplicitFunctionTypes()100%11100%
.ctor(...)100%11100%
get_InitialAgentTypeName()100%11100%
get_InitialAgentClassName()100%11100%
get_TargetAgentTypeName()100%11100%
get_HandoffReason()100%11100%
.ctor(...)100%11100%
get_AgentTypeName()100%11100%
get_GroupName()100%11100%
.ctor(...)100%11100%
get_AgentTypeName()100%11100%
get_PipelineName()100%11100%
get_Order()100%11100%
.ctor(...)100%11100%
get_AgentTypeName()100%11100%
get_ConditionTypeFQN()100%11100%
get_CtorArgLiterals()100%11100%
.ctor(...)100%11100%
get_Name()100%11100%
get_TypeFullName()100%11100%
get_JsonSchemaType()100%11100%
get_ItemJsonSchemaType()100%210%
get_IsCancellationToken()100%11100%
get_IsNullable()100%11100%
get_HasDefault()100%11100%
get_Description()100%11100%
get_IsRequired()50%44100%
.ctor(...)100%11100%
get_MethodName()100%11100%
get_IsAsync()100%11100%
get_IsVoidLike()100%11100%
get_ReturnValueTypeFQN()100%210%
get_Parameters()100%11100%
get_Description()100%11100%

File(s)

/home/runner/work/needlr/needlr/src/NexusLabs.Needlr.AgentFramework.Generators/AgentFrameworkFunctionRegistryGenerator.cs

#LineLine coverage
 1// Copyright (c) NexusLabs. All rights reserved.
 2// Licensed under the MIT License.
 3
 4using System;
 5using System.Collections.Generic;
 6using System.Collections.Immutable;
 7using System.Linq;
 8using System.Text;
 9using System.Threading;
 10
 11using Microsoft.CodeAnalysis;
 12using Microsoft.CodeAnalysis.CSharp.Syntax;
 13using Microsoft.CodeAnalysis.Text;
 14
 15namespace NexusLabs.Needlr.AgentFramework.Generators;
 16
 17/// <summary>
 18/// Source generator for Microsoft Agent Framework functions.
 19/// Discovers classes with [AgentFunction] methods and generates a compile-time type registry.
 20/// Also discovers classes with [AgentFunctionGroup] attributes and generates a group registry.
 21/// Also discovers classes with [NeedlrAiAgent] attributes and generates an agent registry.
 22/// Always emits a [ModuleInitializer] that auto-registers all discovered types with
 23/// AgentFrameworkGeneratedBootstrap on assembly load.
 24/// </summary>
 25[Generator]
 26public class AgentFrameworkFunctionRegistryGenerator : IIncrementalGenerator
 27{
 28    private const string AgentFunctionAttributeName = "NexusLabs.Needlr.AgentFramework.AgentFunctionAttribute";
 29    private const string AgentFunctionGroupAttributeName = "NexusLabs.Needlr.AgentFramework.AgentFunctionGroupAttribute"
 30    private const string NeedlrAiAgentAttributeName = "NexusLabs.Needlr.AgentFramework.NeedlrAiAgentAttribute";
 31    private const string AgentHandoffsToAttributeName = "NexusLabs.Needlr.AgentFramework.AgentHandoffsToAttribute";
 32    private const string AgentGroupChatMemberAttributeName = "NexusLabs.Needlr.AgentFramework.AgentGroupChatMemberAttrib
 33    private const string AgentSequenceMemberAttributeName = "NexusLabs.Needlr.AgentFramework.AgentSequenceMemberAttribut
 34    private const string WorkflowRunTerminationConditionAttributeName = "NexusLabs.Needlr.AgentFramework.WorkflowRunTerm
 35
 36    public void Initialize(IncrementalGeneratorInitializationContext context)
 37    {
 38        // [AgentFunction] method-bearing classes → AgentFrameworkFunctionRegistry
 6339        var functionClasses = context.SyntaxProvider
 6340            .CreateSyntaxProvider(
 4137141                predicate: static (s, _) => s is ClassDeclarationSyntax,
 83542                transform: static (ctx, ct) => GetAgentFunctionTypeInfo(ctx, ct))
 89843            .Where(static m => m is not null);
 44
 45        // [AgentFunctionGroup] class-level annotations → AgentFrameworkFunctionGroupRegistry
 6346        var groupClasses = context.SyntaxProvider
 6347            .CreateSyntaxProvider(
 4137148                predicate: static (s, _) => s is ClassDeclarationSyntax,
 83549                transform: static (ctx, ct) => GetAgentFunctionGroupEntries(ctx, ct))
 89850            .Where(static arr => arr.Length > 0);
 51
 52        // [NeedlrAiAgent] declared agent types → AgentRegistry + partial companions
 6353        var agentClasses = context.SyntaxProvider
 6354            .ForAttributeWithMetadataName(
 6355                NeedlrAiAgentAttributeName,
 5856                predicate: static (s, _) => s is ClassDeclarationSyntax,
 5857                transform: static (ctx, ct) => GetNeedlrAiAgentTypeInfo(ctx, ct))
 12158            .Where(static m => m is not null);
 59
 60        // [AgentHandoffsTo] annotations → handoff topology registry
 6361        var handoffEntries = context.SyntaxProvider
 6362            .ForAttributeWithMetadataName(
 6363                AgentHandoffsToAttributeName,
 564                predicate: static (s, _) => s is ClassDeclarationSyntax,
 565                transform: static (ctx, ct) => GetHandoffEntries(ctx, ct))
 6866            .Where(static arr => arr.Length > 0);
 67
 68        // [AgentGroupChatMember] annotations → group chat registry
 6369        var groupChatEntries = context.SyntaxProvider
 6370            .ForAttributeWithMetadataName(
 6371                AgentGroupChatMemberAttributeName,
 872                predicate: static (s, _) => s is ClassDeclarationSyntax,
 873                transform: static (ctx, ct) => GetGroupChatEntries(ctx, ct))
 7174            .Where(static arr => arr.Length > 0);
 75
 76        // [AgentSequenceMember] annotations → sequential pipeline registry
 6377        var sequenceEntries = context.SyntaxProvider
 6378            .ForAttributeWithMetadataName(
 6379                AgentSequenceMemberAttributeName,
 2480                predicate: static (s, _) => s is ClassDeclarationSyntax,
 2481                transform: static (ctx, ct) => GetSequenceEntries(ctx, ct))
 8782            .Where(static arr => arr.Length > 0);
 83
 84        // [WorkflowRunTerminationCondition] → termination conditions per agent
 6385        var terminationConditionEntries = context.SyntaxProvider
 6386            .ForAttributeWithMetadataName(
 6387                WorkflowRunTerminationConditionAttributeName,
 888                predicate: static (s, _) => s is ClassDeclarationSyntax,
 889                transform: static (ctx, ct) => GetTerminationConditionEntries(ctx, ct))
 7190            .Where(static arr => arr.Length > 0);
 91
 92        // Unified output: all seven pipelines combined with compilation metadata and build config.
 93        // Always emits all registries + [ModuleInitializer] bootstrap, even when empty.
 6394        var combined = functionClasses.Collect()
 6395            .Combine(groupClasses.Collect())
 6396            .Combine(agentClasses.Collect())
 6397            .Combine(handoffEntries.Collect())
 6398            .Combine(groupChatEntries.Collect())
 6399            .Combine(sequenceEntries.Collect())
 63100            .Combine(terminationConditionEntries.Collect())
 63101            .Combine(context.CompilationProvider)
 63102            .Combine(context.AnalyzerConfigOptionsProvider);
 103
 63104        context.RegisterSourceOutput(combined,
 63105            static (spc, data) =>
 63106            {
 63107                var ((((((((functionData, groupData), agentData), handoffData), groupChatData), sequenceData), terminati
 63108                ExecuteAll(functionData, groupData, agentData, handoffData, groupChatData, sequenceData, terminationData
 126109            });
 63110    }
 111
 112    private static AgentFunctionTypeInfo? GetAgentFunctionTypeInfo(
 113        GeneratorSyntaxContext context,
 114        CancellationToken cancellationToken)
 115    {
 835116        var classDeclaration = (ClassDeclarationSyntax)context.Node;
 835117        var typeSymbol = context.SemanticModel
 835118            .GetDeclaredSymbol(classDeclaration, cancellationToken) as INamedTypeSymbol;
 119
 835120        if (typeSymbol is null)
 0121            return null;
 122
 835123        return TryGetTypeInfo(typeSymbol);
 124    }
 125
 126    private static AgentFunctionTypeInfo? TryGetTypeInfo(INamedTypeSymbol typeSymbol)
 127    {
 835128        if (typeSymbol.TypeKind != TypeKind.Class)
 0129            return null;
 130
 835131        if (!IsAccessibleFromGeneratedCode(typeSymbol))
 0132            return null;
 133
 835134        if (!typeSymbol.IsStatic && typeSymbol.IsAbstract)
 0135            return null;
 136
 835137        bool hasAgentFunction = false;
 6483138        foreach (var member in typeSymbol.GetMembers())
 139        {
 2411140            if (member is not IMethodSymbol method)
 141                continue;
 142
 1529143            if (method.MethodKind != MethodKind.Ordinary)
 144                continue;
 145
 136146            if (method.DeclaredAccessibility != Accessibility.Public)
 147                continue;
 148
 281149            foreach (var attribute in method.GetAttributes())
 150            {
 9151                if (attribute.AttributeClass?.ToDisplayString() == AgentFunctionAttributeName)
 152                {
 9153                    hasAgentFunction = true;
 9154                    break;
 155                }
 156            }
 157
 136158            if (hasAgentFunction)
 159                break;
 160        }
 161
 835162        if (!hasAgentFunction)
 826163            return null;
 164
 9165        var methodInfos = ImmutableArray.CreateBuilder<AgentFunctionMethodInfo>();
 52166        foreach (var member in typeSymbol.GetMembers())
 167        {
 17168            if (member is not IMethodSymbol method)
 169                continue;
 170
 17171            if (method.MethodKind != MethodKind.Ordinary)
 172                continue;
 173
 9174            if (method.DeclaredAccessibility != Accessibility.Public)
 175                continue;
 176
 9177            bool isAgentFunction = false;
 27178            foreach (var attribute in method.GetAttributes())
 179            {
 9180                if (attribute.AttributeClass?.ToDisplayString() == AgentFunctionAttributeName)
 181                {
 9182                    isAgentFunction = true;
 9183                    break;
 184                }
 185            }
 186
 9187            if (!isAgentFunction)
 188                continue;
 189
 9190            var returnType = method.ReturnType;
 9191            bool isVoid = returnType.SpecialType == SpecialType.System_Void;
 9192            bool isTask = returnType is INamedTypeSymbol nt &&
 9193                nt.ContainingNamespace?.ToDisplayString() == "System.Threading.Tasks" &&
 9194                (nt.MetadataName == "Task" || nt.MetadataName == "ValueTask");
 9195            bool isTaskOfT = returnType is INamedTypeSymbol nt2 &&
 9196                nt2.ContainingNamespace?.ToDisplayString() == "System.Threading.Tasks" &&
 9197                (nt2.MetadataName == "Task`1" || nt2.MetadataName == "ValueTask`1") &&
 9198                nt2.TypeArguments.Length == 1;
 9199            bool isAsync = isTask || isTaskOfT;
 9200            bool isVoidLike = isVoid || isTask;
 9201            string? returnValueTypeFQN = isVoidLike ? null : isTaskOfT
 9202                ? GetFullyQualifiedName(((INamedTypeSymbol)returnType).TypeArguments[0])
 9203                : GetFullyQualifiedName(returnType);
 204
 9205            string? methodDesc = GetDescriptionFromAttributes(method.GetAttributes());
 206
 9207            var parameters = ImmutableArray.CreateBuilder<AgentFunctionParameterInfo>();
 36208            foreach (var param in method.Parameters)
 209            {
 9210                bool isCancellationToken = param.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) == "glob
 9211                bool isNullable = param.NullableAnnotation == NullableAnnotation.Annotated ||
 9212                    (param.Type is INamedTypeSymbol pnt && pnt.ConstructedFrom.SpecialType == SpecialType.System_Nullabl
 9213                bool hasDefault = param.HasExplicitDefaultValue;
 9214                string? paramDesc = GetDescriptionFromAttributes(param.GetAttributes());
 9215                string jsonSchemaType = GetJsonSchemaType(param.Type, out string? itemJsonSchemaType);
 9216                string typeFullName = GetFullyQualifiedName(param.Type);
 217
 9218                parameters.Add(new AgentFunctionParameterInfo(
 9219                    param.Name, typeFullName, jsonSchemaType, itemJsonSchemaType,
 9220                    isCancellationToken, isNullable, hasDefault, paramDesc));
 221            }
 222
 9223            methodInfos.Add(new AgentFunctionMethodInfo(
 9224                method.Name, isAsync, isVoidLike, returnValueTypeFQN,
 9225                parameters.ToImmutable(), methodDesc ?? ""));
 226        }
 227
 9228        return new AgentFunctionTypeInfo(
 9229            GetFullyQualifiedName(typeSymbol),
 9230            typeSymbol.ContainingAssembly?.Name ?? "Unknown",
 9231            typeSymbol.IsStatic,
 9232            methodInfos.ToImmutable());
 233    }
 234
 235    private static ImmutableArray<AgentFunctionGroupEntry> GetAgentFunctionGroupEntries(
 236        GeneratorSyntaxContext context,
 237        CancellationToken cancellationToken)
 238    {
 835239        var classDeclaration = (ClassDeclarationSyntax)context.Node;
 835240        var typeSymbol = context.SemanticModel
 835241            .GetDeclaredSymbol(classDeclaration, cancellationToken) as INamedTypeSymbol;
 242
 835243        if (typeSymbol is null || !IsAccessibleFromGeneratedCode(typeSymbol))
 0244            return ImmutableArray<AgentFunctionGroupEntry>.Empty;
 245
 835246        var entries = ImmutableArray.CreateBuilder<AgentFunctionGroupEntry>();
 247
 2778248        foreach (var attr in typeSymbol.GetAttributes())
 249        {
 554250            if (attr.AttributeClass?.ToDisplayString() != AgentFunctionGroupAttributeName)
 251                continue;
 252
 10253            if (attr.ConstructorArguments.Length != 1)
 254                continue;
 255
 10256            var groupName = attr.ConstructorArguments[0].Value as string;
 10257            if (string.IsNullOrWhiteSpace(groupName))
 258                continue;
 259
 10260            entries.Add(new AgentFunctionGroupEntry(GetFullyQualifiedName(typeSymbol), groupName!));
 261        }
 262
 835263        return entries.ToImmutable();
 264    }
 265
 266    private static NeedlrAiAgentTypeInfo? GetNeedlrAiAgentTypeInfo(
 267        GeneratorAttributeSyntaxContext context,
 268        CancellationToken cancellationToken)
 269    {
 58270        var typeSymbol = context.TargetSymbol as INamedTypeSymbol;
 58271        if (typeSymbol is null || !IsAccessibleFromGeneratedCode(typeSymbol))
 0272            return null;
 273
 58274        var classDeclaration = context.TargetNode as ClassDeclarationSyntax;
 122275        var isPartial = classDeclaration?.Modifiers.Any(m => m.ValueText == "partial") ?? false;
 276
 58277        var namespaceName = typeSymbol.ContainingNamespace?.IsGlobalNamespace == true
 58278            ? null
 58279            : typeSymbol.ContainingNamespace?.ToDisplayString();
 280
 58281        var functionGroupNames = ImmutableArray<string>.Empty;
 58282        var explicitFunctionTypeFQNs = ImmutableArray<string>.Empty;
 58283        var hasExplicitFunctionTypes = false;
 284
 58285        var agentAttr = context.Attributes.FirstOrDefault();
 58286        if (agentAttr is not null)
 287        {
 63288            var groupsArg = agentAttr.NamedArguments.FirstOrDefault(a => a.Key == "FunctionGroups");
 58289            if (groupsArg.Key is not null && groupsArg.Value.Kind == TypedConstantKind.Array)
 290            {
 2291                functionGroupNames = groupsArg.Value.Values
 2292                    .Select(v => v.Value as string)
 2293                    .Where(s => !string.IsNullOrWhiteSpace(s))
 2294                    .Select(s => s!)
 2295                    .ToImmutableArray();
 296            }
 297
 63298            var typesArg = agentAttr.NamedArguments.FirstOrDefault(a => a.Key == "FunctionTypes");
 58299            if (typesArg.Key is not null && typesArg.Value.Kind == TypedConstantKind.Array)
 300            {
 2301                hasExplicitFunctionTypes = true;
 2302                explicitFunctionTypeFQNs = typesArg.Value.Values
 1303                    .Where(v => v.Kind == TypedConstantKind.Type && v.Value is INamedTypeSymbol)
 1304                    .Select(v => GetFullyQualifiedName((INamedTypeSymbol)v.Value!))
 2305                    .ToImmutableArray();
 306            }
 307        }
 308
 58309        return new NeedlrAiAgentTypeInfo(
 58310            GetFullyQualifiedName(typeSymbol),
 58311            typeSymbol.Name,
 58312            namespaceName,
 58313            isPartial,
 58314            functionGroupNames,
 58315            explicitFunctionTypeFQNs,
 58316            hasExplicitFunctionTypes);
 317    }
 318
 319    private static ImmutableArray<HandoffEntry> GetHandoffEntries(
 320        GeneratorAttributeSyntaxContext context,
 321        CancellationToken cancellationToken)
 322    {
 5323        var typeSymbol = context.TargetSymbol as INamedTypeSymbol;
 5324        if (typeSymbol is null || !IsAccessibleFromGeneratedCode(typeSymbol))
 0325            return ImmutableArray<HandoffEntry>.Empty;
 326
 5327        var initialTypeName = GetFullyQualifiedName(typeSymbol);
 5328        var entries = ImmutableArray.CreateBuilder<HandoffEntry>();
 329
 20330        foreach (var attr in context.Attributes)
 331        {
 5332            if (attr.ConstructorArguments.Length < 1)
 333                continue;
 334
 5335            var typeArg = attr.ConstructorArguments[0];
 5336            if (typeArg.Kind != TypedConstantKind.Type || typeArg.Value is not INamedTypeSymbol targetTypeSymbol)
 337                continue;
 338
 5339            var targetTypeName = GetFullyQualifiedName(targetTypeSymbol);
 5340            var reason = attr.ConstructorArguments.Length > 1 ? attr.ConstructorArguments[1].Value as string : null;
 341
 5342            entries.Add(new HandoffEntry(initialTypeName, typeSymbol.Name, targetTypeName, reason));
 343        }
 344
 5345        return entries.ToImmutable();
 346    }
 347
 348    private static ImmutableArray<GroupChatEntry> GetGroupChatEntries(
 349        GeneratorAttributeSyntaxContext context,
 350        CancellationToken cancellationToken)
 351    {
 8352        var typeSymbol = context.TargetSymbol as INamedTypeSymbol;
 8353        if (typeSymbol is null || !IsAccessibleFromGeneratedCode(typeSymbol))
 0354            return ImmutableArray<GroupChatEntry>.Empty;
 355
 8356        var agentTypeName = GetFullyQualifiedName(typeSymbol);
 8357        var entries = ImmutableArray.CreateBuilder<GroupChatEntry>();
 358
 32359        foreach (var attr in context.Attributes)
 360        {
 8361            if (attr.ConstructorArguments.Length < 1)
 362                continue;
 363
 8364            var groupName = attr.ConstructorArguments[0].Value as string;
 8365            if (string.IsNullOrWhiteSpace(groupName))
 366                continue;
 367
 8368            entries.Add(new GroupChatEntry(agentTypeName, groupName!));
 369        }
 370
 8371        return entries.ToImmutable();
 372    }
 373
 374    private static ImmutableArray<SequenceEntry> GetSequenceEntries(
 375        GeneratorAttributeSyntaxContext context,
 376        CancellationToken cancellationToken)
 377    {
 24378        var typeSymbol = context.TargetSymbol as INamedTypeSymbol;
 24379        if (typeSymbol is null || !IsAccessibleFromGeneratedCode(typeSymbol))
 0380            return ImmutableArray<SequenceEntry>.Empty;
 381
 24382        var agentTypeName = GetFullyQualifiedName(typeSymbol);
 24383        var entries = ImmutableArray.CreateBuilder<SequenceEntry>();
 384
 96385        foreach (var attr in context.Attributes)
 386        {
 24387            if (attr.ConstructorArguments.Length < 2)
 388                continue;
 389
 24390            var pipelineName = attr.ConstructorArguments[0].Value as string;
 24391            if (string.IsNullOrWhiteSpace(pipelineName))
 392                continue;
 393
 24394            if (attr.ConstructorArguments[1].Value is not int order)
 395                continue;
 396
 24397            entries.Add(new SequenceEntry(agentTypeName, pipelineName!, order));
 398        }
 399
 24400        return entries.ToImmutable();
 401    }
 402
 403    private static ImmutableArray<TerminationConditionEntry> GetTerminationConditionEntries(
 404        GeneratorAttributeSyntaxContext context,
 405        CancellationToken cancellationToken)
 406    {
 8407        var typeSymbol = context.TargetSymbol as INamedTypeSymbol;
 8408        if (typeSymbol is null || !IsAccessibleFromGeneratedCode(typeSymbol))
 0409            return ImmutableArray<TerminationConditionEntry>.Empty;
 410
 8411        var agentTypeName = GetFullyQualifiedName(typeSymbol);
 8412        var entries = ImmutableArray.CreateBuilder<TerminationConditionEntry>();
 413
 32414        foreach (var attr in context.Attributes)
 415        {
 8416            if (attr.ConstructorArguments.Length < 1)
 417                continue;
 418
 8419            var typeArg = attr.ConstructorArguments[0];
 8420            if (typeArg.Kind != TypedConstantKind.Type || typeArg.Value is not INamedTypeSymbol condTypeSymbol)
 421                continue;
 422
 8423            var condTypeFQN = GetFullyQualifiedName(condTypeSymbol);
 8424            var ctorArgLiterals = ImmutableArray<string>.Empty;
 425
 8426            if (attr.ConstructorArguments.Length > 1)
 427            {
 8428                var paramsArg = attr.ConstructorArguments[1];
 8429                if (paramsArg.Kind == TypedConstantKind.Array)
 430                {
 8431                    ctorArgLiterals = paramsArg.Values
 8432                        .Select(SerializeTypedConstant)
 8433                        .Where(s => s is not null)
 8434                        .Select(s => s!)
 8435                        .ToImmutableArray();
 436                }
 437            }
 438
 8439            entries.Add(new TerminationConditionEntry(agentTypeName, condTypeFQN, ctorArgLiterals));
 440        }
 441
 8442        return entries.ToImmutable();
 443    }
 444
 445    private static string? SerializeTypedConstant(TypedConstant constant)
 446    {
 8447        return constant.Kind switch
 8448        {
 8449            TypedConstantKind.Primitive when constant.Value is string s =>
 8450                "\"" + s.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"",
 0451            TypedConstantKind.Primitive when constant.Value is int i => i.ToString(),
 0452            TypedConstantKind.Primitive when constant.Value is long l => l.ToString() + "L",
 0453            TypedConstantKind.Primitive when constant.Value is bool b => b ? "true" : "false",
 0454            TypedConstantKind.Primitive when constant.Value is null => "null",
 0455            TypedConstantKind.Type when constant.Value is ITypeSymbol ts =>
 0456                $"typeof({GetFullyQualifiedName(ts)})",
 0457            _ => null,
 8458        };
 459    }
 460
 461    private static string? GetDescriptionFromAttributes(ImmutableArray<AttributeData> attributes)
 462    {
 55463        foreach (var attr in attributes)
 464        {
 10465            if (attr.AttributeClass?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) ==
 10466                "global::System.ComponentModel.DescriptionAttribute" &&
 10467                attr.ConstructorArguments.Length == 1 &&
 10468                attr.ConstructorArguments[0].Value is string desc)
 1469                return desc;
 470        }
 17471        return null;
 472    }
 473
 474    private static string GetJsonSchemaType(ITypeSymbol type, out string? itemType)
 475    {
 9476        itemType = null;
 9477        if (type is INamedTypeSymbol nullable && nullable.ConstructedFrom.SpecialType == SpecialType.System_Nullable_T)
 0478            type = nullable.TypeArguments[0];
 479
 9480        switch (type.SpecialType)
 481        {
 3482            case SpecialType.System_String: return "string";
 0483            case SpecialType.System_Boolean: return "boolean";
 484            case SpecialType.System_Byte:
 485            case SpecialType.System_SByte:
 486            case SpecialType.System_Int16:
 487            case SpecialType.System_UInt16:
 488            case SpecialType.System_Int32:
 489            case SpecialType.System_UInt32:
 490            case SpecialType.System_Int64:
 5491            case SpecialType.System_UInt64: return "integer";
 492            case SpecialType.System_Single:
 493            case SpecialType.System_Double:
 0494            case SpecialType.System_Decimal: return "number";
 495        }
 496
 1497        if (type is IArrayTypeSymbol arrayType)
 498        {
 0499            itemType = GetJsonSchemaType(arrayType.ElementType, out _);
 0500            return "array";
 501        }
 502
 1503        if (type is INamedTypeSymbol named && named.IsGenericType && named.TypeArguments.Length == 1)
 504        {
 0505            var baseName = named.ConstructedFrom.ToDisplayString();
 0506            if (baseName == "System.Collections.Generic.IEnumerable<T>" ||
 0507                baseName == "System.Collections.Generic.List<T>" ||
 0508                baseName == "System.Collections.Generic.IReadOnlyList<T>" ||
 0509                baseName == "System.Collections.Generic.ICollection<T>" ||
 0510                baseName == "System.Collections.Generic.IList<T>")
 511            {
 0512                itemType = GetJsonSchemaType(named.TypeArguments[0], out _);
 0513                return "array";
 514            }
 515        }
 516
 1517        return "";
 518    }
 519
 520    private static bool IsAccessibleFromGeneratedCode(INamedTypeSymbol typeSymbol)
 521    {
 1773522        if (typeSymbol.DeclaredAccessibility == Accessibility.Private ||
 1773523            typeSymbol.DeclaredAccessibility == Accessibility.Protected)
 0524            return false;
 525
 1773526        var current = typeSymbol.ContainingType;
 1773527        while (current != null)
 528        {
 0529            if (current.DeclaredAccessibility == Accessibility.Private)
 0530                return false;
 0531            current = current.ContainingType;
 532        }
 533
 1773534        return true;
 535    }
 536
 537    private static string GetFullyQualifiedName(ITypeSymbol typeSymbol) =>
 152538        "global::" + typeSymbol.ToDisplayString(
 152539            SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle(
 152540                SymbolDisplayGlobalNamespaceStyle.Omitted));
 541
 542    private static string SanitizeIdentifier(string name)
 543    {
 63544        if (string.IsNullOrEmpty(name))
 0545            return "Generated";
 546
 63547        var sb = new StringBuilder(name.Length);
 1638548        foreach (var c in name)
 549        {
 756550            if (char.IsLetterOrDigit(c) || c == '_')
 756551                sb.Append(c);
 0552            else if (c == '.' || c == '-' || c == ' ')
 0553                sb.Append(c == '.' ? '.' : '_');
 554        }
 555
 63556        var result = sb.ToString();
 63557        var segments = result.Split('.');
 252558        for (int i = 0; i < segments.Length; i++)
 559        {
 63560            if (segments[i].Length > 0 && char.IsDigit(segments[i][0]))
 0561                segments[i] = "_" + segments[i];
 562        }
 563
 126564        return string.Join(".", segments.Where(s => s.Length > 0));
 565    }
 566
 567    private static void ExecuteAll(
 568        ImmutableArray<AgentFunctionTypeInfo?> functionData,
 569        ImmutableArray<ImmutableArray<AgentFunctionGroupEntry>> groupData,
 570        ImmutableArray<NeedlrAiAgentTypeInfo?> agentData,
 571        ImmutableArray<ImmutableArray<HandoffEntry>> handoffData,
 572        ImmutableArray<ImmutableArray<GroupChatEntry>> groupChatData,
 573        ImmutableArray<ImmutableArray<SequenceEntry>> sequenceData,
 574        ImmutableArray<ImmutableArray<TerminationConditionEntry>> terminationData,
 575        Compilation compilation,
 576        Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptionsProvider configOptions,
 577        SourceProductionContext spc)
 578    {
 63579        var assemblyName = compilation.AssemblyName ?? "UnknownAssembly";
 63580        var safeAssemblyName = SanitizeIdentifier(assemblyName);
 581
 63582        var validFunctionTypes = functionData
 9583            .Where(t => t.HasValue)
 9584            .Select(t => t!.Value)
 63585            .ToList();
 586
 73587        var allGroupEntries = groupData.SelectMany(a => a).ToList();
 63588        var groupedByName = allGroupEntries
 10589            .GroupBy(e => e.GroupName)
 91590            .ToDictionary(g => g.Key, g => g.Select(e => e.TypeName).Distinct().ToList());
 591
 63592        var validAgentTypes = agentData
 58593            .Where(t => t.HasValue)
 58594            .Select(t => t!.Value)
 63595            .ToList();
 596
 68597        var allHandoffEntries = handoffData.SelectMany(a => a).ToList();
 63598        var handoffByInitialAgent = allHandoffEntries
 5599            .GroupBy(e => (e.InitialAgentTypeName, e.InitialAgentClassName))
 63600            .ToDictionary(
 5601                g => g.Key,
 73602                g => g.Select(e => (e.TargetAgentTypeName, e.HandoffReason)).ToList());
 603
 71604        var allGroupChatEntries = groupChatData.SelectMany(a => a).ToList();
 63605        var groupChatByGroupName = allGroupChatEntries
 8606            .GroupBy(e => e.GroupName)
 79607            .ToDictionary(g => g.Key, g => g.Select(e => e.AgentTypeName).Distinct().ToList());
 608
 87609        var allSequenceEntries = sequenceData.SelectMany(a => a).ToList();
 63610        var sequenceByPipelineName = allSequenceEntries
 24611            .GroupBy(e => e.PipelineName)
 63612            .ToDictionary(
 13613                g => g.Key,
 124614                g => g.OrderBy(e => e.Order).Select(e => e.AgentTypeName).ToList());
 615
 63616        var conditionsByAgentTypeName = terminationData
 8617            .SelectMany(a => a)
 8618            .GroupBy(e => e.AgentTypeName)
 79619            .ToDictionary(g => g.Key, g => g.ToList());
 620
 621        // Always emit all registries (may be empty) and the bootstrap
 63622        spc.AddSource("AgentFrameworkFunctions.g.cs",
 63623            SourceText.From(GenerateRegistrySource(validFunctionTypes, safeAssemblyName), Encoding.UTF8));
 624
 63625        spc.AddSource("AgentFrameworkFunctionGroups.g.cs",
 63626            SourceText.From(GenerateGroupRegistrySource(groupedByName, safeAssemblyName), Encoding.UTF8));
 627
 63628        spc.AddSource("AgentRegistry.g.cs",
 63629            SourceText.From(GenerateAgentRegistrySource(validAgentTypes, safeAssemblyName), Encoding.UTF8));
 630
 63631        spc.AddSource("AgentHandoffTopologyRegistry.g.cs",
 63632            SourceText.From(GenerateHandoffTopologyRegistrySource(handoffByInitialAgent, safeAssemblyName), Encoding.UTF
 633
 63634        spc.AddSource("AgentGroupChatRegistry.g.cs",
 63635            SourceText.From(GenerateGroupChatRegistrySource(groupChatByGroupName, safeAssemblyName), Encoding.UTF8));
 636
 63637        spc.AddSource("AgentSequentialTopologyRegistry.g.cs",
 63638            SourceText.From(GenerateSequentialTopologyRegistrySource(sequenceByPipelineName, safeAssemblyName), Encoding
 639
 63640        spc.AddSource("NeedlrAgentFrameworkBootstrap.g.cs",
 63641            SourceText.From(GenerateBootstrapSource(safeAssemblyName), Encoding.UTF8));
 642
 63643        spc.AddSource("WorkflowFactoryExtensions.g.cs",
 63644            SourceText.From(GenerateWorkflowFactoryExtensionsSource(
 63645                handoffByInitialAgent, groupChatByGroupName, sequenceByPipelineName,
 63646                conditionsByAgentTypeName, safeAssemblyName), Encoding.UTF8));
 647
 63648        spc.AddSource("AgentFactoryExtensions.g.cs",
 63649            SourceText.From(GenerateAgentFactoryExtensionsSource(validAgentTypes, safeAssemblyName), Encoding.UTF8));
 650
 63651        spc.AddSource("AgentTopologyConstants.g.cs",
 63652            SourceText.From(GenerateAgentTopologyConstantsSource(validAgentTypes, allGroupEntries, sequenceByPipelineNam
 653
 63654        spc.AddSource("AgentFrameworkSyringeExtensions.g.cs",
 63655            SourceText.From(GenerateSyringeExtensionsSource(allGroupEntries, safeAssemblyName), Encoding.UTF8));
 656
 63657        spc.AddSource("GeneratedAIFunctionProvider.g.cs",
 63658            SourceText.From(GenerateAIFunctionProviderSource(validFunctionTypes, safeAssemblyName), Encoding.UTF8));
 659
 63660        configOptions.GlobalOptions.TryGetValue("build_property.NeedlrDiagnostics", out var diagValue);
 63661        if (string.Equals(diagValue, "true", StringComparison.OrdinalIgnoreCase))
 662        {
 0663            var mermaid = GenerateMermaidDiagram(handoffByInitialAgent, groupChatByGroupName, sequenceByPipelineName);
 664
 0665            spc.AddSource("AgentTopologyGraph.g.cs",
 0666                SourceText.From(GenerateTopologyGraphSource(mermaid, safeAssemblyName), Encoding.UTF8));
 667        }
 668
 669        // Partial companions for [NeedlrAiAgent] classes declared as partial
 196670        foreach (var agentType in validAgentTypes.Where(a => a.IsPartial))
 671        {
 6672            var safeTypeName = agentType.TypeName
 6673                .Replace("global::", "")
 6674                .Replace(".", "_")
 6675                .Replace("<", "_")
 6676                .Replace(">", "_");
 677
 6678            spc.AddSource($"{safeTypeName}.NeedlrAiAgent.g.cs",
 6679                SourceText.From(GeneratePartialCompanionSource(agentType, groupedByName), Encoding.UTF8));
 680        }
 63681    }
 682
 683    private static string GenerateTopologyGraphSource(
 684        string diagram,
 685        string safeAssemblyName)
 686    {
 0687        var escaped = diagram.Replace("\"", "\"\"");
 688
 0689        var sb = new StringBuilder();
 0690        sb.AppendLine("// <auto-generated/>");
 0691        sb.AppendLine("#nullable enable");
 0692        sb.AppendLine();
 0693        sb.AppendLine($"namespace {safeAssemblyName}.Generated;");
 0694        sb.AppendLine();
 0695        sb.AppendLine("internal static class AgentTopologyGraphDiagnostics");
 0696        sb.AppendLine("{");
 0697        sb.AppendLine($"    public const string AgentTopologyGraph = @\"{escaped}\";");
 0698        sb.AppendLine("}");
 0699        return sb.ToString();
 700    }
 701
 702    private static string GenerateMermaidDiagram(
 703        Dictionary<(string InitialAgentTypeName, string InitialAgentClassName), List<(string TargetAgentTypeName, string
 704        Dictionary<string, List<string>> groupChatByGroupName,
 705        Dictionary<string, List<string>> sequenceByPipelineName)
 706    {
 0707        var sb = new StringBuilder();
 0708        sb.AppendLine("graph TD");
 709
 0710        foreach (var kvp in handoffByInitialAgent.OrderBy(k => k.Key.InitialAgentClassName))
 711        {
 0712            var sourceClass = kvp.Key.InitialAgentClassName;
 0713            foreach (var (targetFqn, reason) in kvp.Value)
 714            {
 0715                var targetClass = GetShortName(targetFqn);
 0716                if (string.IsNullOrWhiteSpace(reason))
 0717                    sb.AppendLine($"    {sourceClass} --> {targetClass}");
 718                else
 0719                    sb.AppendLine($"    {sourceClass} -->|\"{EscapeMermaidLabel(reason!)}\"| {targetClass}");
 720            }
 721        }
 722
 0723        foreach (var kvp in groupChatByGroupName.OrderBy(k => k.Key))
 724        {
 0725            sb.AppendLine($"    subgraph GroupChat_{SanitizeMermaidId(kvp.Key)}");
 0726            foreach (var memberFqn in kvp.Value)
 0727                sb.AppendLine($"        {GetShortName(memberFqn)}");
 0728            sb.AppendLine("    end");
 729        }
 730
 0731        foreach (var kvp in sequenceByPipelineName.OrderBy(k => k.Key))
 732        {
 0733            sb.AppendLine($"    subgraph Sequential_{SanitizeMermaidId(kvp.Key)}");
 0734            var agents = kvp.Value;
 0735            if (agents.Count == 1)
 736            {
 0737                sb.AppendLine($"        {GetShortName(agents[0])}");
 738            }
 739            else
 740            {
 0741                for (int i = 0; i < agents.Count - 1; i++)
 742                {
 0743                    var cur = GetShortName(agents[i]);
 0744                    var next = GetShortName(agents[i + 1]);
 0745                    sb.AppendLine($"        {cur} -->|\"{i + 1}\"| {next}");
 746                }
 747            }
 0748            sb.AppendLine("    end");
 749        }
 750
 0751        return sb.ToString();
 752    }
 753
 754    private static string GetShortName(string fqn)
 755    {
 18756        var stripped = fqn.StartsWith("global::", StringComparison.Ordinal) ? fqn.Substring(8) : fqn;
 18757        var lastDot = stripped.LastIndexOf('.');
 18758        return lastDot >= 0 ? stripped.Substring(lastDot + 1) : stripped;
 759    }
 760
 761    private static string SanitizeMermaidId(string name)
 762    {
 0763        var sb = new StringBuilder();
 0764        foreach (var c in name)
 0765            sb.Append(char.IsLetterOrDigit(c) ? c : '_');
 0766        return sb.ToString();
 767    }
 768
 769    private static string EscapeMermaidLabel(string label) =>
 0770        label.Replace("\"", "&quot;");
 771
 772    private static string GenerateRegistrySource(
 773        List<AgentFunctionTypeInfo> types,
 774        string safeAssemblyName)
 775    {
 63776        var sb = new StringBuilder();
 777
 63778        sb.AppendLine("// <auto-generated/>");
 63779        sb.AppendLine("// Needlr AgentFramework Functions");
 63780        sb.AppendLine("#nullable enable");
 63781        sb.AppendLine();
 63782        sb.AppendLine("using System;");
 63783        sb.AppendLine("using System.Collections.Generic;");
 63784        sb.AppendLine();
 63785        sb.AppendLine($"namespace {safeAssemblyName}.Generated;");
 63786        sb.AppendLine();
 63787        sb.AppendLine("/// <summary>");
 63788        sb.AppendLine("/// Generated registry for Microsoft Agent Framework function types discovered at compile time.")
 63789        sb.AppendLine("/// Pass <see cref=\"AllFunctionTypes\"/> to");
 63790        sb.AppendLine("/// <c>AgentFrameworkSyringeExtensions.AddAgentFunctionsFromGenerated</c>.");
 63791        sb.AppendLine("/// </summary>");
 63792        sb.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.AgentFramework.Generat
 63793        sb.AppendLine("public static class AgentFrameworkFunctionRegistry");
 63794        sb.AppendLine("{");
 63795        sb.AppendLine("    /// <summary>");
 63796        sb.AppendLine("    /// All types containing methods decorated with [AgentFunction], discovered at compile time."
 63797        sb.AppendLine("    /// </summary>");
 63798        sb.AppendLine("    public static IReadOnlyList<Type> AllFunctionTypes { get; } = new Type[]");
 63799        sb.AppendLine("    {");
 800
 144801        foreach (var type in types)
 802        {
 9803            sb.AppendLine($"        typeof({type.TypeName}),");
 804        }
 805
 63806        sb.AppendLine("    };");
 63807        sb.AppendLine();
 63808        sb.AppendLine("    /// <summary>");
 63809        sb.AppendLine("    /// Gets the number of function types discovered at compile time.");
 63810        sb.AppendLine("    /// </summary>");
 63811        sb.AppendLine($"    public static int Count => {types.Count};");
 63812        sb.AppendLine("}");
 813
 63814        return sb.ToString();
 815    }
 816
 817    private static string GenerateGroupRegistrySource(
 818        Dictionary<string, List<string>> groupedByName,
 819        string safeAssemblyName)
 820    {
 63821        var sb = new StringBuilder();
 822
 63823        sb.AppendLine("// <auto-generated/>");
 63824        sb.AppendLine("// Needlr AgentFramework Function Groups");
 63825        sb.AppendLine("#nullable enable");
 63826        sb.AppendLine();
 63827        sb.AppendLine("using System;");
 63828        sb.AppendLine("using System.Collections.Generic;");
 63829        sb.AppendLine();
 63830        sb.AppendLine($"namespace {safeAssemblyName}.Generated;");
 63831        sb.AppendLine();
 63832        sb.AppendLine("/// <summary>");
 63833        sb.AppendLine("/// Generated registry for Microsoft Agent Framework function groups discovered at compile time."
 63834        sb.AppendLine("/// Pass <see cref=\"AllGroups\"/> to");
 63835        sb.AppendLine("/// <c>AgentFrameworkSyringeExtensions.AddAgentFunctionGroupsFromGenerated</c>.");
 63836        sb.AppendLine("/// </summary>");
 63837        sb.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.AgentFramework.Generat
 63838        sb.AppendLine("public static class AgentFrameworkFunctionGroupRegistry");
 63839        sb.AppendLine("{");
 63840        sb.AppendLine("    /// <summary>");
 63841        sb.AppendLine("    /// All function groups, mapping group name to the types in that group, discovered at compile
 63842        sb.AppendLine("    /// </summary>");
 63843        sb.AppendLine("    public static IReadOnlyDictionary<string, IReadOnlyList<Type>> AllGroups { get; } =");
 63844        sb.AppendLine("        new Dictionary<string, IReadOnlyList<Type>>()");
 63845        sb.AppendLine("        {");
 846
 153847        foreach (var kvp in groupedByName.OrderBy(k => k.Key))
 848        {
 9849            var escapedName = kvp.Key.Replace("\"", "\\\"");
 9850            var typeNames = kvp.Value;
 9851            sb.AppendLine($"            [\"{escapedName}\"] = new Type[]");
 9852            sb.AppendLine("            {");
 38853            foreach (var typeName in typeNames)
 854            {
 10855                sb.AppendLine($"                typeof({typeName}),");
 856            }
 9857            sb.AppendLine("            },");
 858        }
 859
 63860        sb.AppendLine("        };");
 63861        sb.AppendLine("}");
 862
 63863        return sb.ToString();
 864    }
 865
 866    private static string GenerateAgentRegistrySource(
 867        List<NeedlrAiAgentTypeInfo> types,
 868        string safeAssemblyName)
 869    {
 63870        var sb = new StringBuilder();
 871
 63872        sb.AppendLine("// <auto-generated/>");
 63873        sb.AppendLine("// Needlr AgentFramework Agent Registry");
 63874        sb.AppendLine("#nullable enable");
 63875        sb.AppendLine();
 63876        sb.AppendLine("using System;");
 63877        sb.AppendLine("using System.Collections.Generic;");
 63878        sb.AppendLine();
 63879        sb.AppendLine($"namespace {safeAssemblyName}.Generated;");
 63880        sb.AppendLine();
 63881        sb.AppendLine("/// <summary>");
 63882        sb.AppendLine("/// Generated registry for agent types declared with [NeedlrAiAgent], discovered at compile time.
 63883        sb.AppendLine("/// </summary>");
 63884        sb.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.AgentFramework.Generat
 63885        sb.AppendLine("public static class AgentRegistry");
 63886        sb.AppendLine("{");
 63887        sb.AppendLine("    /// <summary>");
 63888        sb.AppendLine("    /// All types decorated with [NeedlrAiAgent], discovered at compile time.");
 63889        sb.AppendLine("    /// </summary>");
 63890        sb.AppendLine("    public static IReadOnlyList<Type> AllAgentTypes { get; } = new Type[]");
 63891        sb.AppendLine("    {");
 892
 242893        foreach (var type in types)
 894        {
 58895            sb.AppendLine($"        typeof({type.TypeName}),");
 896        }
 897
 63898        sb.AppendLine("    };");
 63899        sb.AppendLine();
 63900        sb.AppendLine("    /// <summary>");
 63901        sb.AppendLine("    /// Gets the number of agent types discovered at compile time.");
 63902        sb.AppendLine("    /// </summary>");
 63903        sb.AppendLine($"    public static int Count => {types.Count};");
 63904        sb.AppendLine("}");
 905
 63906        return sb.ToString();
 907    }
 908
 909    private static string GenerateHandoffTopologyRegistrySource(
 910        Dictionary<(string InitialAgentTypeName, string InitialAgentClassName), List<(string TargetAgentTypeName, string
 911        string safeAssemblyName)
 912    {
 63913        var sb = new StringBuilder();
 63914        sb.AppendLine("// <auto-generated/>");
 63915        sb.AppendLine("// Needlr AgentFramework Handoff Topology Registry");
 63916        sb.AppendLine("#nullable enable");
 63917        sb.AppendLine();
 63918        sb.AppendLine("using System;");
 63919        sb.AppendLine("using System.Collections.Generic;");
 63920        sb.AppendLine();
 63921        sb.AppendLine($"namespace {safeAssemblyName}.Generated;");
 63922        sb.AppendLine();
 63923        sb.AppendLine("/// <summary>");
 63924        sb.AppendLine("/// Generated registry of agent handoff topology declared via [AgentHandoffsTo] attributes.");
 63925        sb.AppendLine("/// </summary>");
 63926        sb.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.AgentFramework.Generat
 63927        sb.AppendLine("public static class AgentHandoffTopologyRegistry");
 63928        sb.AppendLine("{");
 63929        sb.AppendLine("    /// <summary>");
 63930        sb.AppendLine("    /// All handoff relationships, mapping initial agent type to its declared handoff targets.");
 63931        sb.AppendLine("    /// </summary>");
 63932        sb.AppendLine("    public static IReadOnlyDictionary<Type, IReadOnlyList<(Type TargetType, string? HandoffReason
 63933        sb.AppendLine("        new Dictionary<Type, IReadOnlyList<(Type, string?)>>()");
 63934        sb.AppendLine("        {");
 935
 141936        foreach (var kvp in handoffByInitialAgent.OrderBy(k => k.Key.InitialAgentClassName))
 937        {
 5938            sb.AppendLine($"            [typeof({kvp.Key.InitialAgentTypeName})] = new (Type, string?)[]");
 5939            sb.AppendLine("            {");
 20940            foreach (var (targetType, reason) in kvp.Value)
 941            {
 5942                var reasonLiteral = reason is null ? "null" : $"\"{reason.Replace("\"", "\\\"")}\"";
 5943                sb.AppendLine($"                (typeof({targetType}), {reasonLiteral}),");
 944            }
 5945            sb.AppendLine("            },");
 946        }
 947
 63948        sb.AppendLine("        };");
 63949        sb.AppendLine("}");
 63950        return sb.ToString();
 951    }
 952
 953    private static string GenerateGroupChatRegistrySource(
 954        Dictionary<string, List<string>> groupChatByGroupName,
 955        string safeAssemblyName)
 956    {
 63957        var sb = new StringBuilder();
 63958        sb.AppendLine("// <auto-generated/>");
 63959        sb.AppendLine("// Needlr AgentFramework Group Chat Registry");
 63960        sb.AppendLine("#nullable enable");
 63961        sb.AppendLine();
 63962        sb.AppendLine("using System;");
 63963        sb.AppendLine("using System.Collections.Generic;");
 63964        sb.AppendLine();
 63965        sb.AppendLine($"namespace {safeAssemblyName}.Generated;");
 63966        sb.AppendLine();
 63967        sb.AppendLine("/// <summary>");
 63968        sb.AppendLine("/// Generated registry of agent group chat memberships declared via [AgentGroupChatMember] attrib
 63969        sb.AppendLine("/// </summary>");
 63970        sb.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.AgentFramework.Generat
 63971        sb.AppendLine("public static class AgentGroupChatRegistry");
 63972        sb.AppendLine("{");
 63973        sb.AppendLine("    /// <summary>");
 63974        sb.AppendLine("    /// All group chat groups, mapping group name to the agent types in that group.");
 63975        sb.AppendLine("    /// </summary>");
 63976        sb.AppendLine("    public static IReadOnlyDictionary<string, IReadOnlyList<Type>> AllGroups { get; } =");
 63977        sb.AppendLine("        new Dictionary<string, IReadOnlyList<Type>>()");
 63978        sb.AppendLine("        {");
 979
 138980        foreach (var kvp in groupChatByGroupName.OrderBy(k => k.Key))
 981        {
 4982            var escapedName = kvp.Key.Replace("\"", "\\\"");
 4983            sb.AppendLine($"            [\"{escapedName}\"] = new Type[]");
 4984            sb.AppendLine("            {");
 24985            foreach (var typeName in kvp.Value)
 8986                sb.AppendLine($"                typeof({typeName}),");
 4987            sb.AppendLine("            },");
 988        }
 989
 63990        sb.AppendLine("        };");
 63991        sb.AppendLine("}");
 63992        return sb.ToString();
 993    }
 994
 995    private static string GenerateSequentialTopologyRegistrySource(
 996        Dictionary<string, List<string>> sequenceByPipelineName,
 997        string safeAssemblyName)
 998    {
 63999        var sb = new StringBuilder();
 631000        sb.AppendLine("// <auto-generated/>");
 631001        sb.AppendLine("// Needlr AgentFramework Sequential Pipeline Registry");
 631002        sb.AppendLine("#nullable enable");
 631003        sb.AppendLine();
 631004        sb.AppendLine("using System;");
 631005        sb.AppendLine("using System.Collections.Generic;");
 631006        sb.AppendLine();
 631007        sb.AppendLine($"namespace {safeAssemblyName}.Generated;");
 631008        sb.AppendLine();
 631009        sb.AppendLine("/// <summary>");
 631010        sb.AppendLine("/// Generated registry of sequential pipeline memberships declared via [AgentSequenceMember] attr
 631011        sb.AppendLine("/// Agents within each pipeline are stored in ascending order value order.");
 631012        sb.AppendLine("/// </summary>");
 631013        sb.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.AgentFramework.Generat
 631014        sb.AppendLine("public static class AgentSequentialTopologyRegistry");
 631015        sb.AppendLine("{");
 631016        sb.AppendLine("    /// <summary>");
 631017        sb.AppendLine("    /// All sequential pipelines, mapping pipeline name to the ordered agent types.");
 631018        sb.AppendLine("    /// </summary>");
 631019        sb.AppendLine("    public static IReadOnlyDictionary<string, IReadOnlyList<Type>> AllPipelines { get; } =");
 631020        sb.AppendLine("        new Dictionary<string, IReadOnlyList<Type>>()");
 631021        sb.AppendLine("        {");
 1022
 1651023        foreach (var kvp in sequenceByPipelineName.OrderBy(k => k.Key))
 1024        {
 131025            var escapedName = kvp.Key.Replace("\"", "\\\"");
 131026            sb.AppendLine($"            [\"{escapedName}\"] = new Type[]");
 131027            sb.AppendLine("            {");
 741028            foreach (var typeName in kvp.Value)
 241029                sb.AppendLine($"                typeof({typeName}),");
 131030            sb.AppendLine("            },");
 1031        }
 1032
 631033        sb.AppendLine("        };");
 631034        sb.AppendLine("}");
 631035        return sb.ToString();
 1036    }
 1037
 1038    private static string GenerateWorkflowFactoryExtensionsSource(
 1039        Dictionary<(string InitialAgentTypeName, string InitialAgentClassName), List<(string TargetAgentTypeName, string
 1040        Dictionary<string, List<string>> groupChatByGroupName,
 1041        Dictionary<string, List<string>> sequenceByPipelineName,
 1042        Dictionary<string, List<TerminationConditionEntry>> conditionsByAgentTypeName,
 1043        string safeAssemblyName)
 1044    {
 631045        var sb = new StringBuilder();
 631046        sb.AppendLine("// <auto-generated/>");
 631047        sb.AppendLine("// Needlr AgentFramework IWorkflowFactory Extension Methods");
 631048        sb.AppendLine("#nullable enable");
 631049        sb.AppendLine();
 631050        sb.AppendLine("using Microsoft.Agents.AI.Workflows;");
 631051        sb.AppendLine("using NexusLabs.Needlr.AgentFramework;");
 631052        sb.AppendLine();
 631053        sb.AppendLine($"namespace {safeAssemblyName}.Generated;");
 631054        sb.AppendLine();
 631055        sb.AppendLine("/// <summary>");
 631056        sb.AppendLine("/// Generated strongly-typed extension methods on <see cref=\"IWorkflowFactory\"/>.");
 631057        sb.AppendLine("/// Each method encapsulates an agent type or group name so the composition root");
 631058        sb.AppendLine("/// requires no direct agent type references or magic strings.");
 631059        sb.AppendLine("/// </summary>");
 631060        sb.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.AgentFramework.Generat
 631061        sb.AppendLine("public static partial class WorkflowFactoryExtensions");
 631062        sb.AppendLine("{");
 1063
 1411064        foreach (var kvp in handoffByInitialAgent.OrderBy(k => k.Key.InitialAgentClassName))
 1065        {
 51066            var className = kvp.Key.InitialAgentClassName;
 51067            var typeName = kvp.Key.InitialAgentTypeName;
 51068            var methodName = $"Create{StripAgentSuffix(className)}HandoffWorkflow";
 1069
 51070            sb.AppendLine($"    /// <summary>");
 51071            sb.AppendLine($"    /// Creates a handoff workflow starting from <see cref=\"{typeName.Replace("global::", "
 51072            sb.AppendLine($"    /// </summary>");
 51073            sb.AppendLine($"    /// <remarks>");
 51074            sb.AppendLine($"    /// Handoff targets declared via <see cref=\"global::NexusLabs.Needlr.AgentFramework.Age
 51075            sb.AppendLine($"    /// <list type=\"bullet\">");
 201076            foreach (var (targetTypeName, reason) in kvp.Value)
 1077            {
 51078                var cref = targetTypeName.Replace("global::", "");
 51079                if (string.IsNullOrEmpty(reason))
 41080                    sb.AppendLine($"    /// <item><description><see cref=\"{cref}\"/></description></item>");
 1081                else
 11082                    sb.AppendLine($"    /// <item><description><see cref=\"{cref}\"/> — {reason}</description></item>");
 1083            }
 51084            sb.AppendLine($"    /// </list>");
 51085            sb.AppendLine($"    /// </remarks>");
 51086            sb.AppendLine($"    public static Workflow {methodName}(this IWorkflowFactory workflowFactory)");
 51087            sb.AppendLine($"        => workflowFactory.CreateHandoffWorkflow<{typeName}>();");
 51088            sb.AppendLine();
 1089
 51090            var allHandoffAgents = new[] { typeName }
 51091                .Concat(kvp.Value.Select(v => v.TargetAgentTypeName))
 51092                .ToList();
 51093            var handoffConditions = allHandoffAgents
 101094                .SelectMany(t => conditionsByAgentTypeName.TryGetValue(t, out var c) ? c : Enumerable.Empty<TerminationC
 51095                .ToList();
 1096
 51097            if (handoffConditions.Count > 0)
 1098            {
 11099                var runMethodName = $"Run{StripAgentSuffix(className)}HandoffWorkflowAsync";
 11100                sb.AppendLine($"    /// <summary>");
 11101                sb.AppendLine($"    /// Creates and runs the handoff workflow starting from <see cref=\"{typeName.Replac
 11102                sb.AppendLine($"    /// </summary>");
 11103                sb.AppendLine($"    /// <remarks>");
 11104                sb.AppendLine($"    /// Termination conditions are evaluated after each completed agent turn (Layer 2)."
 11105                sb.AppendLine($"    /// The workflow stops early when any condition is satisfied.");
 11106                sb.AppendLine($"    /// </remarks>");
 11107                sb.AppendLine($"    /// <param name=\"workflowFactory\">The workflow factory.</param>");
 11108                sb.AppendLine($"    /// <param name=\"message\">The input message to start the workflow.</param>");
 11109                sb.AppendLine($"    /// <param name=\"cancellationToken\">Optional cancellation token.</param>");
 11110                sb.AppendLine($"    /// <returns>A dictionary mapping executor IDs to their response text.</returns>");
 11111                sb.AppendLine($"    public static async global::System.Threading.Tasks.Task<global::System.Collections.G
 11112                sb.AppendLine($"        this IWorkflowFactory workflowFactory,");
 11113                sb.AppendLine($"        string message,");
 11114                sb.AppendLine($"        global::System.Threading.CancellationToken cancellationToken = default)");
 11115                sb.AppendLine($"    {{");
 11116                sb.AppendLine($"        var workflow = workflowFactory.{methodName}();");
 11117                sb.AppendLine($"        global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.AgentF
 11118                sb.AppendLine($"            new global::System.Collections.Generic.List<global::NexusLabs.Needlr.AgentFr
 11119                sb.AppendLine($"            {{");
 41120                foreach (var cond in handoffConditions)
 1121                {
 11122                    var args = cond.CtorArgLiterals.IsEmpty ? "" : string.Join(", ", cond.CtorArgLiterals);
 11123                    sb.AppendLine($"                new {cond.ConditionTypeFQN}({args}),");
 1124                }
 11125                sb.AppendLine($"            }};");
 11126                sb.AppendLine($"        return await global::NexusLabs.Needlr.AgentFramework.Workflows.StreamingRunWorkf
 11127                sb.AppendLine($"    }}");
 11128                sb.AppendLine();
 1129            }
 1130        }
 1131
 1381132        foreach (var kvp in groupChatByGroupName.OrderBy(k => k.Key))
 1133        {
 41134            var groupName = kvp.Key;
 41135            var methodSuffix = GroupNameToPascalCase(groupName);
 41136            var methodName = $"Create{methodSuffix}GroupChatWorkflow";
 41137            var escapedGroupName = groupName.Replace("\"", "\\\"");
 1138
 41139            sb.AppendLine($"    /// <summary>");
 41140            sb.AppendLine($"    /// Creates a round-robin group chat workflow for the \"{escapedGroupName}\" group.");
 41141            sb.AppendLine($"    /// </summary>");
 41142            sb.AppendLine($"    /// <remarks>");
 41143            sb.AppendLine($"    /// Participants declared via <see cref=\"global::NexusLabs.Needlr.AgentFramework.AgentG
 41144            sb.AppendLine($"    /// <list type=\"bullet\">");
 241145            foreach (var participantTypeName in kvp.Value)
 1146            {
 81147                var cref = participantTypeName.Replace("global::", "");
 81148                sb.AppendLine($"    /// <item><description><see cref=\"{cref}\"/></description></item>");
 1149            }
 41150            sb.AppendLine($"    /// </list>");
 41151            sb.AppendLine($"    /// </remarks>");
 41152            sb.AppendLine($"    public static Workflow {methodName}(this IWorkflowFactory workflowFactory, int maxIterat
 41153            sb.AppendLine($"        => workflowFactory.CreateGroupChatWorkflow(\"{escapedGroupName}\", maxIterations);")
 41154            sb.AppendLine();
 1155
 41156            var gcConditions = kvp.Value
 81157                .SelectMany(t => conditionsByAgentTypeName.TryGetValue(t, out var c) ? c : Enumerable.Empty<TerminationC
 41158                .ToList();
 1159
 41160            if (gcConditions.Count > 0)
 1161            {
 11162                var runMethodName = $"Run{methodSuffix}GroupChatWorkflowAsync";
 11163                sb.AppendLine($"    /// <summary>");
 11164                sb.AppendLine($"    /// Creates and runs the \"{escapedGroupName}\" group chat workflow, applying declar
 11165                sb.AppendLine($"    /// </summary>");
 11166                sb.AppendLine($"    /// <remarks>");
 11167                sb.AppendLine($"    /// Termination conditions are evaluated after each completed agent turn (Layer 2)."
 11168                sb.AppendLine($"    /// The workflow stops early when any condition is satisfied.");
 11169                sb.AppendLine($"    /// </remarks>");
 11170                sb.AppendLine($"    /// <param name=\"workflowFactory\">The workflow factory.</param>");
 11171                sb.AppendLine($"    /// <param name=\"message\">The input message to start the workflow.</param>");
 11172                sb.AppendLine($"    /// <param name=\"maxIterations\">Maximum number of group chat iterations.</param>")
 11173                sb.AppendLine($"    /// <param name=\"cancellationToken\">Optional cancellation token.</param>");
 11174                sb.AppendLine($"    /// <returns>A dictionary mapping executor IDs to their response text.</returns>");
 11175                sb.AppendLine($"    public static async global::System.Threading.Tasks.Task<global::System.Collections.G
 11176                sb.AppendLine($"        this IWorkflowFactory workflowFactory,");
 11177                sb.AppendLine($"        string message,");
 11178                sb.AppendLine($"        int maxIterations = 10,");
 11179                sb.AppendLine($"        global::System.Threading.CancellationToken cancellationToken = default)");
 11180                sb.AppendLine($"    {{");
 11181                sb.AppendLine($"        var workflow = workflowFactory.{methodName}(maxIterations);");
 11182                sb.AppendLine($"        global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.AgentF
 11183                sb.AppendLine($"            new global::System.Collections.Generic.List<global::NexusLabs.Needlr.AgentFr
 11184                sb.AppendLine($"            {{");
 41185                foreach (var cond in gcConditions)
 1186                {
 11187                    var args = cond.CtorArgLiterals.IsEmpty ? "" : string.Join(", ", cond.CtorArgLiterals);
 11188                    sb.AppendLine($"                new {cond.ConditionTypeFQN}({args}),");
 1189                }
 11190                sb.AppendLine($"            }};");
 11191                sb.AppendLine($"        return await global::NexusLabs.Needlr.AgentFramework.Workflows.StreamingRunWorkf
 11192                sb.AppendLine($"    }}");
 11193                sb.AppendLine();
 1194            }
 1195        }
 1196
 1651197        foreach (var kvp in sequenceByPipelineName.OrderBy(k => k.Key))
 1198        {
 131199            var pipelineName = kvp.Key;
 131200            var methodSuffix = GroupNameToPascalCase(pipelineName);
 131201            var methodName = $"Create{methodSuffix}SequentialWorkflow";
 131202            var escapedPipelineName = pipelineName.Replace("\"", "\\\"");
 1203
 131204            sb.AppendLine($"    /// <summary>");
 131205            sb.AppendLine($"    /// Creates a sequential pipeline workflow for the \"{escapedPipelineName}\" pipeline.")
 131206            sb.AppendLine($"    /// </summary>");
 131207            sb.AppendLine($"    /// <remarks>");
 131208            sb.AppendLine($"    /// Agents declared via <see cref=\"global::NexusLabs.Needlr.AgentFramework.AgentSequenc
 131209            sb.AppendLine($"    /// <list type=\"number\">");
 741210            foreach (var memberTypeName in kvp.Value)
 1211            {
 241212                var cref = memberTypeName.Replace("global::", "");
 241213                sb.AppendLine($"    /// <item><description><see cref=\"{cref}\"/></description></item>");
 1214            }
 131215            sb.AppendLine($"    /// </list>");
 131216            sb.AppendLine($"    /// </remarks>");
 131217            sb.AppendLine($"    public static Workflow {methodName}(this IWorkflowFactory workflowFactory)");
 131218            sb.AppendLine($"        => workflowFactory.CreateSequentialWorkflow(\"{escapedPipelineName}\");");
 131219            sb.AppendLine();
 1220
 131221            var seqConditions = kvp.Value
 241222                .SelectMany(t => conditionsByAgentTypeName.TryGetValue(t, out var c) ? c : Enumerable.Empty<TerminationC
 131223                .ToList();
 1224
 131225            if (seqConditions.Count > 0)
 1226            {
 51227                var runMethodName = $"Run{methodSuffix}SequentialWorkflowAsync";
 51228                sb.AppendLine($"    /// <summary>");
 51229                sb.AppendLine($"    /// Creates and runs the \"{escapedPipelineName}\" sequential workflow, applying dec
 51230                sb.AppendLine($"    /// </summary>");
 51231                sb.AppendLine($"    /// <remarks>");
 51232                sb.AppendLine($"    /// Termination conditions are evaluated after each completed agent turn (Layer 2)."
 51233                sb.AppendLine($"    /// The workflow stops early when any condition is satisfied.");
 51234                sb.AppendLine($"    /// </remarks>");
 51235                sb.AppendLine($"    /// <param name=\"workflowFactory\">The workflow factory.</param>");
 51236                sb.AppendLine($"    /// <param name=\"message\">The input message to start the workflow.</param>");
 51237                sb.AppendLine($"    /// <param name=\"cancellationToken\">Optional cancellation token.</param>");
 51238                sb.AppendLine($"    /// <returns>A dictionary mapping executor IDs to their response text.</returns>");
 51239                sb.AppendLine($"    public static async global::System.Threading.Tasks.Task<global::System.Collections.G
 51240                sb.AppendLine($"        this IWorkflowFactory workflowFactory,");
 51241                sb.AppendLine($"        string message,");
 51242                sb.AppendLine($"        global::System.Threading.CancellationToken cancellationToken = default)");
 51243                sb.AppendLine($"    {{");
 51244                sb.AppendLine($"        var workflow = workflowFactory.{methodName}();");
 51245                sb.AppendLine($"        global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.AgentF
 51246                sb.AppendLine($"            new global::System.Collections.Generic.List<global::NexusLabs.Needlr.AgentFr
 51247                sb.AppendLine($"            {{");
 221248                foreach (var cond in seqConditions)
 1249                {
 61250                    var args = cond.CtorArgLiterals.IsEmpty ? "" : string.Join(", ", cond.CtorArgLiterals);
 61251                    sb.AppendLine($"                new {cond.ConditionTypeFQN}({args}),");
 1252                }
 51253                sb.AppendLine($"            }};");
 51254                sb.AppendLine($"        return await global::NexusLabs.Needlr.AgentFramework.Workflows.StreamingRunWorkf
 51255                sb.AppendLine($"    }}");
 51256                sb.AppendLine();
 1257            }
 1258        }
 1259
 631260        sb.AppendLine("}");
 631261        return sb.ToString();
 1262    }
 1263
 1264    private static string GenerateAgentFactoryExtensionsSource(
 1265        List<NeedlrAiAgentTypeInfo> agents,
 1266        string safeAssemblyName)
 1267    {
 631268        var sb = new StringBuilder();
 631269        sb.AppendLine("// <auto-generated/>");
 631270        sb.AppendLine("// Needlr AgentFramework IAgentFactory Extension Methods");
 631271        sb.AppendLine("#nullable enable");
 631272        sb.AppendLine();
 631273        sb.AppendLine("using Microsoft.Agents.AI;");
 631274        sb.AppendLine("using NexusLabs.Needlr.AgentFramework;");
 631275        sb.AppendLine();
 631276        sb.AppendLine($"namespace {safeAssemblyName}.Generated;");
 631277        sb.AppendLine();
 631278        sb.AppendLine("/// <summary>");
 631279        sb.AppendLine("/// Generated strongly-typed extension methods on <see cref=\"IAgentFactory\"/>.");
 631280        sb.AppendLine("/// Each method creates an agent from its <c>[NeedlrAiAgent]</c> declaration,");
 631281        sb.AppendLine("/// eliminating magic strings and direct type references at the composition root.");
 631282        sb.AppendLine("/// </summary>");
 631283        sb.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.AgentFramework.Generat
 631284        sb.AppendLine("public static class GeneratedAgentFactoryExtensions");
 631285        sb.AppendLine("{");
 1286
 3001287        foreach (var agent in agents.OrderBy(a => a.ClassName))
 1288        {
 581289            sb.AppendLine($"    /// <summary>");
 581290            sb.AppendLine($"    /// Creates an <see cref=\"AIAgent\"/> configured for <see cref=\"{agent.TypeName.Replac
 581291            sb.AppendLine($"    /// </summary>");
 581292            sb.AppendLine($"    public static AIAgent Create{agent.ClassName}(this IAgentFactory factory)");
 581293            sb.AppendLine($"        => factory.CreateAgent<{agent.TypeName}>();");
 581294            sb.AppendLine();
 1295        }
 1296
 631297        sb.AppendLine("}");
 631298        return sb.ToString();
 1299    }
 1300
 1301    private static string GenerateAgentTopologyConstantsSource(
 1302        List<NeedlrAiAgentTypeInfo> agents,
 1303        List<AgentFunctionGroupEntry> groupEntries,
 1304        Dictionary<string, List<string>> sequenceByPipelineName,
 1305        string safeAssemblyName)
 1306    {
 631307        var sb = new StringBuilder();
 631308        sb.AppendLine("// <auto-generated/>");
 631309        sb.AppendLine("// Needlr AgentFramework Topology Constants");
 631310        sb.AppendLine("#nullable enable");
 631311        sb.AppendLine();
 631312        sb.AppendLine($"namespace {safeAssemblyName}.Generated;");
 631313        sb.AppendLine();
 1314
 631315        sb.AppendLine("/// <summary>String constants for agent type names discovered at compile time.</summary>");
 631316        sb.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.AgentFramework.Generat
 631317        sb.AppendLine("public static class AgentNames");
 631318        sb.AppendLine("{");
 3001319        foreach (var agent in agents.OrderBy(a => a.ClassName))
 1320        {
 581321            sb.AppendLine($"    /// <summary>The name of <see cref=\"{agent.TypeName.Replace("global::", "")}\"/>.</summ
 581322            sb.AppendLine($"    public const string {agent.ClassName} = \"{agent.ClassName}\";");
 1323        }
 631324        sb.AppendLine("}");
 631325        sb.AppendLine();
 1326
 771327        var groupNames = groupEntries.Select(e => e.GroupName).Distinct().OrderBy(g => g).ToList();
 631328        sb.AppendLine("/// <summary>String constants for function group names discovered at compile time.</summary>");
 631329        sb.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.AgentFramework.Generat
 631330        sb.AppendLine("public static class GroupNames");
 631331        sb.AppendLine("{");
 1441332        foreach (var gn in groupNames)
 1333        {
 91334            var propName = GroupNameToPascalCase(gn);
 91335            var escaped = gn.Replace("\"", "\\\"");
 91336            sb.AppendLine($"    /// <summary>The group name <c>\"{escaped}\"</c>.</summary>");
 91337            sb.AppendLine($"    public const string {propName} = \"{escaped}\";");
 1338        }
 631339        sb.AppendLine("}");
 631340        sb.AppendLine();
 1341
 631342        sb.AppendLine("/// <summary>String constants for sequential pipeline names discovered at compile time.</summary>
 631343        sb.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.AgentFramework.Generat
 631344        sb.AppendLine("public static class PipelineNames");
 631345        sb.AppendLine("{");
 1651346        foreach (var pn in sequenceByPipelineName.Keys.OrderBy(k => k))
 1347        {
 131348            var propName = GroupNameToPascalCase(pn);
 131349            var escaped = pn.Replace("\"", "\\\"");
 131350            sb.AppendLine($"    /// <summary>The pipeline name <c>\"{escaped}\"</c>.</summary>");
 131351            sb.AppendLine($"    public const string {propName} = \"{escaped}\";");
 1352        }
 631353        sb.AppendLine("}");
 1354
 631355        return sb.ToString();
 1356    }
 1357
 1358    private static string GenerateSyringeExtensionsSource(
 1359        List<AgentFunctionGroupEntry> groupEntries,
 1360        string safeAssemblyName)
 1361    {
 631362        var sb = new StringBuilder();
 631363        sb.AppendLine("// <auto-generated/>");
 631364        sb.AppendLine("// Needlr AgentFramework AgentFrameworkSyringe Extension Methods");
 631365        sb.AppendLine("#nullable enable");
 631366        sb.AppendLine();
 631367        sb.AppendLine("using System.Diagnostics.CodeAnalysis;");
 631368        sb.AppendLine("using NexusLabs.Needlr.AgentFramework;");
 631369        sb.AppendLine();
 631370        sb.AppendLine($"namespace {safeAssemblyName}.Generated;");
 631371        sb.AppendLine();
 631372        sb.AppendLine("/// <summary>");
 631373        sb.AppendLine("/// Generated strongly-typed extension methods on <see cref=\"AgentFrameworkSyringe\"/>.");
 631374        sb.AppendLine("/// Each method registers a named function group without requiring direct type references.");
 631375        sb.AppendLine("/// </summary>");
 631376        sb.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.AgentFramework.Generat
 631377        sb.AppendLine("public static class GeneratedAgentFrameworkSyringeExtensions");
 631378        sb.AppendLine("{");
 1379
 631380        var byGroupName = groupEntries
 101381            .GroupBy(e => e.GroupName)
 721382            .OrderBy(g => g.Key);
 1383
 1441384        foreach (var grp in byGroupName)
 1385        {
 91386            var methodName = $"With{GroupNameToPascalCase(grp.Key)}";
 91387            var escapedGroupName = grp.Key.Replace("\"", "\\\"");
 191388            var types = grp.Select(e => e.TypeName).Distinct().ToList();
 1389
 91390            sb.AppendLine($"    /// <summary>Registers the '{escapedGroupName}' function group with the syringe.</summar
 91391            sb.AppendLine($"    [RequiresUnreferencedCode(\"AgentFramework function setup uses reflection to discover [A
 91392            sb.AppendLine($"    [RequiresDynamicCode(\"AgentFramework function setup uses reflection APIs that require d
 91393            sb.AppendLine($"    public static AgentFrameworkSyringe {methodName}(this AgentFrameworkSyringe syringe)");
 191394            sb.AppendLine($"        => syringe.AddAgentFunctions(new global::System.Type[] {{ {string.Join(", ", types.S
 91395            sb.AppendLine();
 1396        }
 1397
 631398        sb.AppendLine("}");
 631399        return sb.ToString();
 1400    }
 1401
 1402    private static string GenerateAIFunctionProviderSource(
 1403        List<AgentFunctionTypeInfo> types,
 1404        string safeAssemblyName)
 1405    {
 631406        var sb = new StringBuilder();
 631407        sb.AppendLine("// <auto-generated/>");
 631408        sb.AppendLine("#nullable enable");
 631409        sb.AppendLine();
 631410        sb.AppendLine("using global::Microsoft.Extensions.DependencyInjection;");
 631411        sb.AppendLine();
 631412        sb.AppendLine($"namespace {safeAssemblyName}.Generated;");
 631413        sb.AppendLine();
 631414        sb.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.AgentFramework.Generat
 631415        sb.AppendLine("internal sealed class GeneratedAIFunctionProvider : global::NexusLabs.Needlr.AgentFramework.IAIFu
 631416        sb.AppendLine("{");
 631417        sb.AppendLine("    public bool TryGetFunctions(");
 631418        sb.AppendLine("        global::System.Type functionType,");
 631419        sb.AppendLine("        global::System.IServiceProvider serviceProvider,");
 631420        sb.AppendLine("        [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)]");
 631421        sb.AppendLine("        out global::System.Collections.Generic.IReadOnlyList<global::Microsoft.Extensions.AI.AIFu
 631422        sb.AppendLine("    {");
 1423
 631424        if (types.Count == 0)
 1425        {
 541426            sb.AppendLine("        functions = null;");
 541427            sb.AppendLine("        return false;");
 1428        }
 1429        else
 1430        {
 91431            var first = true;
 361432            foreach (var type in types)
 1433            {
 91434                var keyword = first ? "if" : "else if";
 91435                first = false;
 91436                sb.AppendLine($"        {keyword} (functionType == typeof({type.TypeName}))");
 91437                sb.AppendLine("        {");
 91438                if (!type.IsStatic)
 81439                    sb.AppendLine($"            var typed = serviceProvider.GetRequiredService<{type.TypeName}>();");
 91440                sb.AppendLine("            functions = new global::System.Collections.Generic.List<global::Microsoft.Ext
 91441                sb.AppendLine("            {");
 361442                foreach (var m in type.Methods)
 1443                {
 91444                    var nestedName = $"{GetShortName(type.TypeName)}_{m.MethodName}";
 91445                    if (type.IsStatic)
 11446                        sb.AppendLine($"                new {nestedName}(),");
 1447                    else
 81448                        sb.AppendLine($"                new {nestedName}(typed),");
 1449                }
 91450                sb.AppendLine("            }.AsReadOnly();");
 91451                sb.AppendLine("            return true;");
 91452                sb.AppendLine("        }");
 1453            }
 91454            sb.AppendLine("        functions = null;");
 91455            sb.AppendLine("        return false;");
 1456        }
 1457
 631458        sb.AppendLine("    }");
 631459        sb.AppendLine();
 1460
 1441461        foreach (var type in types)
 1462        {
 91463            var shortTypeName = GetShortName(type.TypeName);
 361464            foreach (var m in type.Methods)
 1465            {
 91466                var nestedName = $"{shortTypeName}_{m.MethodName}";
 91467                AppendAIFunctionNestedClass(sb, type, m, nestedName);
 1468            }
 1469        }
 1470
 631471        sb.AppendLine("}");
 631472        return sb.ToString();
 1473    }
 1474
 1475    private static void AppendAIFunctionNestedClass(
 1476        StringBuilder sb,
 1477        AgentFunctionTypeInfo type,
 1478        AgentFunctionMethodInfo method,
 1479        string nestedClassName)
 1480    {
 91481        sb.AppendLine($"    private sealed class {nestedClassName} : global::Microsoft.Extensions.AI.AIFunction");
 91482        sb.AppendLine("    {");
 1483
 91484        if (!type.IsStatic)
 81485            sb.AppendLine($"        private readonly {type.TypeName} _instance;");
 1486
 91487        var schemaJson = BuildJsonSchema(method.Parameters);
 91488        sb.AppendLine("        private static readonly global::System.Text.Json.JsonElement _schema =");
 91489        sb.AppendLine($"            global::System.Text.Json.JsonDocument.Parse(\"\"\"{schemaJson}\"\"\").RootElement.Cl
 91490        sb.AppendLine();
 1491
 91492        if (!type.IsStatic)
 81493            sb.AppendLine($"        public {nestedClassName}({type.TypeName} instance) {{ _instance = instance; }}");
 1494        else
 11495            sb.AppendLine($"        public {nestedClassName}() {{ }}");
 1496
 91497        sb.AppendLine();
 1498
 91499        var escapedName = method.MethodName.Replace("\"", "\\\"");
 91500        var escapedDesc = method.Description.Replace("\\", "\\\\").Replace("\"", "\\\"");
 91501        sb.AppendLine($"        public override string Name => \"{escapedName}\";");
 91502        sb.AppendLine($"        public override string Description => \"{escapedDesc}\";");
 91503        sb.AppendLine("        public override global::System.Text.Json.JsonElement JsonSchema => _schema;");
 91504        sb.AppendLine();
 1505
 91506        if (method.IsAsync)
 11507            sb.AppendLine("        protected override async global::System.Threading.Tasks.ValueTask<object?> InvokeCore
 1508        else
 81509            sb.AppendLine("        protected override global::System.Threading.Tasks.ValueTask<object?> InvokeCoreAsync(
 1510
 91511        sb.AppendLine("            global::Microsoft.Extensions.AI.AIFunctionArguments arguments,");
 91512        sb.AppendLine("            global::System.Threading.CancellationToken ct)");
 91513        sb.AppendLine("        {");
 1514
 361515        foreach (var param in method.Parameters)
 1516        {
 91517            if (param.IsCancellationToken)
 1518                continue;
 81519            AppendParameterExtraction(sb, param);
 1520        }
 1521
 181522        var paramList = string.Join(", ", method.Parameters.Select(p => p.IsCancellationToken ? "ct" : p.Name));
 91523        var instanceExpr = type.IsStatic ? type.TypeName : "_instance";
 1524
 91525        if (method.IsAsync)
 1526        {
 11527            if (method.IsVoidLike)
 1528            {
 01529                sb.AppendLine($"            await {instanceExpr}.{method.MethodName}({paramList}).ConfigureAwait(false);
 01530                sb.AppendLine("            return null;");
 1531            }
 1532            else
 1533            {
 11534                sb.AppendLine($"            var result = await {instanceExpr}.{method.MethodName}({paramList}).Configure
 11535                sb.AppendLine("            return result;");
 1536            }
 1537        }
 1538        else
 1539        {
 81540            if (method.IsVoidLike)
 1541            {
 21542                sb.AppendLine($"            {instanceExpr}.{method.MethodName}({paramList});");
 21543                sb.AppendLine("            return global::System.Threading.Tasks.ValueTask.FromResult<object?>(null);");
 1544            }
 1545            else
 1546            {
 61547                sb.AppendLine($"            var result = {instanceExpr}.{method.MethodName}({paramList});");
 61548                sb.AppendLine("            return global::System.Threading.Tasks.ValueTask.FromResult<object?>(result);"
 1549            }
 1550        }
 1551
 91552        sb.AppendLine("        }");
 91553        sb.AppendLine("    }");
 91554        sb.AppendLine();
 91555    }
 1556
 1557    private static string BuildJsonSchema(ImmutableArray<AgentFunctionParameterInfo> parameters)
 1558    {
 181559        var nonCtParams = parameters.Where(p => !p.IsCancellationToken).ToList();
 91560        if (nonCtParams.Count == 0)
 31561            return "{\"type\":\"object\",\"properties\":{}}";
 1562
 61563        var props = new StringBuilder();
 61564        var required = new List<string>();
 1565
 61566        props.Append("{");
 61567        var firstProp = true;
 281568        foreach (var param in nonCtParams)
 1569        {
 101570            if (!firstProp) props.Append(",");
 81571            firstProp = false;
 81572            var escapedParamName = param.Name.Replace("\"", "\\\"");
 81573            props.Append($"\"{escapedParamName}\":");
 81574            props.Append(BuildJsonSchemaTypeEntry(param));
 81575            if (param.IsRequired)
 71576                required.Add(param.Name);
 1577        }
 61578        props.Append("}");
 1579
 61580        var sb = new StringBuilder();
 61581        sb.Append("{\"type\":\"object\",\"properties\":");
 61582        sb.Append(props);
 61583        if (required.Count > 0)
 1584        {
 51585            sb.Append(",\"required\":[");
 121586            sb.Append(string.Join(",", required.Select(r => "\"" + r.Replace("\"", "\\\"") + "\"")));
 51587            sb.Append("]");
 1588        }
 61589        sb.Append("}");
 61590        return sb.ToString();
 1591    }
 1592
 1593    private static string BuildJsonSchemaTypeEntry(AgentFunctionParameterInfo param)
 1594    {
 81595        if (string.IsNullOrEmpty(param.JsonSchemaType))
 01596            return "{}";
 1597
 81598        if (param.JsonSchemaType == "array")
 1599        {
 01600            var desc = string.IsNullOrEmpty(param.Description)
 01601                ? ""
 01602                : $",\"description\":\"{param.Description!.Replace("\\", "\\\\").Replace("\"", "\\\"")}\"";
 01603            if (!string.IsNullOrEmpty(param.ItemJsonSchemaType))
 01604                return $"{{\"type\":\"array\",\"items\":{{\"type\":\"{param.ItemJsonSchemaType}\"}}{desc}}}";
 01605            return $"{{\"type\":\"array\"{desc}}}";
 1606        }
 1607
 81608        if (!string.IsNullOrEmpty(param.Description))
 1609        {
 11610            var escapedDesc = param.Description!.Replace("\\", "\\\\").Replace("\"", "\\\"");
 11611            return $"{{\"type\":\"{param.JsonSchemaType}\",\"description\":\"{escapedDesc}\"}}";
 1612        }
 1613
 71614        return $"{{\"type\":\"{param.JsonSchemaType}\"}}";
 1615    }
 1616
 1617    private static void AppendParameterExtraction(StringBuilder sb, AgentFunctionParameterInfo param)
 1618    {
 81619        var rawVar = $"_raw_{param.Name}";
 81620        var jVar = $"_j_{param.Name}";
 81621        sb.AppendLine($"            arguments.TryGetValue(\"{param.Name}\", out var {rawVar});");
 1622
 81623        switch (param.JsonSchemaType)
 1624        {
 1625            case "string":
 31626                sb.AppendLine($"            var {param.Name} = {rawVar} is global::System.Text.Json.JsonElement {jVar} ?
 31627                break;
 1628            case "boolean":
 1629            {
 01630                var bVar = $"_b_{param.Name}";
 01631                sb.AppendLine($"            var {param.Name} = {rawVar} is global::System.Text.Json.JsonElement {jVar} ?
 01632                break;
 1633            }
 1634            case "integer":
 51635                AppendIntegerExtraction(sb, param, rawVar, jVar);
 51636                break;
 1637            case "number":
 01638                AppendNumberExtraction(sb, param, rawVar, jVar);
 01639                break;
 1640            default:
 1641            {
 01642                var cVar = $"_c_{param.Name}";
 01643                var nullSuppress = param.IsNullable ? "" : "!";
 01644                sb.AppendLine($"            var {param.Name} = {rawVar} is {param.TypeFullName} {cVar} ? {cVar} : defaul
 1645                break;
 1646            }
 1647        }
 01648    }
 1649
 1650    private static void AppendIntegerExtraction(StringBuilder sb, AgentFunctionParameterInfo param, string rawVar, strin
 1651    {
 51652        var typeFqn = param.TypeFullName;
 1653        string getMethod, castType, convertMethod;
 51654        var iVar = $"_i_{param.Name}";
 1655
 51656        if (typeFqn.Contains("System.Int64"))
 01657        { getMethod = "GetInt64()"; castType = "long"; convertMethod = "ToInt64"; }
 51658        else if (typeFqn.Contains("System.Int16"))
 01659        { getMethod = "GetInt16()"; castType = "short"; convertMethod = "ToInt16"; }
 51660        else if (typeFqn.Contains("System.SByte"))
 01661        { getMethod = "GetSByte()"; castType = "sbyte"; convertMethod = "ToSByte"; }
 51662        else if (typeFqn.Contains("System.Byte"))
 01663        { getMethod = "GetByte()"; castType = "byte"; convertMethod = "ToByte"; }
 51664        else if (typeFqn.Contains("System.UInt64"))
 01665        { getMethod = "GetUInt64()"; castType = "ulong"; convertMethod = "ToUInt64"; }
 51666        else if (typeFqn.Contains("System.UInt32"))
 01667        { getMethod = "GetUInt32()"; castType = "uint"; convertMethod = "ToUInt32"; }
 51668        else if (typeFqn.Contains("System.UInt16"))
 01669        { getMethod = "GetUInt16()"; castType = "ushort"; convertMethod = "ToUInt16"; }
 1670        else
 151671        { getMethod = "GetInt32()"; castType = "int"; convertMethod = "ToInt32"; }
 1672
 51673        sb.AppendLine($"            var {param.Name} = {rawVar} is global::System.Text.Json.JsonElement {jVar} ? {jVar}.
 51674    }
 1675
 1676    private static void AppendNumberExtraction(StringBuilder sb, AgentFunctionParameterInfo param, string rawVar, string
 1677    {
 01678        var typeFqn = param.TypeFullName;
 1679        string getMethod, castType, convertMethod;
 01680        var nVar = $"_n_{param.Name}";
 1681
 01682        if (typeFqn.Contains("System.Single"))
 01683        { getMethod = "GetSingle()"; castType = "float"; convertMethod = "ToSingle"; }
 01684        else if (typeFqn.Contains("System.Decimal"))
 01685        { getMethod = "GetDecimal()"; castType = "decimal"; convertMethod = "ToDecimal"; }
 1686        else
 01687        { getMethod = "GetDouble()"; castType = "double"; convertMethod = "ToDouble"; }
 1688
 01689        sb.AppendLine($"            var {param.Name} = {rawVar} is global::System.Text.Json.JsonElement {jVar} ? {jVar}.
 01690    }
 1691
 1692    private static string StripAgentSuffix(string className)
 1693    {
 1694        const string suffix = "Agent";
 61695        return className.EndsWith(suffix, StringComparison.Ordinal) && className.Length > suffix.Length
 61696            ? className.Substring(0, className.Length - suffix.Length)
 61697            : className;
 1698    }
 1699
 1700    private static string GroupNameToPascalCase(string groupName)
 1701    {
 481702        var sb = new StringBuilder();
 481703        var capitalizeNext = true;
 12361704        foreach (var c in groupName)
 1705        {
 5701706            if (c == '-' || c == '_' || c == ' ')
 1707            {
 441708                capitalizeNext = true;
 1709            }
 5261710            else if (char.IsLetterOrDigit(c))
 1711            {
 5261712                sb.Append(capitalizeNext ? char.ToUpperInvariant(c) : c);
 5261713                capitalizeNext = false;
 1714            }
 1715        }
 481716        return sb.ToString();
 1717    }
 1718
 1719    private static string GenerateBootstrapSource(string safeAssemblyName)
 1720    {
 631721        return $@"// <auto-generated/>
 631722// Needlr AgentFramework Module Initializer
 631723#nullable enable
 631724
 631725using System.Runtime.CompilerServices;
 631726
 631727namespace {safeAssemblyName}.Generated;
 631728
 631729/// <summary>
 631730/// Auto-registers source-generated Agent Framework types with the Needlr bootstrap.
 631731/// Fires automatically when the assembly loads — no explicit <c>Add*FromGenerated()</c> calls needed.
 631732/// </summary>
 631733[global::System.CodeDom.Compiler.GeneratedCodeAttribute(""NexusLabs.Needlr.AgentFramework.Generators"", ""1.0.0"")]
 631734internal static class NeedlrAgentFrameworkModuleInitializer
 631735{{
 631736    [ModuleInitializer]
 631737    internal static void Initialize()
 631738    {{
 631739        global::NexusLabs.Needlr.AgentFramework.AgentFrameworkGeneratedBootstrap.Register(
 631740            () => AgentFrameworkFunctionRegistry.AllFunctionTypes,
 631741            () => AgentFrameworkFunctionGroupRegistry.AllGroups,
 631742            () => AgentRegistry.AllAgentTypes,
 631743            () => AgentHandoffTopologyRegistry.AllHandoffs,
 631744            () => AgentGroupChatRegistry.AllGroups,
 631745            () => AgentSequentialTopologyRegistry.AllPipelines);
 631746        global::NexusLabs.Needlr.AgentFramework.AgentFrameworkGeneratedBootstrap.RegisterAIFunctionProvider(
 631747            new global::{safeAssemblyName}.Generated.GeneratedAIFunctionProvider());
 631748    }}
 631749}}
 631750";
 1751    }
 1752
 1753    private static string GeneratePartialCompanionSource(
 1754        NeedlrAiAgentTypeInfo agentType,
 1755        Dictionary<string, List<string>> groupedByName)
 1756    {
 61757        var sb = new StringBuilder();
 61758        sb.AppendLine("// <auto-generated/>");
 61759        sb.AppendLine("#nullable enable");
 61760        sb.AppendLine();
 1761
 61762        if (agentType.NamespaceName is not null)
 1763        {
 61764            sb.AppendLine($"namespace {agentType.NamespaceName};");
 61765            sb.AppendLine();
 1766        }
 1767
 61768        sb.AppendLine($"partial class {agentType.ClassName}");
 61769        sb.AppendLine("{");
 1770
 61771        sb.AppendLine("    /// <summary>The declared name of this agent, equal to the class name.</summary>");
 1772
 61773        var toolsDocLines = BuildToolsDocComment(agentType, groupedByName);
 541774        foreach (var line in toolsDocLines)
 211775            sb.AppendLine(line);
 1776
 61777        sb.AppendLine($"    public static string AgentName => nameof({agentType.ClassName});");
 61778        sb.AppendLine("}");
 1779
 61780        return sb.ToString();
 1781    }
 1782
 1783    private static List<string> BuildToolsDocComment(
 1784        NeedlrAiAgentTypeInfo agentType,
 1785        Dictionary<string, List<string>> groupedByName)
 1786    {
 1787        static string ShortName(string fqn)
 1788        {
 21789            var clean = fqn.StartsWith("global::") ? fqn.Substring(8) : fqn;
 21790            var dot = clean.LastIndexOf('.');
 21791            return dot >= 0 ? clean.Substring(dot + 1) : clean;
 1792        }
 1793
 61794        var lines = new List<string>();
 1795
 1796        // FunctionTypes = new Type[0] — explicitly no tools
 61797        if (agentType.HasExplicitFunctionTypes && agentType.ExplicitFunctionTypeFQNs.IsEmpty
 61798            && agentType.FunctionGroupNames.IsEmpty)
 1799        {
 11800            lines.Add("    /// <remarks>This agent has no tools assigned (declared with an empty <c>FunctionTypes</c>).<
 11801            return lines;
 1802        }
 1803
 1804        // Neither set — uses all registered function types
 51805        if (!agentType.HasExplicitFunctionTypes && agentType.FunctionGroupNames.IsEmpty)
 1806        {
 21807            lines.Add("    /// <remarks>This agent uses all registered function types.</remarks>");
 21808            return lines;
 1809        }
 1810
 1811        // Build the resolved list of function types
 31812        var entries = new List<(string displayName, string? groupSource)>();
 1813
 101814        foreach (var group in agentType.FunctionGroupNames)
 1815        {
 21816            if (groupedByName.TryGetValue(group, out var types))
 1817            {
 41818                foreach (var typeFqn in types)
 11819                    entries.Add((ShortName(typeFqn), group));
 1820            }
 1821            else
 1822            {
 11823                entries.Add(($"(unresolved group \"{group}\")", group));
 1824            }
 1825        }
 1826
 81827        foreach (var typeFqn in agentType.ExplicitFunctionTypeFQNs)
 1828        {
 11829            var shortName = ShortName(typeFqn);
 11830            if (!entries.Any(e => e.displayName == shortName))
 11831                entries.Add((shortName, null));
 1832        }
 1833
 31834        lines.Add("    /// <remarks>");
 31835        lines.Add("    /// <para>Agent tools:</para>");
 31836        lines.Add("    /// <list type=\"bullet\">");
 121837        foreach (var (displayName, groupSource) in entries)
 1838        {
 31839            var source = groupSource is not null ? $" (group <c>\"{groupSource}\"</c>)" : " (explicit type)";
 31840            lines.Add($"    /// <item><term><see cref=\"{displayName}\"/>{source}</term></item>");
 1841        }
 31842        lines.Add("    /// </list>");
 31843        lines.Add("    /// </remarks>");
 1844
 31845        return lines;
 1846    }
 1847
 1848    private readonly struct AgentFunctionTypeInfo
 1849    {
 1850        public AgentFunctionTypeInfo(string typeName, string assemblyName, bool isStatic, ImmutableArray<AgentFunctionMe
 1851        {
 361852            TypeName = typeName; AssemblyName = assemblyName; IsStatic = isStatic; Methods = methods;
 91853        }
 1854
 611855        public string TypeName { get; }
 01856        public string AssemblyName { get; }
 451857        public bool IsStatic { get; }
 181858        public ImmutableArray<AgentFunctionMethodInfo> Methods { get; }
 1859    }
 1860
 1861    private readonly struct AgentFunctionGroupEntry
 1862    {
 1863        public AgentFunctionGroupEntry(string typeName, string groupName)
 1864        {
 101865            TypeName = typeName;
 101866            GroupName = groupName;
 101867        }
 1868
 201869        public string TypeName { get; }
 301870        public string GroupName { get; }
 1871    }
 1872
 1873    private readonly struct NeedlrAiAgentTypeInfo
 1874    {
 1875        public NeedlrAiAgentTypeInfo(
 1876            string typeName,
 1877            string className,
 1878            string? namespaceName,
 1879            bool isPartial,
 1880            ImmutableArray<string> functionGroupNames,
 1881            ImmutableArray<string> explicitFunctionTypeFQNs,
 1882            bool hasExplicitFunctionTypes)
 1883        {
 581884            TypeName = typeName;
 581885            ClassName = className;
 581886            NamespaceName = namespaceName;
 581887            IsPartial = isPartial;
 581888            FunctionGroupNames = functionGroupNames;
 581889            ExplicitFunctionTypeFQNs = explicitFunctionTypeFQNs;
 581890            HasExplicitFunctionTypes = hasExplicitFunctionTypes;
 581891        }
 1892
 2381893        public string TypeName { get; }
 3021894        public string ClassName { get; }
 121895        public string? NamespaceName { get; }
 581896        public bool IsPartial { get; }
 81897        public ImmutableArray<string> FunctionGroupNames { get; }
 51898        public ImmutableArray<string> ExplicitFunctionTypeFQNs { get; }
 111899        public bool HasExplicitFunctionTypes { get; }
 1900    }
 1901
 1902    private readonly struct HandoffEntry
 1903    {
 1904        public HandoffEntry(string initialAgentTypeName, string initialAgentClassName, string targetAgentTypeName, strin
 1905        {
 51906            InitialAgentTypeName = initialAgentTypeName;
 51907            InitialAgentClassName = initialAgentClassName;
 51908            TargetAgentTypeName = targetAgentTypeName;
 51909            HandoffReason = handoffReason;
 51910        }
 1911
 51912        public string InitialAgentTypeName { get; }
 51913        public string InitialAgentClassName { get; }
 51914        public string TargetAgentTypeName { get; }
 51915        public string? HandoffReason { get; }
 1916    }
 1917
 1918    private readonly struct GroupChatEntry
 1919    {
 1920        public GroupChatEntry(string agentTypeName, string groupName)
 1921        {
 81922            AgentTypeName = agentTypeName;
 81923            GroupName = groupName;
 81924        }
 1925
 81926        public string AgentTypeName { get; }
 81927        public string GroupName { get; }
 1928    }
 1929
 1930    private readonly struct SequenceEntry
 1931    {
 1932        public SequenceEntry(string agentTypeName, string pipelineName, int order)
 1933        {
 241934            AgentTypeName = agentTypeName;
 241935            PipelineName = pipelineName;
 241936            Order = order;
 241937        }
 1938
 241939        public string AgentTypeName { get; }
 241940        public string PipelineName { get; }
 241941        public int Order { get; }
 1942    }
 1943
 1944    private readonly struct TerminationConditionEntry
 1945    {
 1946        public TerminationConditionEntry(string agentTypeName, string conditionTypeFQN, ImmutableArray<string> ctorArgLi
 1947        {
 81948            AgentTypeName = agentTypeName;
 81949            ConditionTypeFQN = conditionTypeFQN;
 81950            CtorArgLiterals = ctorArgLiterals;
 81951        }
 1952
 81953        public string AgentTypeName { get; }
 81954        public string ConditionTypeFQN { get; }
 161955        public ImmutableArray<string> CtorArgLiterals { get; }
 1956    }
 1957
 1958    private readonly struct AgentFunctionParameterInfo
 1959    {
 1960        public AgentFunctionParameterInfo(
 1961            string name, string typeFullName,
 1962            string jsonSchemaType, string? itemJsonSchemaType,
 1963            bool isCancellationToken, bool isNullable, bool hasDefault, string? description)
 1964        {
 181965            Name = name; TypeFullName = typeFullName;
 181966            JsonSchemaType = jsonSchemaType; ItemJsonSchemaType = itemJsonSchemaType;
 181967            IsCancellationToken = isCancellationToken; IsNullable = isNullable;
 181968            HasDefault = hasDefault; Description = description;
 91969        }
 1970
 601971        public string Name { get; }
 51972        public string TypeFullName { get; }
 321973        public string JsonSchemaType { get; }
 01974        public string? ItemJsonSchemaType { get; }
 351975        public bool IsCancellationToken { get; }
 81976        public bool IsNullable { get; }
 81977        public bool HasDefault { get; }
 91978        public string? Description { get; }
 81979        public bool IsRequired => !IsCancellationToken && !IsNullable && !HasDefault;
 1980    }
 1981
 1982    private readonly struct AgentFunctionMethodInfo
 1983    {
 1984        public AgentFunctionMethodInfo(
 1985            string methodName, bool isAsync, bool isVoidLike,
 1986            string? returnValueTypeFQN, ImmutableArray<AgentFunctionParameterInfo> parameters,
 1987            string description)
 1988        {
 271989            MethodName = methodName; IsAsync = isAsync; IsVoidLike = isVoidLike;
 271990            ReturnValueTypeFQN = returnValueTypeFQN; Parameters = parameters; Description = description;
 91991        }
 1992
 361993        public string MethodName { get; }
 181994        public bool IsAsync { get; }
 91995        public bool IsVoidLike { get; }
 01996        public string? ReturnValueTypeFQN { get; }
 271997        public ImmutableArray<AgentFunctionParameterInfo> Parameters { get; }
 91998        public string Description { get; }
 1999    }
 2000}

Methods/Properties

Initialize(Microsoft.CodeAnalysis.IncrementalGeneratorInitializationContext)
GetAgentFunctionTypeInfo(Microsoft.CodeAnalysis.GeneratorSyntaxContext,System.Threading.CancellationToken)
TryGetTypeInfo(Microsoft.CodeAnalysis.INamedTypeSymbol)
GetAgentFunctionGroupEntries(Microsoft.CodeAnalysis.GeneratorSyntaxContext,System.Threading.CancellationToken)
GetNeedlrAiAgentTypeInfo(Microsoft.CodeAnalysis.GeneratorAttributeSyntaxContext,System.Threading.CancellationToken)
GetHandoffEntries(Microsoft.CodeAnalysis.GeneratorAttributeSyntaxContext,System.Threading.CancellationToken)
GetGroupChatEntries(Microsoft.CodeAnalysis.GeneratorAttributeSyntaxContext,System.Threading.CancellationToken)
GetSequenceEntries(Microsoft.CodeAnalysis.GeneratorAttributeSyntaxContext,System.Threading.CancellationToken)
GetTerminationConditionEntries(Microsoft.CodeAnalysis.GeneratorAttributeSyntaxContext,System.Threading.CancellationToken)
SerializeTypedConstant(Microsoft.CodeAnalysis.TypedConstant)
GetDescriptionFromAttributes(System.Collections.Immutable.ImmutableArray`1<Microsoft.CodeAnalysis.AttributeData>)
GetJsonSchemaType(Microsoft.CodeAnalysis.ITypeSymbol,System.String&)
IsAccessibleFromGeneratedCode(Microsoft.CodeAnalysis.INamedTypeSymbol)
GetFullyQualifiedName(Microsoft.CodeAnalysis.ITypeSymbol)
SanitizeIdentifier(System.String)
ExecuteAll(System.Collections.Immutable.ImmutableArray`1<System.Nullable`1<NexusLabs.Needlr.AgentFramework.Generators.AgentFrameworkFunctionRegistryGenerator/AgentFunctionTypeInfo>>,System.Collections.Immutable.ImmutableArray`1<System.Collections.Immutable.ImmutableArray`1<NexusLabs.Needlr.AgentFramework.Generators.AgentFrameworkFunctionRegistryGenerator/AgentFunctionGroupEntry>>,System.Collections.Immutable.ImmutableArray`1<System.Nullable`1<NexusLabs.Needlr.AgentFramework.Generators.AgentFrameworkFunctionRegistryGenerator/NeedlrAiAgentTypeInfo>>,System.Collections.Immutable.ImmutableArray`1<System.Collections.Immutable.ImmutableArray`1<NexusLabs.Needlr.AgentFramework.Generators.AgentFrameworkFunctionRegistryGenerator/HandoffEntry>>,System.Collections.Immutable.ImmutableArray`1<System.Collections.Immutable.ImmutableArray`1<NexusLabs.Needlr.AgentFramework.Generators.AgentFrameworkFunctionRegistryGenerator/GroupChatEntry>>,System.Collections.Immutable.ImmutableArray`1<System.Collections.Immutable.ImmutableArray`1<NexusLabs.Needlr.AgentFramework.Generators.AgentFrameworkFunctionRegistryGenerator/SequenceEntry>>,System.Collections.Immutable.ImmutableArray`1<System.Collections.Immutable.ImmutableArray`1<NexusLabs.Needlr.AgentFramework.Generators.AgentFrameworkFunctionRegistryGenerator/TerminationConditionEntry>>,Microsoft.CodeAnalysis.Compilation,Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptionsProvider,Microsoft.CodeAnalysis.SourceProductionContext)
GenerateTopologyGraphSource(System.String,System.String)
GenerateMermaidDiagram(System.Collections.Generic.Dictionary`2<System.ValueTuple`2<System.String,System.String>,System.Collections.Generic.List`1<System.ValueTuple`2<System.String,System.String>>>,System.Collections.Generic.Dictionary`2<System.String,System.Collections.Generic.List`1<System.String>>,System.Collections.Generic.Dictionary`2<System.String,System.Collections.Generic.List`1<System.String>>)
GetShortName(System.String)
SanitizeMermaidId(System.String)
EscapeMermaidLabel(System.String)
GenerateRegistrySource(System.Collections.Generic.List`1<NexusLabs.Needlr.AgentFramework.Generators.AgentFrameworkFunctionRegistryGenerator/AgentFunctionTypeInfo>,System.String)
GenerateGroupRegistrySource(System.Collections.Generic.Dictionary`2<System.String,System.Collections.Generic.List`1<System.String>>,System.String)
GenerateAgentRegistrySource(System.Collections.Generic.List`1<NexusLabs.Needlr.AgentFramework.Generators.AgentFrameworkFunctionRegistryGenerator/NeedlrAiAgentTypeInfo>,System.String)
GenerateHandoffTopologyRegistrySource(System.Collections.Generic.Dictionary`2<System.ValueTuple`2<System.String,System.String>,System.Collections.Generic.List`1<System.ValueTuple`2<System.String,System.String>>>,System.String)
GenerateGroupChatRegistrySource(System.Collections.Generic.Dictionary`2<System.String,System.Collections.Generic.List`1<System.String>>,System.String)
GenerateSequentialTopologyRegistrySource(System.Collections.Generic.Dictionary`2<System.String,System.Collections.Generic.List`1<System.String>>,System.String)
GenerateWorkflowFactoryExtensionsSource(System.Collections.Generic.Dictionary`2<System.ValueTuple`2<System.String,System.String>,System.Collections.Generic.List`1<System.ValueTuple`2<System.String,System.String>>>,System.Collections.Generic.Dictionary`2<System.String,System.Collections.Generic.List`1<System.String>>,System.Collections.Generic.Dictionary`2<System.String,System.Collections.Generic.List`1<System.String>>,System.Collections.Generic.Dictionary`2<System.String,System.Collections.Generic.List`1<NexusLabs.Needlr.AgentFramework.Generators.AgentFrameworkFunctionRegistryGenerator/TerminationConditionEntry>>,System.String)
GenerateAgentFactoryExtensionsSource(System.Collections.Generic.List`1<NexusLabs.Needlr.AgentFramework.Generators.AgentFrameworkFunctionRegistryGenerator/NeedlrAiAgentTypeInfo>,System.String)
GenerateAgentTopologyConstantsSource(System.Collections.Generic.List`1<NexusLabs.Needlr.AgentFramework.Generators.AgentFrameworkFunctionRegistryGenerator/NeedlrAiAgentTypeInfo>,System.Collections.Generic.List`1<NexusLabs.Needlr.AgentFramework.Generators.AgentFrameworkFunctionRegistryGenerator/AgentFunctionGroupEntry>,System.Collections.Generic.Dictionary`2<System.String,System.Collections.Generic.List`1<System.String>>,System.String)
GenerateSyringeExtensionsSource(System.Collections.Generic.List`1<NexusLabs.Needlr.AgentFramework.Generators.AgentFrameworkFunctionRegistryGenerator/AgentFunctionGroupEntry>,System.String)
GenerateAIFunctionProviderSource(System.Collections.Generic.List`1<NexusLabs.Needlr.AgentFramework.Generators.AgentFrameworkFunctionRegistryGenerator/AgentFunctionTypeInfo>,System.String)
AppendAIFunctionNestedClass(System.Text.StringBuilder,NexusLabs.Needlr.AgentFramework.Generators.AgentFrameworkFunctionRegistryGenerator/AgentFunctionTypeInfo,NexusLabs.Needlr.AgentFramework.Generators.AgentFrameworkFunctionRegistryGenerator/AgentFunctionMethodInfo,System.String)
BuildJsonSchema(System.Collections.Immutable.ImmutableArray`1<NexusLabs.Needlr.AgentFramework.Generators.AgentFrameworkFunctionRegistryGenerator/AgentFunctionParameterInfo>)
BuildJsonSchemaTypeEntry(NexusLabs.Needlr.AgentFramework.Generators.AgentFrameworkFunctionRegistryGenerator/AgentFunctionParameterInfo)
AppendParameterExtraction(System.Text.StringBuilder,NexusLabs.Needlr.AgentFramework.Generators.AgentFrameworkFunctionRegistryGenerator/AgentFunctionParameterInfo)
AppendIntegerExtraction(System.Text.StringBuilder,NexusLabs.Needlr.AgentFramework.Generators.AgentFrameworkFunctionRegistryGenerator/AgentFunctionParameterInfo,System.String,System.String)
AppendNumberExtraction(System.Text.StringBuilder,NexusLabs.Needlr.AgentFramework.Generators.AgentFrameworkFunctionRegistryGenerator/AgentFunctionParameterInfo,System.String,System.String)
StripAgentSuffix(System.String)
GroupNameToPascalCase(System.String)
GenerateBootstrapSource(System.String)
GeneratePartialCompanionSource(NexusLabs.Needlr.AgentFramework.Generators.AgentFrameworkFunctionRegistryGenerator/NeedlrAiAgentTypeInfo,System.Collections.Generic.Dictionary`2<System.String,System.Collections.Generic.List`1<System.String>>)
ShortName()
BuildToolsDocComment(NexusLabs.Needlr.AgentFramework.Generators.AgentFrameworkFunctionRegistryGenerator/NeedlrAiAgentTypeInfo,System.Collections.Generic.Dictionary`2<System.String,System.Collections.Generic.List`1<System.String>>)
.ctor(System.String,System.String,System.Boolean,System.Collections.Immutable.ImmutableArray`1<NexusLabs.Needlr.AgentFramework.Generators.AgentFrameworkFunctionRegistryGenerator/AgentFunctionMethodInfo>)
get_TypeName()
get_AssemblyName()
get_IsStatic()
get_Methods()
.ctor(System.String,System.String)
get_TypeName()
get_GroupName()
.ctor(System.String,System.String,System.String,System.Boolean,System.Collections.Immutable.ImmutableArray`1<System.String>,System.Collections.Immutable.ImmutableArray`1<System.String>,System.Boolean)
get_TypeName()
get_ClassName()
get_NamespaceName()
get_IsPartial()
get_FunctionGroupNames()
get_ExplicitFunctionTypeFQNs()
get_HasExplicitFunctionTypes()
.ctor(System.String,System.String,System.String,System.String)
get_InitialAgentTypeName()
get_InitialAgentClassName()
get_TargetAgentTypeName()
get_HandoffReason()
.ctor(System.String,System.String)
get_AgentTypeName()
get_GroupName()
.ctor(System.String,System.String,System.Int32)
get_AgentTypeName()
get_PipelineName()
get_Order()
.ctor(System.String,System.String,System.Collections.Immutable.ImmutableArray`1<System.String>)
get_AgentTypeName()
get_ConditionTypeFQN()
get_CtorArgLiterals()
.ctor(System.String,System.String,System.String,System.String,System.Boolean,System.Boolean,System.Boolean,System.String)
get_Name()
get_TypeFullName()
get_JsonSchemaType()
get_ItemJsonSchemaType()
get_IsCancellationToken()
get_IsNullable()
get_HasDefault()
get_Description()
get_IsRequired()
.ctor(System.String,System.Boolean,System.Boolean,System.String,System.Collections.Immutable.ImmutableArray`1<NexusLabs.Needlr.AgentFramework.Generators.AgentFrameworkFunctionRegistryGenerator/AgentFunctionParameterInfo>,System.String)
get_MethodName()
get_IsAsync()
get_IsVoidLike()
get_ReturnValueTypeFQN()
get_Parameters()
get_Description()