* Add foundry-hosted-agent-copilotkit skill (development-focused) Reworked from PR #2090 feedback: drops all scaffolding/sample-template content and focuses on ongoing development with CopilotKit + AG-UI + Azure AI Foundry hosted agents — adding/gating tools, human-in-the-loop approvals, generative UI and shared state, event-stream debugging, pre-1.0 dependency upgrades, and the hosted-agent deploy loop. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Add HITL code examples to foundry-hosted-agent-copilotkit skill Address review feedback: the .NET approval paragraph was prose-only. Add a .NET snippet from the official Step04_HumanInLoop sample and correct the type/behavior (ToolApprovalRequestContent; convert—not remove—the request_approval call/result). Also add live-verified Python snippets for hosted-agent approval-forwarding (#6652) and the previous_response_id #6851 guard. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
6.8 KiB
Architecture: where AG-UI is produced
The stack has three layers that must agree: CopilotKit (React hooks + runtime), the AG-UI protocol (SSE event stream), and the Microsoft Agent Framework (MAF) agent, optionally running as an Azure AI Foundry hosted agent. The deployed Foundry hosted agent endpoint speaks OpenAI Responses ({project_endpoint}/agents/{name}/endpoint/protocols/openai/responses) or a raw invocations protocol (.../protocols/invocations) — not AG-UI. Pointing HttpAgent from @ag-ui/client directly at a hosted agent's Responses endpoint does not work.
There are three viable wirings. Identify which one the codebase uses before changing anything.
Architecture A — in-process AG-UI endpoint (agent runs inside your service)
The agent object lives in the same process as the AG-UI HTTP endpoint.
Python:
from agent_framework import Agent
from agent_framework_ag_ui import AgentFrameworkAgent, add_agent_framework_fastapi_endpoint
from fastapi import FastAPI
agent = Agent(name="assistant", instructions="...", client=chat_client, tools=[...])
wrapped = AgentFrameworkAgent(agent=agent, require_confirmation=True) # HITL on
app = FastAPI()
add_agent_framework_fastapi_endpoint(app, wrapped, "/")
.NET: builder.Services.AddAGUI() + app.MapAGUI("/", agent) from Microsoft.Agents.AI.Hosting.AGUI.AspNetCore, with approval middleware (see hitl.md).
- All 7 AG-UI patterns (including state snapshots/deltas) work natively — the adapter sees the agent's internal events.
- The model client can still be a Foundry model deployment; "in-process" refers to where the agent loop runs, not the model.
- This is what the CopilotKit CLI and MAF samples produce, and the right choice when you don't need platform-managed conversations, per-user isolation, or Foundry-managed compute.
Architecture B — hosted agent serves AG-UI itself (invocations protocol)
The hosted agent's own container speaks AG-UI, deployed under Foundry's invocations protocol ("Custom streaming protocol (AG-UI, etc.) → Invocations" per the Foundry hosted-agents docs). The agent.yaml declares:
protocols:
- protocol: invocations
version: 2.0.0
and the container serves AG-UI requests at /invocations (Microsoft's foundry-samples repository has a bring-your-own invocations AG-UI sample under samples/python/hosted-agents/bring-your-own/invocations/ag-ui/). The CopilotKit runtime's HttpAgent points at the deployed invocations endpoint.
- AG-UI features behave like Architecture A because the adapter still wraps the in-process agent — it just runs inside Foundry-managed compute.
- You give up the Responses protocol's platform-managed conversation history; conversation state is yours to manage.
- Calls to the deployed endpoint need Entra auth (
DefaultAzureCredential), so the CopilotKit runtime usually still needs a thin server-side proxy to attach tokens — browsers cannot call it directly.
Architecture C — translation bridge to a Responses-protocol hosted agent
The hosted agent is deployed with the responses protocol (platform-managed conversation history, agent versioning, per-user isolation), and a separate bridge service translates between AG-UI and the Responses stream. This is the highest-effort wiring; choose it only when you specifically need the Responses platform features.
The bridge must handle, at minimum:
- Stream translation: OpenAI Responses SSE events → AG-UI events (
response.output_text.delta→TEXT_MESSAGE_CONTENT, function call items →TOOL_CALL_*,response.completed→RUN_FINISHED, etc.). - Turn derivation, not history replay: derive each turn's input from the latest user message (or an approval decision). Replaying the full raw AG-UI message history to the Responses endpoint fails with 400 errors about orphaned tool calls.
- HITL forwarding: surface the hosted agent's
mcp_approval_requestto the UI, and forward the user's decision back as anmcp_approval_responseinput item — approved tools then re-execute server-side. The stock AG-UI adapter resolves approvals locally and never forwards them to a remote agent (tracked as microsoft/agent-framework#6652), so a bridge needs explicit code for this path. Verify against the current package version whether this is still required before writing custom routing. - Conversation continuity: either
previous_response_idchaining (local/direct mode) or a Foundryconversationobject (deployed/platform mode). See hitl.md for the critical hazard withprevious_response_idchaining across approval turns. - State synthesis caveat:
STATE_SNAPSHOT/STATE_DELTAevents are NOT produced by a Responses stream. Shared-state and predictive-state patterns require the bridge to synthesize them (e.g. fromresponse.function_call_arguments.delta); if the bridge doesn't implement that, those patterns silently don't work. Check before promising the feature.
Bridge state (response-id or conversation cache) is typically in-memory: run a single replica or externalize the cache before scaling out.
Local development modes
- Architecture A/B: run the FastAPI/ASP.NET service directly; point the CopilotKit runtime's
HttpAgentathttp://localhost:<port>/. - Hosted agents (B/C):
azd ai agent runruns the REAL hosted agent locally (default port 8088) using youraz logincredentials and the provisioned Foundry project — there is no mock.azd ai agent invoke --local "..."sends a single test payload. A bridge in local mode points at the bare local endpoint instead of the deployed one (commonly switched by a single environment variable holding the direct URL).
CopilotKit runtime wiring (all architectures)
The AG-UI endpoint, wherever it lives, registers in the CopilotKit runtime as an HttpAgent:
import { HttpAgent } from "@ag-ui/client";
import { CopilotRuntime } from "@copilotkit/runtime";
const runtime = new CopilotRuntime({
agents: { "my_agent": new HttpAgent({ url: process.env.AGUI_BACKEND_URL! }) },
});
and the provider selects it by name: <CopilotKit runtimeUrl="/api/copilotkit" agent="my_agent">. Name drift between the agents key, the agent prop, and (for hosted agents) the name in agent.yaml is a recurring failure — keep one constant.
Frontend tools registered with useFrontendTool flow through the runtime into the AG-UI RunAgentInput.tools array and become callable by the agent; this is native in all three architectures.
Auth to Foundry endpoints
- Token audience is
https://ai.azure.com/.default— the defaultcognitiveservices.azure.comscope yields 401 "audience is incorrect". - Keyless (Entra /
DefaultAzureCredential) is the norm; the async Python credential path needsaiohttpinstalled. - Never send
x-ms-user-isolation-keyto a deployed agent — deployed agents derive isolation from the Entra identity and reject the header with a 400.