feat: Add AgentGraph support to the AI SDK#292
Conversation
jsonbailey
left a comment
There was a problem hiding this comment.
Reviewed the graph implementation against the shipped js-core and python-server-sdk-ai SDKs and reconciled the differences against the AI SDK spec (sdk-specs, specs/AISDK-ai-sdk/). Security review came back clean (resumption-token decode, at-most-once concurrency, fail-closed validation, graphKey back-compat all verified).
Two spec-compliance items to fix (reverse-traverse no-terminal behavior, validation log level) plus an out-of-scope version-coercion change and a couple of low-severity items inline. Where js-core and python disagreed, the spec sided with js-core on forward-traverse ordering (BFS) and trackPath at-most-once, so .NET is correct on those — no change needed there.
Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 08075f1. Configure here.
jsonbailey
left a comment
There was a problem hiding this comment.
Follow-up on _ldMeta.version handling: it should be carried opaquely — if set, use the value as-is; if absent, default to 1; no other coercion. A few spots still clamp a non-positive value to 1. Details inline.

Summary
Adds first-class support for agent graphs to
LaunchDarkly.ServerSdk.Ai. An agent graph is a LaunchDarkly-managed directed graph whose nodes are existingLdAiAgentConfigs and whose edges describe handoffs between agents. The SDK fetches the graph flag, resolves every referenced agent config, validates connectivity, and hands the caller a typed object they can traverse and instrument — without exposing the underlying flag shape.AgentGraphandCreateGraphTrackeronILdAiGraphClientLdAiClientnow implements bothILdAiClientand the newILdAiGraphClient. The existingILdAiClientinterface is unchanged — no members added, removed, or modified. Graph functionality lives entirely on the separateILdAiGraphClientinterface, making this a purely additive change.AgentGraphfires a$ld:ai:usage:agent-graphusage event, evaluates the graph flag, fetches every childLdAiAgentConfig, runs connectivity validation, and returns anAgentGraphDefinition. The returned definition'sEnabledproperty reflects the result of all validation checks combined:_ldMeta.enabled, root present, every node reachable from the root, and every child agent config fetchable and enabled. When any check fails, the SDK logs a warning and returns a disabled definition whose traversal/inspection methods are safe no-ops; onlyGetConfig()(raw flag value +_ldMeta) andCreateTracker()remain meaningful.CreateGraphTrackerreconstructs anAiGraphTrackerfrom a base64url resumption token, enabling cross-process continuation (e.g. emitting graph-level events from a worker that didn't run the original evaluation).AgentGraphDefinitionTraversedoes a breadth-first walk from the root;ReverseTraversewalks from terminals back toward the root (the root is always processed last unless it is the only node). Both are cycle-safe — each node is visited at most once. The callback receives the current node and an accumulator dictionary; whatever the callback returns is stored in the accumulator under that node's key and is visible to subsequent visits.Node and edge types
AgentGraphNode— wraps anLdAiAgentConfigplus its outgoingGraphEdges and exposesIsTerminal(true when there are no outgoing edges). Per-node metrics are recorded via the wrapped config's existingCreateTracker().GraphEdge—recordcarrying the targetKeyand an optionalHandoffdictionary (IReadOnlyDictionary<string, LdValue>). Handoff maps are wrapped inReadOnlyDictionaryto enforce immutability; edge lists are frozen viaAsReadOnly().AgentGraphFlagValue/LdMeta— the parsed raw flag value, including_ldMeta. Always non-null on the returned definition, even when the graph is disabled.AiGraphTrackerGraph-scoped tracker emitting
$ld:ai:graph:*events. At-most-once methods useInterlocked.CompareExchangeso the contract holds under racing callers — a second call logs a warning and is silently dropped.TrackTotalTokensshort-circuits before claiming the slot when usage is empty, consistent withLdAiConfigTracker.TrackTokens. Multi-fire methods may be called once per edge traversal. Every event carries the standardrunId+graphKey+versiontrack data (plusvariationKeywhen non-empty).ResumptionTokenis a base64url-encoded JSON payload of those fields;FromResumptionTokenvalidates and round-trips it, throwingArgumentExceptionon malformed or missing-required-fields input.graphKeypropagation throughLdAiConfigTrackerWhen a per-node tracker is created via
AgentGraph(rather than via the standaloneAgentConfigpath), the parent graph's key is threaded through toLdAiConfigTrackerso every per-node event includesgraphKeyin its track data and resumption token. The wire format omits empty optional fields, so existing non-graph trackers continue to round-trip exactly.Other touched files
ConfigFactory.BuildAgentConfigandTrackerFactoryForaccept an optionalgraphKeyparameter (defaultnull). The default-path helperBuildAgentFromDefaultis nowinternal(wasprivate) so the graph code path can reuse it. All existing call sites are unchanged.Polyfills/IsExternalInit.csenablesinitaccessors and positional records on thenet462/netstandard2.0targets used by the new graph types.Migration
None required.
ILdAiClientis unchanged — no new members were added. Graph functionality is on the newILdAiGraphClientinterface, whichLdAiClientimplements alongsideILdAiClient. Existing consumers, including hand-rolled test doubles that implementILdAiClient, will continue to compile and work without modification. The wire format is backward-compatible (the newgraphKeyfield on config resumption tokens is omitted when empty).Test plan
dotnet buildsucceeds acrossnetstandard2.0,net462,net6.0,net8.0dotnet test pkgs/sdk/server-ai/test/LaunchDarkly.ServerSdk.Ai.Tests.csproj --framework net8.0passesAgentGraphDefinitionTestcovers node lookup, parent/child/terminal queries, BFS and reverse-BFS traversal, cycle safety, and disabled-graph no-op behaviorAiGraphTrackerTestcovers track-data shape, at-most-once vs. multi-fire semantics,TrackTotalTokensempty-usage short-circuit,ResumptionTokenround-trip,FromResumptionTokenerror handling, and theSummarysnapshotLdAiAgentGraphConfigTestcoversgraphKeypropagation into per-node track data and resumption tokensLdAiClientAgentGraphTestcovers end-to-endAgentGraphretrieval and every validation path: disabled_ldMeta, missing root, unreachable node, and unfetchable/disabled child agent config — plus the$ld:ai:usage:agent-graphusage eventNote
Medium Risk
Large additive public API and shared telemetry/resumption-token contracts must stay aligned with other AI SDKs; optional ILogger.Debug is a compile-time break only for custom ILogger implementations outside the package.
Overview
Adds agent graph support to the server AI SDK:
LdAiClientnow also implementsILdAiGraphClientwithAgentGraphandCreateGraphTracker, whileILdAiClientstays unchanged.AgentGraphevaluates a graph flag, parsesroot/edges/_ldMeta, validates connectivity and that every referenced agent config is enabled, then returns anAgentGraphDefinition(or a disabled shell with empty nodes when validation fails). The definition exposes node lookup, BFSTraverse/ terminal-upReverseTraverse, andCreateTrackerfor graph-scoped metrics.New types include
AgentGraphNode,GraphEdge(optional handoff payload),AgentGraphFlagValue,AiGraphTracker($ld:ai:graph:*events, at-most-once vs multi-fire handoff/redirect), andAiGraphMetricSummary. Graph node fetches thread an optionalgraphKeyintoLdAiConfigTrackertrack data and resumption tokens when absent from standaloneAgentConfigcalls.Supporting tweaks:
ILogger.Debug(+ adapter), optionalgraphKeyonConfigFactory.BuildAgentConfig, and anIsExternalInitpolyfill for older TFMs. Broad xUnit coverage for definition, tracker, client validation paths, andgraphKeypropagation.Reviewed by Cursor Bugbot for commit d64294c. Bugbot is set up for automated code reviews on this repo. Configure here.