Commit Graph

5 Commits

Author SHA1 Message Date
Jordan Ritter bd77954ab5 feat(showcase): instrument per-framework x-aimock-context forwarding with gated CVDIAG breadcrumb
Add CVDIAG logging + x-diag-hops breadcrumb (route-<fw>/backend-<fw>) at each forwarding hop across LangGraph (py/ts/fastapi), google-adk, the self-contained Node + Python shims, spring-ai (Java) and ms-agent (.NET). Breadcrumb append is gated on diagnostic-header presence so non-diagnostic traffic stays byte-identical; surfaces previously-silent forwarding misses (empty configurable, missing httpx event-hooks target, swallowed hook-install errors).
2026-06-06 10:40:25 -07:00
Jordan Ritter 4f66ba23ed fix(showcase/ms-agent-harness-dotnet): keep aimock header context for streaming response tail
ASP.NET Core hands control back to the middleware once the endpoint handler
completes via `await _next(context)`, but for streaming endpoints (AG-UI uses
`IAsyncEnumerable`/SSE) the response delegate continues writing to the response
body — and may issue downstream OpenAI calls — AFTER `_next` returns. The
previous `finally` clause reset `AimockHeaderContext` to an empty dictionary at
that point, which caused `AimockHeaderPolicy` to silently drop aimock headers
on those streaming-tail calls. That defeated the entire purpose of the
middleware for the exact codepath it was built to serve.

The `finally`-clear was also unnecessary for isolation: `AsyncLocal<T>` is
scoped to the current execution context, so each ASP.NET request gets its own
slot automatically. There is no cross-request leakage to defend against.

Remove the `try`/`finally` and just call `AimockHeaderContext.Set(headers)`
followed by `await _next(context)`. Add a comment documenting why no reset is
performed.
2026-05-26 13:36:38 -07:00
Jordan Ritter 44733961ad fix(showcase/ms-agent-harness-dotnet): log silent header drops in aimock middleware
When iterating IHeaderDictionary's underlying store yields case-variant
duplicates (e.g., a misbehaving proxy injecting both `X-Foo` and `x-foo`),
the previous GroupBy + ToDictionary path silently kept only the first value
and discarded the rest. For aimock context routing — where header semantics
drive fixture selection — silent drops are a debugging nightmare.

We still keep `.First()` because HTTP defines no canonical merge for
case-variant collisions across distinct keys (the comma-join rule only
applies when keys are ASCII-equal). Instead, when a group has more than
one entry, we emit a structured warning naming the key, the kept value,
and the number of dropped variants, so operators can spot the upstream
misbehavior in logs.

Constructor now takes `ILogger<AimockHeaderMiddleware>` — ASP.NET's
middleware activator injects it automatically via `UseMiddleware<T>()`.
2026-05-26 13:36:38 -07:00
Jordan Ritter 514f630904 fix(showcase/ms-agent-harness-dotnet): unify aimock header casing and dedupe-safety
The header propagation chain (middleware -> context -> policy) had two bugs
that combined to threaten D5/D6 header-forwarding:

1. AimockHeaderMiddleware.ToDictionary used the default ORDINAL case-sensitive
   comparer. ASP.NET's IHeaderDictionary is case-insensitive, but iterating
   the underlying store can yield case-variant duplicates (e.g., a misbehaving
   proxy injecting both `X-Foo` and `x-foo`). Default-comparer ToDictionary
   throws ArgumentException on duplicates and fails the request.

2. AimockHeaderContext.Set lowercased all keys via ToLowerInvariant.
   AimockHeaderMiddleware captured original case; the context then mutated
   the casing; AimockHeaderPolicy.TryGetValue then matched against whatever
   case the OpenAI SDK happened to use. This is inconsistent and aimock
   fixture matching can be case-sensitive depending on configuration.

Canonical strategy: preserve original header casing as captured by the
middleware, but compare case-insensitively throughout via
StringComparer.OrdinalIgnoreCase. The middleware now also groups variants
defensively so duplicate keys cannot blow up the dictionary build.
AimockHeaderPolicy's add-if-absent TryGetValue then works correctly under
case-insensitive comparison without any further changes.
2026-05-26 13:36:38 -07:00
Alem Tuzlak 2405a46fa6 feat(showcase): add ms agent harness dotnet chat 2026-05-26 13:36:38 -07:00