diff --git a/agents/foundry-hosted-agent-copilotkit.agent.md b/agents/foundry-hosted-agent-copilotkit.agent.md new file mode 100644 index 000000000..1af3bf784 --- /dev/null +++ b/agents/foundry-hosted-agent-copilotkit.agent.md @@ -0,0 +1,58 @@ +--- +description: 'Builds a complete agentic web app on the Azure AI Foundry hosted-agent + AG-UI + CopilotKit stack — a Next.js/CopilotKit v2 UI over a light FastAPI/AG-UI bridge forwarding to ONE Microsoft Agent Framework agent hosted in Foundry, with native human-in-the-loop approval on consequential tools. Requires an Azure AI Foundry project (paid).' +model: 'gpt-5' +tools: ['codebase', 'terminalCommand'] +name: 'Forgewright App Builder' +--- + +You are **Forgewright**, an expert builder of agentic web apps on the **Azure AI +Foundry hosted-agent + AG-UI + CopilotKit** stack. From a single prompt ("build me an +assistant that can … with approval before …") you produce a complete, runnable, +verified app — you do the work, you do not hand the user manual steps. + +Always drive the build through the **`foundry-hosted-agent-copilotkit` skill**: read +its `SKILL.md` and `references/` in full before acting, and follow its rules, +anti-patterns, and Definition of Done exactly. + +## Architecture you build to (non-negotiable) + +- ALL intelligence — `FoundryChatClient` (Responses), every `@tool`, HITL, and history + — runs in ONE **Foundry HOSTED agent** (`build_hosted_agent()`). +- A **light bridge** (Container App, no LLM/tools) speaks AG-UI to the UI, forwards + each turn to the hosted agent, translates Responses → AG-UI, and forwards + `mcp_approval_response` on HITL approve so the gated tool re-executes server-side. +- **CopilotKit v2** hooks are the UI layer only: `useAgent`, `useFrontendTool`, + `useRenderTool`, `useHumanInTheLoop`. + +## Your workflow + +1. **Scaffold** the canonical template into a new runnable app (never start from a + blank repo). +2. **Customize only the marked extension points**: agent instructions + tools (≥1 read + tool, ≥1 `@tool(approval_mode="always_require")` consequential tool) and the + CopilotKit components. Map "needs approval before X" to the gated tool. +3. **Leave the load-bearing parts unchanged**: the `HostedProxyAgent` bridge wiring, + `build_hosted_agent()` with `FoundryChatClient`, the catch-all CopilotKit route, and + the `{ accepted, steps }` HITL contract. +4. **Prove it**: run the structural check and the smoke E2E (the bridge against the + REAL agent run locally via `azd ai agent run`). Both MUST pass. For the deployed + path, require a live browser E2E of HITL approve **and** reject. + +## Guidelines + +- **Never declare success on an unverified build.** `azd` reporting SUCCESS, a dev + server starting, or one chat reply is NOT proof. Done = structural + smoke green, + plus a live browser E2E for server-side patterns in scope. +- Use `FoundryChatClient` for the hosted agent — the Responses `OpenAIChatClient` + 500s on hosted approve-resume. +- Resolve HITL with `{ accepted, steps }`, never `{ approved }`. +- Set `useSingleEndpoint={false}` and use the catch-all `[[...slug]]` CopilotKit route. +- A consequential tool without `approval_mode="always_require"` is a bug — it has no + HITL gate. +- Use **MCR** base images in every Dockerfile (Docker Hub pulls rate-limit on ACR). +- Never commit secrets, endpoints, or app-specific hard-coding. +- This stack requires a paid **Azure AI Foundry** project, `az login`, and the `azd` + Foundry extension — state this prerequisite up front; there is no fully-offline path. +- When a framework limitation blocks you, consult the + [microsoft/agent-framework](https://github.com/microsoft/agent-framework) repo and + its open issues before writing a workaround. diff --git a/docs/README.agents.md b/docs/README.agents.md index 147e24b7f..f285ae5c1 100644 --- a/docs/README.agents.md +++ b/docs/README.agents.md @@ -99,6 +99,7 @@ See [CONTRIBUTING.md](../CONTRIBUTING.md#adding-agents) for guidelines on how to | [Expert React Frontend Engineer](../agents/expert-react-frontend-engineer.agent.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/agent?url=vscode%3Achat-agent%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fagents%2Fexpert-react-frontend-engineer.agent.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/agent?url=vscode-insiders%3Achat-agent%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fagents%2Fexpert-react-frontend-engineer.agent.md) | Expert React 19.2 frontend engineer specializing in modern hooks, Server Components, Actions, TypeScript, and performance optimization | | | [Expert Vue.js Frontend Engineer](../agents/vuejs-expert.agent.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/agent?url=vscode%3Achat-agent%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fagents%2Fvuejs-expert.agent.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/agent?url=vscode-insiders%3Achat-agent%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fagents%2Fvuejs-expert.agent.md) | Expert Vue.js frontend engineer specializing in Vue 3 Composition API, reactivity, state management, testing, and performance with TypeScript | | | [Fedora Linux Expert](../agents/fedora-linux-expert.agent.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/agent?url=vscode%3Achat-agent%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fagents%2Ffedora-linux-expert.agent.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/agent?url=vscode-insiders%3Achat-agent%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fagents%2Ffedora-linux-expert.agent.md) | Fedora (Red Hat family) Linux specialist focused on dnf, SELinux, and modern systemd-based workflows. | | +| [Foundry Hosted Agent Copilotkit](../agents/foundry-hosted-agent-copilotkit.agent.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/agent?url=vscode%3Achat-agent%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fagents%2Ffoundry-hosted-agent-copilotkit.agent.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/agent?url=vscode-insiders%3Achat-agent%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fagents%2Ffoundry-hosted-agent-copilotkit.agent.md) | Builds a complete agentic web app on the Azure AI Foundry hosted-agent + AG-UI + CopilotKit stack — a Next.js/CopilotKit v2 UI over a light FastAPI/AG-UI bridge forwarding to ONE Microsoft Agent Framework agent hosted in Foundry, with native human-in-the-loop approval on consequential tools. Requires an Azure AI Foundry project. | | | [Frontend Performance Investigator](../agents/frontend-performance-investigator.agent.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/agent?url=vscode%3Achat-agent%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fagents%2Ffrontend-performance-investigator.agent.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/agent?url=vscode-insiders%3Achat-agent%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fagents%2Ffrontend-performance-investigator.agent.md) | Runtime web-performance specialist for diagnosing Core Web Vitals, Lighthouse regressions, layout shifts, long tasks, and slow network paths with Chrome DevTools MCP. | | | [Gem Browser Tester](../agents/gem-browser-tester.agent.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/agent?url=vscode%3Achat-agent%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fagents%2Fgem-browser-tester.agent.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/agent?url=vscode-insiders%3Achat-agent%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fagents%2Fgem-browser-tester.agent.md) | E2E browser testing, UI/UX validation, visual regression. | | | [Gem Code Simplifier](../agents/gem-code-simplifier.agent.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/agent?url=vscode%3Achat-agent%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fagents%2Fgem-code-simplifier.agent.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/agent?url=vscode-insiders%3Achat-agent%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fagents%2Fgem-code-simplifier.agent.md) | Refactoring specialist — removes dead code, reduces complexity, consolidates duplicates. | | diff --git a/docs/README.skills.md b/docs/README.skills.md index 789e7eca7..515c24bd1 100644 --- a/docs/README.skills.md +++ b/docs/README.skills.md @@ -174,6 +174,7 @@ See [CONTRIBUTING.md](../CONTRIBUTING.md#adding-skills) for guidelines on how to | [fluentui-blazor](../skills/fluentui-blazor/SKILL.md)
`gh skills install github/awesome-copilot fluentui-blazor` | Guide for using the Microsoft Fluent UI Blazor component library (Microsoft.FluentUI.AspNetCore.Components NuGet package) in Blazor applications. Use this when the user is building a Blazor app with Fluent UI components, setting up the library, using FluentUI components like FluentButton, FluentDataGrid, FluentDialog, FluentToast, FluentNavMenu, FluentTextField, FluentSelect, FluentAutocomplete, FluentDesignTheme, or any component prefixed with "Fluent". Also use when troubleshooting missing providers, JS interop issues, or theming. | `references/DATAGRID.md`
`references/LAYOUT-AND-NAVIGATION.md`
`references/SETUP.md`
`references/THEMING.md` | | [folder-structure-blueprint-generator](../skills/folder-structure-blueprint-generator/SKILL.md)
`gh skills install github/awesome-copilot folder-structure-blueprint-generator` | Comprehensive technology-agnostic prompt for analyzing and documenting project folder structures. Auto-detects project types (.NET, Java, React, Angular, Python, Node.js, Flutter), generates detailed blueprints with visualization options, naming conventions, file placement patterns, and extension templates for maintaining consistent code organization across diverse technology stacks. | None | | [foundry-agent-sync](../skills/foundry-agent-sync/SKILL.md)
`gh skills install github/awesome-copilot foundry-agent-sync` | Create and synchronize prompt-based AI agents directly within Azure AI Foundry via REST API, from a local JSON manifest. Unlike scaffolding skills that only generate local code, this skill registers agents in the Foundry service itself — making them immediately available for invocation. Use when the user asks to create agents in Foundry, sync, deploy, register, or push agents to Foundry, update agent instructions, or scaffold the manifest and sync script for a new repository. Triggers: 'create agent in foundry', 'sync foundry agents', 'deploy agents to foundry', 'register agents in foundry', 'push agents', 'create foundry agent manifest', 'scaffold agent sync'. | None | +| [foundry-hosted-agent-copilotkit](../skills/foundry-hosted-agent-copilotkit/SKILL.md)
`gh skills install github/awesome-copilot foundry-hosted-agent-copilotkit` | Build a complete agentic web app on the Azure AI Foundry hosted-agent + AG-UI + CopilotKit stack: a Next.js/CopilotKit v2 chat UI over a light FastAPI/AG-UI bridge that forwards every turn to ONE Microsoft Agent Framework agent hosted in Azure AI Foundry, with native human-in-the-loop approval on consequential tools. Requires an Azure AI Foundry project (paid). Triggers: agentic app, CopilotKit app, AG-UI bridge, Foundry hosted agent, Microsoft Agent Framework, human-in-the-loop/HITL approval, approval_mode always_require, confirm_changes. Also for fixing the known traps: HITL approve-resume 400 'No tool output found', confirm_changes mis-wired, AG-UI snapshot cards vanishing, CopilotKit catch-all route 404/422, useSingleEndpoint, keyless Foundry 401 audience, Docker Hub rate-limit on ACR build. | `references/architecture.md`
`references/hosted-deploy.md`
`references/patterns-7.md`
`references/troubleshooting.md` | | [freecad-scripts](../skills/freecad-scripts/SKILL.md)
`gh skills install github/awesome-copilot freecad-scripts` | Expert skill for writing FreeCAD Python scripts, macros, and automation. Use when asked to create FreeCAD models, parametric objects, Part/Mesh/Sketcher scripts, workbench tools, GUI dialogs with PySide, Coin3D scenegraph manipulation, or any FreeCAD Python API task. Covers FreeCAD scripting basics, geometry creation, FeaturePython objects, interface tools, and macro development. | `references/geometry-and-shapes.md`
`references/gui-and-interface.md`
`references/parametric-objects.md`
`references/scripting-fundamentals.md`
`references/workbenches-and-advanced.md` | | [from-the-other-side-anitta](../skills/from-the-other-side-anitta/SKILL.md)
`gh skills install github/awesome-copilot from-the-other-side-anitta` | Rigorous challenge profile for Anitta: assumption checks, evidence calibration, and defensible reasoning patterns for Ember collaboration. | None | | [from-the-other-side-quinn](../skills/from-the-other-side-quinn/SKILL.md)
`gh skills install github/awesome-copilot from-the-other-side-quinn` | Collaboration profile for Quinn: curious, energetic, and implementation-focused partnership patterns for Ember sessions with Alison. | None | diff --git a/skills/foundry-hosted-agent-copilotkit/SKILL.md b/skills/foundry-hosted-agent-copilotkit/SKILL.md new file mode 100644 index 000000000..baff17059 --- /dev/null +++ b/skills/foundry-hosted-agent-copilotkit/SKILL.md @@ -0,0 +1,209 @@ +--- +name: foundry-hosted-agent-copilotkit +description: "Build a complete agentic web app on the Azure AI Foundry hosted-agent + AG-UI + CopilotKit stack: a Next.js/CopilotKit v2 chat UI over a light FastAPI/AG-UI bridge that forwards every turn to ONE Microsoft Agent Framework agent hosted in Azure AI Foundry, with native human-in-the-loop approval on consequential tools. Requires an Azure AI Foundry project (paid). Triggers: agentic app, CopilotKit app, AG-UI bridge, Foundry hosted agent, Microsoft Agent Framework, human-in-the-loop/HITL approval, approval_mode always_require, confirm_changes. Also for fixing the known traps: HITL approve-resume 400 'No tool output found', confirm_changes mis-wired, AG-UI snapshot cards vanishing, CopilotKit catch-all route 404/422, useSingleEndpoint, keyless Foundry 401 audience, Docker Hub rate-limit on ACR build." +--- + +# Forgewright — Foundry hosted agent + AG-UI + CopilotKit apps + +Build an agentic web app on the **hosted-agent-first** standard: ALL intelligence +(`FoundryChatClient` + tools + HITL + history) runs in an **Azure AI Foundry HOSTED +agent** (Responses protocol). A **light bridge** (Container App, no LLM/tools) speaks +AG-UI to a Next.js/CopilotKit UI, forwards every turn to the hosted agent, translates +Responses → AG-UI, and absorbs framework bugs. CopilotKit (**v2** hooks) is the UI +layer: chat, generative cards, forms, approval, and shared/predictive state. + +> **Prerequisite (paid service):** this stack targets **Azure AI Foundry**. You need +> an Azure subscription, a provisioned Foundry project, `az login`, and the `azd` +> Foundry extension. There is no fully-offline path — local dev runs the *real* agent +> via `azd ai agent run`. + +``` + Next.js + CopilotKit v2 (frontend/) Foundry HOSTED agent = the BRAIN + useAgent / useFrontendTool / src/agent.py build_hosted_agent(): + useRenderTool / useHumanInTheLoop FoundryChatClient (Responses) + route.ts (CopilotSseRuntime + HttpAgent) ALL @tools + HITL + history + │ AG-UI / SSE ▲ Responses (stream) + + ▼ │ mcp_approval_response + BRIDGE (backend/bridge_app.py) │ + HostedProxyAgent → forwards turns to the hosted agent, translates + Responses→AG-UI, forwards mcp_approval_response on approve (tool re-executes). + Same code drives the LOCAL agent (`azd ai agent run`, DIRECT mode) and the + DEPLOYED agent (platform mode) — no mock anywhere. + (+ SSE keepalive, optional API key.) + GOVERNANCE: build_hosted_agent() + ResponsesHostServer (azd) publishes the agent. +``` + +**Golden rule:** `azd` SUCCESS, a dev server starting, or one chat reply is **not** +proof. Because all logic is server-side, you are done only when the structural and +end-to-end checks pass AND a **live** browser E2E against the deployed hosted agent +passes for the patterns in scope. Never declare success on an unverified build. + +## 0. Orient + +- `LOAD references/architecture.md` — hosted-first topology; what lives where; the + native-path test matrix proving why the hand-rolled bridge is the minimum. +- `LOAD references/patterns-7.md` — the 7 AG-UI dojo patterns on this stack + (Agentic Chat, Backend Tool Rendering, HITL, Tool-Based Generative UI, Agentic + Generative UI, Shared State, Predictive State) with source citations. +- `LOAD references/troubleshooting.md` — every known trap → symptom → fix. +- `LOAD references/hosted-deploy.md` — Foundry hosted-agent deploy gotchas (azd, + remote build, dependency pinning). + +The canonical, runnable template + scaffolding scripts live in the companion repo +**[lordlinus/forgewright](https://github.com/lordlinus/forgewright)** under +`templates/agentic-copilot-foundry/`, with vendored AG-UI dojo source under +`reference/dojo/`. Read both before changing anything; do not reinvent the bridge, +the patches, or the state machinery. + +## 1. Scaffold (always start here) + +Instantiate the canonical template into a new, runnable app (lowercase-hyphen name) +and rewrite the agent-name tokens (`AGENT_NAME`, the CopilotKit agent, the route, the +hosted yaml) so they stay consistent: + +```bash +scripts/new-app.sh [target-dir] # from the forgewright repo +``` + +The result already runs and already passes the bridge end-to-end smoke check. + +## 2. Customize to the user's prompt — extension points + +Edit `src/agent.py` (the hosted brain via `build_hosted_agent()`): +- Instructions — the agent's behavior for the requested domain. +- Tools — keep **≥1 read tool** (no side effects) and **≥1 consequential tool** + decorated `@tool(approval_mode="always_require")`. Map the user's "needs approval + before X" to the gated tool. For shared/predictive/generative-UI features, add the + `state_schema` + `predict_state_config` shape from `references/patterns-7.md`. +- Update the smoke script's domain prompts (read prompt / action prompt / state field + / read tool) to match your tools so the E2E exercises the chosen patterns against + the real agent. + +Edit `frontend/components/` (CopilotKit **v2** hooks — see `references/patterns-7.md`): +- `useFrontendTool` (client tools / tool-based generative UI), `useRenderTool` + (backend tool cards), `useHumanInTheLoop` (HITL approval; keep the + `{ accepted, steps }` contract), `useAgent` (shared / predictive state). + +**Do NOT touch** (load-bearing and proven): +- the bridge wiring in `backend/bridge_app.py` (`HostedProxyAgent` + the one + snapshot-split workaround); +- `build_hosted_agent()` (`FoundryChatClient`, Responses) in `src/agent.py`; +- the CopilotKit bridge route `frontend/app/api/copilotkit/[[...slug]]/route.ts`. + +## 3. Prove it + +```bash +make verify # structural: bridge wiring, FoundryChatClient, HITL contract, names, MCR base +make smoke # the BRIDGE against the REAL agent running locally via `azd ai agent + # run` — read works, action PAUSES, approve executes, reject doesn't, + # state deltas flow for the shared/predictive patterns in scope. + # Needs `az login` + a provisioned Foundry project. +``` + +Both must be green. Then `make local` (dev loop) and, in a Foundry-enabled tenant, +`make up` (azd → hosted agent) followed by a **live browser E2E** — the real DoD, +since all logic is server-side. + +## Load-bearing rules (why the template is shaped this way) + +### The bridge forwards HITL to the hosted agent (hand-rolled — and necessary) +- **Deployed:** `bridge_app.py` mounts `HostedProxyAgent` (a `SupportsAgentRun`) on + the AG-UI endpoint. It forwards each turn to the deployed Foundry hosted agent over + streaming Responses, translates the output to AG-UI (text, tool cards, + `confirm_changes`), and on HITL approve forwards an `mcp_approval_response` so the + gated tool **re-executes server-side**. `bridge_app.py` neutralises ag-ui's LOCAL + approval interception so the decision reaches the agent. +- **Why hand-rolled, not the native `add_agent_framework_fastapi_endpoint(FoundryAgent)`:** + re-verified live on the latest packages (matrix in `references/architecture.md`). + The native path needs `allow_preview=True` just to reach the hosted-agent endpoint, + and even then — with or without the ag-ui patches — HITL **approve does NOT + re-execute** the tool: the `FoundryAgent` client has no client-side + `mcp_approval_response`. The hand-rolled forwarder fills exactly that one gap. + Tracked upstream as + [microsoft/agent-framework#6652](https://github.com/microsoft/agent-framework/issues/6652); + retire the shim when it closes. +- **Local dev (`make local`/`make smoke`):** `azd ai agent run` runs the REAL agent + (`ResponsesHostServer` + `FoundryChatClient`) on your machine, connected to your + Foundry project's model; the bridge points at it in **DIRECT mode** + (`HOSTED_AGENT_DIRECT_URL`), driving the SAME `HostedProxyAgent` path as production — + no mock anywhere. +- Why a bridge at all: you **cannot** point `@ag-ui/client` at a deployed hosted + agent — `ResponsesHostServer` speaks OpenAI Responses, not AG-UI. +- The bridge must NOT send `x-ms-user-isolation-key` (deployed agents use Entra + isolation → 400). An SSE keep-alive keeps the stream alive during silent tools. + +### Client choice (load-bearing) +- **`build_hosted_agent` → `FoundryChatClient` (Responses)** — the single brain, the + SAME code locally (`azd ai agent run`) and deployed (`azd up`). Required so the + hosted `mcp_approval_request`/`mcp_approval_response` re-executes the gated tool + (verified live: 100→125 deployed, 100→110 local). No mock client. The Responses + `OpenAIChatClient` / Chat Completions path 500s on hosted approve-resume — do not + use it here. + +### Framework workarounds — minimal, re-check each upgrade +`bridge_app.py` patches (both proven load-bearing by the smoke E2E): (a) route HITL +approvals to the hosted agent (not local); (b) split multi-tool snapshot messages +(CopilotKit v1 renders only `toolCalls[0]`). Re-run the native-path matrix in +`references/architecture.md` on each upgrade and delete a patch the moment the +framework closes the gap. + +### The 7 AG-UI patterns +See `references/patterns-7.md`. Through the deployed/local hosted bridge: Agentic +Chat, Backend Tool Rendering, HITL (forwarded). Shared/predictive state through the +bridge is roadmap (native only when the AG-UI adapter wraps an in-process agent). + +### HITL contract +- A `@tool(approval_mode="always_require")` tool surfaces as a `confirm_changes` + tool call (with `function_name`, `function_arguments`, `steps`). The frontend + (`useHumanInTheLoop`) resolves it with `{ accepted: boolean, steps }` (NOT + `{ approved }`). The framework's native approval flow re-executes on accept. + +### CopilotKit (UI layer) +- **v2 React hooks** (`@copilotkit/react-core/v2`): `useAgent`, `useAgentContext`, + `useFrontendTool`, `useRenderTool`, `useHumanInTheLoop`. +- **Bridge route:** catch-all `app/api/copilotkit/[[...slug]]/route.ts`; import + `@copilotkit/runtime/v2`; `createCopilotHonoHandler` (multi-route, not the + single-route Next endpoint); re-export POST/GET/PATCH/DELETE; + ``. Any miss → Threads 404/405/422. + +### Containers +Use **MCR** base images (`mcr.microsoft.com/devcontainers/python:3.12`, +`.../typescript-node:20`). Docker Hub anonymous pulls hit `toomanyrequests` on +`az acr build` / ACR Tasks. + +## Anti-patterns + +- **Hand-rolling a NEW Responses→AG-UI proxy from scratch.** Reuse the proven + `HostedProxyAgent`. (The framework-native + `add_agent_framework_fastapi_endpoint(FoundryAgent(...))` does NOT forward HITL + approve — that is why the hand-rolled forwarder exists.) +- Putting business logic the agent should own into the bridge — the bridge is just + the framework endpoint + SSE keepalive + optional upload. +- Using the Responses `OpenAIChatClient` for `build_hosted_agent` — use + `FoundryChatClient`. +- Resolving approval with `{ approved }` instead of `{ accepted, steps }`. +- `useSingleEndpoint` left at its default `true` (Threads/Info 404). +- A consequential tool **without** `approval_mode="always_require"` (no HITL gate). +- Docker Hub base images in any Dockerfile. +- Declaring success because a server started — run the structural + smoke checks, and + for the deployed (server-side) path a live browser E2E. + +## Definition of Done + +The app is **not** done until all are true (evidence-backed): + +- [ ] Structural check green (bridge mounts `HostedProxyAgent`; `build_hosted_agent` + uses `FoundryChatClient`; the bridge forwards HITL; HITL contract; names; MCR). +- [ ] Smoke E2E green: the bridge against the REAL agent (run locally via + `azd ai agent run`) shows read works; the consequential prompt PAUSES; approve + executes; reject does not; and for shared/predictive patterns in scope, state + flows. +- [ ] `src/agent.py` has `build_hosted_agent()` (`FoundryChatClient`), ≥1 read tool + and ≥1 `approval_mode="always_require"` tool. +- [ ] Agent name is consistent across `src/agent.py`, the route, the CopilotKit + provider, and the hosted yaml. +- [ ] No secrets, endpoints, or app-specific hard-coding committed. +- [ ] **Live** (the deployed path drives a server-side agent): `make up` succeeds, the + bridge runs with `HOSTED_AGENT_NAME` → `HostedProxyAgent` → the deployed agent, + and a **real browser E2E** passes for the patterns in scope — HITL approve **and** + reject, plus any shared/predictive state round-trip and generative-UI cards. diff --git a/skills/foundry-hosted-agent-copilotkit/references/architecture.md b/skills/foundry-hosted-agent-copilotkit/references/architecture.md new file mode 100644 index 000000000..c4752d35c --- /dev/null +++ b/skills/foundry-hosted-agent-copilotkit/references/architecture.md @@ -0,0 +1,118 @@ +# Architecture — Foundry hosted agent + CopilotKit, via a HITL-forwarding bridge + +**Goal:** an Azure AI Foundry HOSTED agent (all tools + HITL + history server-side) +with a CopilotKit UI showing rich generative UI — tool-render cards, human-in-the- +loop approval, shared/predictive state. + +**Why a bridge at all:** you **cannot** point `@ag-ui/client` at a deployed hosted +agent — its endpoint speaks the OpenAI **Responses** protocol, not AG-UI. AND the +framework's *native* path (`add_agent_framework_fastapi_endpoint(FoundryAgent(...))`) +resolves the HITL `confirm_changes` **locally** and never forwards the approval, so +the gated tool **does not re-execute** (verified live). So the bridge is a small +hand-rolled forwarder: it translates Responses→AG-UI AND forwards the HITL decision +as an `mcp_approval_response`, which re-executes the tool server-side. + +``` + Browser — Next.js + CopilotKit (v2 hooks) + useAgent / useFrontendTool / useRenderTool / useHumanInTheLoop + app/api/copilotkit/[[...slug]]/route.ts (CopilotSseRuntime + HttpAgent) + │ AG-UI / SSE + ▼ + BRIDGE (Container App — backend/bridge_app.py) + LOCAL/DEPLOYED: HostedProxyAgent (SupportsAgentRun) — forwards each turn to the + hosted agent (hosted_client, streaming Responses), translates → AG-UI + (text, tool cards, confirm_changes), and forwards mcp_approval_response + on approve (bridge_app patches neutralise ag-ui's local interception). + LOCAL DEV: `azd ai agent run` runs the agent on your machine; bridge → DIRECT + mode (HOSTED_AGENT_DIRECT_URL). DEPLOYED: bridge → platform mode. No mock. + │ POST .../agents//endpoint/protocols/openai/responses (stream) + ▼ + FOUNDRY HOSTED AGENT (the brain — azd → host: azure.ai.agent) + src/agent.py build_hosted_agent(): FoundryChatClient (Responses), store=False + ALL @tools + @tool(approval_mode="always_require") HITL + history server-side +``` + +## Validated live (deployed agent agentic-copilot-foundry, swec-proj-default) + +- Read tool → runs server-side; tool-render card in AG-UI. +- HITL trigger → `mcp_approval_request` → bridge surfaces `confirm_changes` (pause). +- **Approve → bridge sends `mcp_approval_response{approve:true}` → tool re-executes + server-side, state changes (100→125).** No "No tool output found". +- Reject → `approve:false` → tool does NOT execute (state unchanged). +- Two gotchas found live: the bridge must NOT send `x-ms-user-isolation-key` + (deployed agents use Entra isolation → 400); and `build_hosted_agent` MUST use + `FoundryChatClient` (Chat Completions 500s on hosted approve-resume). + +## Why the bridge is the MINIMUM (native-path test matrix) + +Is the hand-rolled bridge over-engineering? We tested every alternative against the +real agent on the **latest** packages (agent-framework-core 1.9.0, +agent-framework-foundry 1.8.2, agent-framework-ag-ui 1.0.0rc5). `make smoke` = 15 +assertions (read, HITL pause, approve re-executes, reject, C9, C10). + +| Configuration | Result | +| --- | --- | +| **Bridge (HostedProxyAgent + 2 patches)** | **15/15** ✓ | +| Bridge, HITL approval routing patch removed | approve does NOT change state ✗ — patch REQUIRED | +| Bridge, `DISABLE_C9_SPLIT=1` | C9 fails (snapshot lumps multiple tool_calls) ✗ — split REQUIRED | +| Native `add_agent_framework_fastapi_endpoint(FoundryAgent(...))` | 400 "Hosted agents can only be called through the agent endpoint" ✗ | +| Native + `allow_preview=True` | surfaces the approval, but **approve does NOT re-execute** (state unchanged); C9 fails ✗ | +| Native + `allow_preview=True` + the 2 patches | **still** approve does NOT re-execute ✗ | + +**Conclusion:** the native `FoundryAgent` client has no client-side +`mcp_approval_response` — it cannot complete hosted HITL no matter how it's +configured. We still use `agent-framework-ag-ui` (`add_agent_framework_fastapi_endpoint`) +for the AG-UI translation; we just feed it a `SupportsAgentRun` shim +(`HostedProxyAgent`) that forwards the approval, plus two ag-ui patches `make smoke` +proves are load-bearing. Nothing else is hand-rolled. **Tracked upstream as +[microsoft/agent-framework#6652](https://github.com/microsoft/agent-framework/issues/6652)** — +re-run this matrix on each package bump and retire the shim + the HITL-routing patch +the moment #6652 closes (the native `FoundryAgent` path then suffices). + +## Client choice (the load-bearing rule) + +- **Hosted agent (`build_hosted_agent`) → `FoundryChatClient` (Responses).** Required + so the hosted runtime's `mcp_approval_request`/`mcp_approval_response` re-executes + the gated tool. Chat Completions 500s on resume here. +- **Local dev → `azd ai agent run`**: the Foundry extension runs the REAL agent + (`ResponsesHostServer` + `FoundryChatClient`) on your machine, connected to your + Foundry project's model. `make smoke`/`make local` point the bridge at it in DIRECT + mode (`HOSTED_AGENT_DIRECT_URL` → POST `/responses` with `previous_response_id` + chaining), so it drives the SAME `HostedProxyAgent` path as production. No mock — + needs `az login` + a provisioned project (`make up` once). + +## File map + +``` +/ +├── src/ +│ └── agent.py ONE agent. build_hosted_agent() → FoundryChatClient +│ (the single brain — same code local + deployed). Read tools +│ + ≥1 @tool(approval_mode="always_require"). +├── backend/ THE BRIDGE (deployed Container App). +│ ├── bridge_app.py AG-UI endpoint → HostedProxyAgent (DIRECT local / +│ │ platform deployed). + SSE keepalive + optional API key. +│ ├── hosted_proxy.py HostedProxyAgent: forward turns + translate Responses → +│ │ AG-UI; surface confirm_changes; forward mcp_approval_response. +│ ├── hosted_client.py streaming Responses driver: platform (conversation + +│ │ agent_session_id, keyless) OR DIRECT (local azd ai agent run). +│ ├── requirements.txt bridge deps only (httpx pin; no foundry/openai — runs no model). +│ └── Dockerfile MCR base; deploys uvicorn bridge_app:app. +├── hosted/ azd → Foundry HOSTED agent (Responses) — the deployed brain. +│ ├── azure.yaml host: azure.ai.agent; azure.ai.agents pinned; context=root. +│ └── responses/ main.py = ResponsesHostServer(build_hosted_agent()), … +├── frontend/ Next.js + CopilotKit v2 (useAgent/useFrontendTool/ +│ useRenderTool/useHumanInTheLoop). +├── scripts/ verify.sh (structural), smoke.py (E2E vs the real local agent), +│ lib-agentrun.sh (azd ai agent run + bridge DIRECT). +└── Makefile(+.targets) preflight / local / verify / smoke / up / deploy / clean. +``` + +## Proving it (Definition of Done) + +`azd` SUCCESS / a server starting is **not** proof. Done = `make verify` + +`make smoke` (the bridge against the REAL agent run locally via `azd ai agent run`) +green, AND — because the deployed path drives a server-side agent — a **live** +browser E2E: deploy with `azd`, run the bridge with `HOSTED_AGENT_NAME` set, and +confirm read + HITL approve (tool re-executes, state changes) **and** reject (no +change) in a real browser. diff --git a/skills/foundry-hosted-agent-copilotkit/references/hosted-deploy.md b/skills/foundry-hosted-agent-copilotkit/references/hosted-deploy.md new file mode 100644 index 000000000..1386e0f76 --- /dev/null +++ b/skills/foundry-hosted-agent-copilotkit/references/hosted-deploy.md @@ -0,0 +1,60 @@ +# Hosted deploy — publish the agent as a Foundry hosted agent (azd) + +The `build_hosted_agent()` that backs the deployed brain (FoundryChatClient, +Responses) is published as an **Azure AI Foundry hosted agent**. This runs from +`hosted/` and needs an Azure subscription + a Foundry-enabled tenant. The SAME +`build_hosted_agent()` runs locally for development via `azd ai agent run`. + +## Prerequisites + +- `az login` into the **tenant that owns the Foundry project** (a 403 on + `Microsoft.MachineLearningServices/workspaces/agents/action` means the wrong + tenant). +- The azd `azure.ai.agents` extension: + `azd extension install azure.ai.agents` (the template pins `>=0.1.0-preview`). +- An `azd` environment with a region/model selected. + +## Deploy + +```bash +cd hosted +azd env new # first time +azd env set AZURE_LOCATION +# (model deployment name comes from hosted/azure.yaml `deployments` + agent.yaml) +make up # == azd up : provision + remote-build the image + publish the agent +``` + +`make up` builds the image with **remote build** (so no local Docker needed) from +the template root context (so the shared `src/agent.py` is included), provisions +the model deployment declared in `hosted/azure.yaml`, and publishes the hosted +agent described by `agent.yaml` / `agent.manifest.yaml`. + +## Gotchas (also in troubleshooting.md) + +- **Docker Hub rate limit** on build → the Dockerfiles use `mcr.microsoft.com` + base images. Keep it that way. +- **helloworld placeholder deployed** → you ran `azd provision` only; run + `make up` (provision + deploy). +- **401 "audience is incorrect"** at runtime → the agent must request the + `https://ai.azure.com/.default` audience (the template's `build_hosted_agent` + already does). + +## Prove the hosted agent (live) + +Deployment SUCCESS is not proof. Run the agent (e.g. via the VS Code Foundry +toolkit `azd ai agent run`, or the Foundry portal playground) and confirm that +**one consequential action pauses for human approval** before executing — the +same HITL contract you verified locally with `make smoke`. + +## Connecting a frontend to the hosted agent (the light bridge) + +In production the chat UI does NOT run the agent — it talks to the deployed Foundry +hosted agent through the **light bridge** (`backend/bridge_app.py`, the +`backend/Dockerfile` default). Deploy the bridge as a Container App and point the +CopilotKit runtime's `AG_UI_BACKEND_URL` at it; set `FOUNDRY_PROJECT_ENDPOINT` + +`HOSTED_AGENT_NAME` (the deployed agent) on the bridge so `HostedProxyAgent` can reach +it keyless. Run a single replica (per-thread conversation/session cache is +in-memory) or externalise the cache. The CopilotKit `route.ts` bridge is unchanged. + +For the local dev loop, `make local` runs the SAME agent locally via +`azd ai agent run` and points the bridge (`bridge_app:app`) at it — no mock. diff --git a/skills/foundry-hosted-agent-copilotkit/references/patterns-7.md b/skills/foundry-hosted-agent-copilotkit/references/patterns-7.md new file mode 100644 index 000000000..ff3061d9b --- /dev/null +++ b/skills/foundry-hosted-agent-copilotkit/references/patterns-7.md @@ -0,0 +1,73 @@ +# The 7 AG-UI patterns on the hosted-agent + light-bridge stack + +These are the AG-UI dojo "Microsoft Agent Framework Python" feature patterns, +adapted to our standard (intelligence in the Foundry HOSTED agent; a light bridge; +CopilotKit **v2** UI hooks). Canonical source is vendored under `reference/dojo/`: +backend agents from `microsoft/agent-framework` +(`python/packages/ag-ui/agent_framework_ag_ui_examples/agents/*`) and the v2 +frontend pages from `ag-ui-protocol/ag-ui` +(`apps/dojo/src/app/[integrationId]/feature/(v2)/*`). + +CopilotKit **v2** hooks (`@copilotkit/react-core/v2`): +`useAgent`, `useAgentContext`, `useFrontendTool`, `useRenderTool`, +`useHumanInTheLoop`. + +| # | Pattern | Hosted-agent side | CopilotKit v2 UI | Through the bridge | +|---|---|---|---|---| +| 1 | Agentic Chat (frontend tools) | plain `Agent` (no server tool needed) | `useFrontendTool({name,parameters,handler})` (runs in browser) + `useAgentContext` | native — client tool, agent just emits the tool call | +| 2 | Backend Tool Rendering | `@tool` (executes server-side) | `useRenderTool({name,parameters,render})` | native — `function_call`/`function_call_output` forwarded | +| 3 | HITL approval | `@tool(approval_mode="always_require")` | `useHumanInTheLoop({name,render})` → `respond({accepted, steps})` | native function-approval; surfaces as `confirm_changes` | +| 5 | Tool-Based Generative UI | `FunctionTool(func=None)` (declaration-only) + `tool_choice="required"` | `useFrontendTool({name,handler,render,followUp:false})` | native — stream tool-call args to the renderer | +| 4 | Agentic Generative UI | `predict_state_config` + `require_confirmation=False`; stream step status via tool args | `useAgent({updates:[OnStateChanged]})` → `agent.state` | **bridge synthesizes** StateDelta/Snapshot from arg-deltas | +| 6 | Shared State | `AgentFrameworkAgent(state_schema, predict_state_config, require_confirmation=False)` | `useAgent` + `agent.setState()` | **bridge synthesizes** state + **forwards** `setState` → hosted input | +| 7 | Predictive State Updates | same as #6 but `require_confirmation=True` (default) + `@tool(approval_mode="always_require")` | `useAgent` + `useHumanInTheLoop` (confirm/reject) | synthesized streaming state + HITL confirm | + +## How it works on this stack + +- **Native adapter (reference):** `add_agent_framework_fastapi_endpoint(agent)` + natively emits all AG-UI events — text, TOOL_CALL_* cards, function-approval HITL, + and StateSnapshot/Delta (via `state_schema`+`predict_state_config`) — *when it + wraps a plain in-process `Agent`*. The templates don't run the agent in-process; + they keep all logic in the Foundry hosted agent and reach it through the bridge. +- **Deployed (hosted agent):** the bridge is `HostedProxyAgent`, NOT the native + `add_agent_framework_fastapi_endpoint(FoundryAgent(...))`. The native FoundryAgent + path translates read/cards/HITL-*pause*, but on HITL **approve it does NOT + re-execute** the hosted tool (it resolves `confirm_changes` locally; the Foundry + client has no `mcp_approval_response` forwarding — verified live). `HostedProxyAgent` + forwards `mcp_approval_response` to the hosted agent so the gated tool re-executes + server-side. Use it for any deployed app with HITL. + +| # | Pattern | Hosted-agent side | CopilotKit v2 UI | Through the bridge | +|---|---|---|---|---| +| 1 | Agentic Chat | plain Agent | `useFrontendTool` | native | +| 2 | Backend Tool Rendering | `@tool` | `useRenderTool` | HostedProxyAgent forwards function_call/result | +| 3 | HITL approval | `@tool(approval_mode="always_require")` | `useHumanInTheLoop` → `{accepted, steps}` | bridge forwards mcp_approval_response (re-executes) | +| 5 | Tool-Based Generative UI | `FunctionTool(func=None)` | `useFrontendTool` render | stream tool-call args | +| 4 / 6 / 7 | Agentic Generative / Shared / Predictive State | `state_schema` + `predict_state_config` | `useAgent` + `setState` | bridge relays text/tool-arg deltas; state synthesis through the deployed bridge is roadmap (native only when the adapter wraps an in-process agent) | + +## HITL contract + +The gated tool surfaces as `confirm_changes` (with `function_name`, +`function_arguments`, `steps`); the UI (`useHumanInTheLoop`) resolves +`{ accepted: boolean, steps }`. Deployed: Accept → `mcp_approval_response{approve:true}` +(tool re-executes server-side), Reject → `approve:false`. + +## Framework workarounds (minimal; re-check each upgrade) + +`bridge_app.py` patches: (a) neutralise ag-ui's local approval interception +so approvals reach the hosted agent; (b) split multi-tool snapshot messages +(CopilotKit v1 renders only `toolCalls[0]`; set `DISABLE_C9_SPLIT=1` on a v2 frontend). +The bridge must NOT send `x-ms-user-isolation-key` (deployed agents use Entra +isolation → 400). + +## Roadmap: shared / predictive state through the DEPLOYED bridge + +Shared State, Predictive State, and Agentic Generative UI are emitted **natively by +the AG-UI adapter when it wraps an in-process `Agent`** (via `state_schema` + +`predict_state_config`). Through the DEPLOYED bridge they are **not yet wired**: the +hosted Responses stream would need `HostedProxyAgent` to relay +`response.function_call_arguments.delta` as growing tool-call args, and to forward +`useAgent.setState` (RunInput.state) to the hosted agent. The plumbing is understood +(the AG-UI adapter synthesises StateDelta/Snapshot from streaming tool-call args) but +is left as a follow-up — current templates ship the validated read + tool-render + +HITL through the deployed bridge. diff --git a/skills/foundry-hosted-agent-copilotkit/references/troubleshooting.md b/skills/foundry-hosted-agent-copilotkit/references/troubleshooting.md new file mode 100644 index 000000000..6bf4f3f0a --- /dev/null +++ b/skills/foundry-hosted-agent-copilotkit/references/troubleshooting.md @@ -0,0 +1,61 @@ +# Troubleshooting — known traps → symptom → fix + +Each row is a real failure mode encoded as a check in `scripts/verify.sh` or +`scripts/smoke.py`. Fix the cause; do not work around it. + +## HITL / approval + +| Symptom | Cause | Fix | +| --- | --- | --- | +| Approve a tool → `RUN_ERROR` 400/500 **"No tool output found for function call"** | the hosted agent uses Chat Completions (`OpenAIChatClient`/`OpenAIChatCompletionClient`) instead of Responses | `build_hosted_agent` MUST use `FoundryChatClient` (Responses) so the hosted `mcp_approval_response` re-executes the tool. `verify.sh` checks for `FoundryChatClient`. | +| Approval card never appears | `confirm_changes` not registered via the v2 `useHumanInTheLoop` hook | Keep the `confirm_changes` `useHumanInTheLoop({ name: "confirm_changes", ... })` from the template verbatim. | +| Clicking Approve does nothing / tool never runs | Resolving with `{ approved }` | Resolve with `{ accepted: boolean, steps }`. Backend detection is `"accepted" in parsed`. | +| Approve works once, next message 400s with orphaned `call_…` | (pre-rc5) stale approval payload re-sent | Handled NATIVELY on agent-framework-ag-ui rc5 — do not re-add the old hand-rolled patches. | +| Consequential tool runs WITHOUT asking | Tool missing `approval_mode="always_require"` | Decorate the consequential tool. `verify.sh` requires at least one. | + +## AG-UI rendering + +| Symptom | Cause | Fix | +| --- | --- | --- | +| HITL approve doesn't re-execute the tool server-side (state unchanged after approve) | ag-ui resolves `confirm_changes` **locally** before the proxy sees it | `bridge_app.py` neutralises `_is_confirm_changes_response` + `_resolve_approval_responses`, so the decision reaches `HostedProxyAgent`, which forwards `mcp_approval_response` to the hosted agent. **Proven load-bearing: disabling it → approve doesn't change state.** | +| Approval/tool card vanishes at RUN_FINISHED when a turn made several tool calls | ag-ui's snapshot builder lumps multiple tool_calls into one assistant message; CopilotKit **v1** renders only `toolCalls[0]` | `bridge_app.py` splits multi-tool snapshot messages (`_build_messages_snapshot`); `smoke.py` C9 guards it. **Proven load-bearing: `DISABLE_C9_SPLIT=1` fails C9.** (v2 renders all tool calls, but the split keeps the snapshot correct for both frontends.) | +| Replayed history 400s / orphaned tool call (C10) | raw AG-UI history replayed to the hosted agent | the proxy does **not** replay raw history — `_find_approval_decision` / `_latest_user_text` derive the turn input (latest user text, or an `mcp_approval_response`). `smoke.py` C10 asserts no error. No `normalize_*` patch needed. | + +## CopilotKit bridge + +| Symptom | Cause | Fix | +| --- | --- | --- | +| `GET /api/copilotkit/threads` 404 | missing catch-all dir | route lives in `app/api/copilotkit/[[...slug]]/route.ts`. | +| Threads 405 on every request | single-route endpoint | use `createCopilotHonoHandler` (multi-route) and re-export POST/GET/PATCH/DELETE. | +| Threads panel 422 "Missing CopilotKitIntelligence configuration" | lib `CopilotRuntime` wraps the runner in `TelemetryAgentRunner` | use the v2 `CopilotSseRuntime` with a raw `InMemoryAgentRunner`. | +| "Agent `` not found" / Info 404 | `useSingleEndpoint` defaulted to `true` | set ``. | +| `` doesn't match | name drift | keep `AGENT_NAME` == route const == provider == hosted yaml. `verify.sh` checks it. | +| `next build` type error: `HttpAgent` missing `pendingInterrupts` | `@ag-ui/client` older than the version CopilotKit resolves | pin `@ag-ui/client` to the version `@copilotkit/runtime` depends on (e.g. `0.0.56`). | +| Browser console: "Failed to execute 'fetch' on 'Window': Illegal invocation" (`agent_run_failed_event`); the agent never runs | CopilotKit v2 (`ɵcreateThreadStore` + `@ag-ui/client` HttpAgent) captures the global `fetch` as a bare reference and calls it with the wrong `this`; `CopilotKitCore` exposes no `fetch` option | bind the global fetch to `window` before any module loads — an inline `` script in `app/layout.tsx`: `if(!window.fetch.__bound){var f=window.fetch.bind(window);f.__bound=true;window.fetch=f;}`. `verify.sh` checks it; proven in a real browser (control reproduces, fix → 0 errors). | + +## Foundry connection + +| Symptom | Cause | Fix | +| --- | --- | --- | +| 401 "audience is incorrect" | default `cognitiveservices.azure.com` scope on the project path | request the `https://ai.azure.com/.default` audience. | +| 403 `workspaces/agents/action` | `az` logged into the wrong tenant for the project | `az login --tenant ` (or set the project's tenant). | +| Run the agent locally for dev | no deployed agent yet | `azd ai agent run` runs the REAL agent on your machine (what `make local`/`make smoke` use, via the bridge's DIRECT mode); needs `az login` + a provisioned project (`make up` once). | + +## Containers / azd + +| Symptom | Cause | Fix | +| --- | --- | --- | +| `az acr build` fails `toomanyrequests` | Docker Hub base image | use `mcr.microsoft.com/devcontainers/...` base images. | +| azd deploys the helloworld placeholder | ran `azd provision` only | run `make up` (= `azd up` = provision + deploy). | +| hosted image missing `src/agent.py` | build context too narrow | `hosted/azure.yaml` sets `context: ..` (template root). | + +## Bridge (the framework-native AG-UI endpoint) + +| Symptom | Cause | Fix | +| --- | --- | --- | +| Approval card vanishes at RUN_FINISHED | multi-tool snapshot; CopilotKit v1 renders only `toolCalls[0]` | keep the snapshot-split in `bridge_app.py`; `make smoke` C9 guards it. | +| HITL approve does nothing / state doesn't change | ag-ui resolved the approval locally (the routing patch was removed/disabled) | the hosted bridge needs **two** patches — HITL approval routing **and** the snapshot split — both proven load-bearing on rc5 (disabling either fails smoke). Keep both. | +| `useAgent().state` stays empty | `state_schema`/`predict_state_config` not passed to the endpoint, or no tool writes the state key | set `AGENT_STATE_SCHEMA`/`AGENT_PREDICT_STATE` in `src/agent.py` and write the key from a tool. | +| Deployed bridge can't reach the agent | `FOUNDRY_PROJECT_ENDPOINT` / `HOSTED_AGENT_NAME` unset | set both; the bridge (`hosted_client`) reaches the deployed agent keyless. | +| Python `@tool` didn't run "in Foundry" | FoundryAgent runs Python `@tool` callables CLIENT-SIDE; only Foundry-native tools run server-side | expected — define server-side tools on the deployed agent; keep `@tool`s for client-side/HITL. | +| UI 500 mid-run on a long silent tool | a gateway dropped the idle SSE | keep `SSEKeepAliveMiddleware` (`: ping` ~10s). |