The middleware reads state["ag-ui"]["inject_a2ui_tool"] and
state["ag-ui"]["a2ui_schema"], but "ag-ui" was never declared on the
StateSchema, so create_agent's StateGraph dropped both keys before the
middleware ran — the generate_a2ui tool never injected and the catalog was
lost. Declare the "ag-ui" channel via StateSchema.__annotations__ so the flag
and catalog survive.
Also add an a2ui_params kwarg so a host can steer the auto-injected
generate_a2ui subagent (design/generation guidelines, catalog id, ...). The
middleware still injects the bound model and folds the registered catalog in,
but host-set values win.
ag-ui-langgraph's tool factory now takes one A2UIToolParams object (model
inside) and folds composition_guide into the guidelines bag.
- sdk-js middleware: getA2UITools(params) + type A2UIToolParams import;
pin @ag-ui/langgraph 0.0.40 (the JS release carrying the new API).
- sdk-python middleware: get_a2ui_tools(params) + guarded A2UIToolParams
import; pin ag-ui-langgraph >=0.0.41 (the py release with the new API).
- Update the a2ui injection test skip-reason wording.
The A2UI middleware (@ag-ui/a2ui-middleware) forwards injectA2UITool on
forwardedProps; ag-ui-langgraph surfaces it into agent state at
state["ag-ui"]["inject_a2ui_tool"]. The CopilotKit LangGraph middleware
(py + js) now reads that flag and only injects generate_a2ui when it is
truthy (opt-in), drops the runtime's render_a2ui so the model sees one
A2UI tool, and skips if the agent already defines generate_a2ui. The
catalog only binds surfaces; it is no longer the gate.
Reverts the earlier runtime-forward + context-channel approach.
Deps: ag-ui-langgraph>=0.0.38 (py), @ag-ui/langgraph 0.0.37 (sdk-js),
@ag-ui/a2ui-middleware 0.0.6 + @ag-ui/langgraph 0.0.37 (runtime);
.npmrc min-release-age exclude for @ag-ui/a2ui-middleware. Showcase
langgraph pins bumped to copilotkit==0.1.94a3 / sdk-js 1.59.3-alpha.3 /
@ag-ui/langgraph 0.0.37.
The auto-injected generate_a2ui tool now resolves the A2UI catalog from
both delivery paths: the CopilotKit runtime proxy (a copilotkit.context
entry) and the AG-UI native endpoint (state["ag-ui"].a2ui_schema). The
registered catalog id is extracted and bound to generated surfaces so
BYOC custom catalogs render their own components instead of the basic
catalog. Covers @copilotkit/sdk-js and the copilotkit Python SDK.
Prebuilt agents get dynamic A2UI with no extra wiring — adding the
middleware is enough. When the frontend registers an A2UI catalog
(surfaced by the runtime into state["ag-ui"].a2ui_schema), the
middleware infers the agent's own model, advertises the generate_a2ui
tool in the model-call hook, and executes it in the tool-call hook.
No catalog → the tool is never advertised.
Covers both @copilotkit/sdk-js and the copilotkit Python SDK. Bumps
the A2UI tool-factory dependency to where get_a2ui_tools ships
(@ag-ui/langgraph 0.0.35, ag-ui-langgraph >=0.0.37).
langgraph-api auto-copies the entire config.configurable dict into runtime.context. That dict
carries the CopilotKit-internal transport key `copilotkit_forwarded_headers`, populated solely
to drive the httpx header-forwarding hook (it is not user-visible state). The middleware's
before_agent step then rendered runtime.context into the LLM prompt as an "App Context" system
message, and the expose_state path could surface the same key via the state note — either path
leaks transport headers into the prompt body and, under strict fixture matching (D6/aimock),
changes the request payload and breaks the match.
Fix: hard-exclude `copilotkit_forwarded_headers` from both render paths. The App Context
renderer strips the reserved key before serialization, and the expose_state allowlist applies
the same exclusion so an explicit allow cannot reintroduce the leak. The httpx hook is
unchanged, so the key still reaches aimock as an HTTP header — pure conveyance, no body
pollution.
Adds red-green unit coverage for both paths (App Context strip, expose_state default + allowlist).
Extract incoming x-* headers from LangGraph's runtime config and republish
them via the forwarded-headers ContextVar so the httpx hook can attach
them to outbound provider requests. Apply documented precedence
(context > configurable, wrapper-dict > raw x-*) by processing sources
in order with first-write-wins and lowercasing keys at insertion so
mixed-case headers do not silently overwrite each other downstream.
Always clear the ContextVar on early exits so stale headers from a prior
request never leak into the next: explicit set_forwarded_headers({}) on
the RuntimeError no-active-runnable path and on the generic exception
fallback. The happy path already overwrites the ContextVar
unconditionally, even with an empty dict.
The change also installs the httpx event hook once per chat-model client
via a module-level set keyed by id(client), so models reused across
requests pick up fresh per-request headers without re-hooking.
The if (agent.headers) guard in configureAgentForRequest silently
skipped header forwarding when agent.headers was undefined (the
default for LangGraphAgent). This meant x-aimock-context, x-test-id,
and other x-* headers were never forwarded to agent backends.
Also wires install_httpx_hook in the Python SDK middleware so
forwarded headers propagate to outgoing LLM API calls.
Closes the gap documented in PR #4773 spec as out-of-scope.
Add copilotkit.a2ui module with operation builders and prompt helpers,
fix SystemMessage ID reuse in before_agent to prevent LangGraph duplication,
add Pydantic model_dump serialization for app_context.
The re module was used in _fix_messages_for_bedrock (re.compile) but
never imported, causing NameError at runtime when the middleware is
invoked. Bumps version to 0.1.83.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add _fix_messages_for_bedrock() to handle three issues caused by
after_agent restoring frontend tool_calls to the checkpoint:
1. Strip unanswered tool_calls without matching ToolMessages (Bedrock
rejects toolUse without a corresponding toolResult)
2. Sync msg.content tool_use blocks with msg.tool_calls
3. Fix tool_use/tool_call blocks with string input/args (must be dict)