# Foundry Hosted Agents

> >

- **Type:** Skill
- **Install:** `agentstack add skill-aiappsgbb-awesome-gbb-foundry-hosted-agents`
- **Verified:** Pending review
- **Seller:** [aiappsgbb](https://agentstack.voostack.com/s/aiappsgbb)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [aiappsgbb](https://github.com/aiappsgbb)
- **Source:** https://github.com/aiappsgbb/awesome-gbb/tree/main/skills/foundry-hosted-agents

## Install

```sh
agentstack add skill-aiappsgbb-awesome-gbb-foundry-hosted-agents
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Microsoft Foundry Hosted Agents — Reference Guide

Production-tested patterns for deploying hosted agents on Microsoft Foundry
(refreshed preview, April 2026). Covers the `Agent` + `FoundryChatClient` +
`ResponsesHostServer` (MAF) variant exclusively.

> **⚠️ MAF 1.8.0 recommended (June 2026) — caller-side `FoundryAgent(timeout=...)` knob.**
> Upgrade from 1.6.0 → 1.8.0 to pick up the new HTTP timeout kwarg on the
> caller-side `agent_framework.foundry.FoundryAgent` class (overrides the
> OpenAI-SDK 5s connect / 600s total defaults). All MAF 1.6.0 telemetry
> bundling (`microsoft-opentelemetry` + `opentelemetry-instrumentation-openai-v2`)
> is unchanged. The two MAF 1.8.0 `[BREAKING]` markers (github-copilot
> sub-package internal rename + experimental `Skill` ABC refactor) are
> **non-impact** for hosted-agent code — see [§ MAF 1.8.0 update](#maf-180-update-june-2026).
>
> If you are still on 1.3.x, upgrade directly to 1.8.0 — absorb the 1.4.0
> breaking changes below AND the 1.6.0 telemetry improvements AND the
> 1.8.0 timeout knob in one pass.

## When to Use

- Deploying a custom container agent to Foundry
- Debugging hosted agent failures (401, 500, import errors)
- Setting up RBAC for agent identities
- Configuring `agent.yaml`, `azure.yaml`, Bicep parameters
- Understanding the refreshed preview changes (packages, identity, invocation)
- **Migrating from MAF 1.3.x → 1.4.0** ([§ below](#maf-140-breaking-changes-may-2026))

> **Choosing a model?** See [`references/model-selection.md`](references/model-selection.md) for the model / region / capacity / data-residency decision before you `azd provision`.

---

## MAF 1.4.0 breaking changes (May 2026)

> **If upgrading from 1.3.x today, skip to 1.6.0 directly.** The version
> pins below show 1.4.0 for historical accuracy; use the 1.6.0 pins from
> [§ MAF 1.6.0 update](#maf-160-update-may-2026) instead — they absorb
> all 1.4.0 breaking changes AND add gen_ai telemetry.

Azure renamed the Foundry data-plane role from **"Azure AI User"** to
**"Foundry User"** and changed the AAD token audience the SDK requests
from `https://cognitiveservices.azure.com/.default` to
`https://ai.azure.com/.default`. **`agent-framework-core` 1.4.0**
(2026-05-14, alongside `agent-framework-foundry-hosting` 1.0.0a260514)
is the first SDK that requests the new scope; everything pinned to 1.3.x
or earlier requests the old scope and gets **`401 Unauthorized`** from
the post-rename data plane.

### The three changes you have to absorb

| # | Change | Symptom on pinned 1.3.x | Fix |
|---|--------|------------------------|-----|
| 1 | **AAD token scope:** `cognitiveservices.azure.com` → `ai.azure.com` | Every Responses request → `401 Unauthorized` (with valid `Foundry User` RBAC) | Upgrade to `agent-framework-core~=1.4.0` + `agent-framework-foundry~=1.4.0` + `agent-framework-foundry-hosting==1.0.0a260514` |
| 2 | **Role display name** "Azure AI User" → "Foundry User" | `az role assignment create --role "Azure AI User"` fails with `RoleDefinitionNotFound` | Use `--role "Foundry User"` OR pin by GUID `53ca6127-db72-4b80-b1b0-d745d6d5456d` (unchanged across the rename — the safest call-site form is the GUID) |
| 3 | **`AzureOpenAIChatClient` removed** from `agent_framework.azure` | `ImportError: cannot import name 'AzureOpenAIChatClient'` after `pip install -U` | Use `OpenAIChatClient(azure_endpoint=..., model=..., credential=...)` from `agent_framework.openai` — see snippet below |

### `AzureOpenAIChatClient` → `OpenAIChatClient` migration

`FoundryChatClient` is the right choice for hosted Foundry agents and is
unaffected. The removal hits **adjacent code paths** that talked directly
to Azure OpenAI (eval judges, batch scoring, sidecar services, agents
that route through APIM):

```python
# OLD (MAF 1.3.x — REMOVED in 1.4.0)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import get_bearer_token_provider, DefaultAzureCredential
client = AzureOpenAIChatClient(
    endpoint=AZURE_OPENAI_ENDPOINT,
    deployment_name=DEPLOYMENT,
    ad_token_provider=get_bearer_token_provider(
        DefaultAzureCredential(),
        "https://cognitiveservices.azure.com/.default",
    ),
)

# NEW (MAF 1.4.0)
from agent_framework.openai import OpenAIChatClient
from azure.identity import DefaultAzureCredential
client = OpenAIChatClient(
    azure_endpoint=AZURE_OPENAI_ENDPOINT,    # NB: kwarg renamed from `endpoint`
    model=DEPLOYMENT,                         # NB: kwarg renamed from `deployment_name`
    credential=DefaultAzureCredential(),     # SDK derives the right token scope internally
)
```

Drop the explicit `get_bearer_token_provider` / `ad_token_provider` —
`OpenAIChatClient` now derives the scope itself. The unused
`get_bearer_token_provider` import can be removed.

### Image-tag staleness trap (mandatory read for any pilot that pins by digest)

Hosted agent versions reference the orchestrator container by ACR
image **reference**. There are two common shapes:

- `.azurecr.io/tl-maf-orchestrator:latest` — re-resolves to the
  newest pushed image on every container start. Auto-picks up MAF
  rebuilds the next time the agent provisions.
- `.azurecr.io/tl-maf-orchestrator@sha256:abc…` — pinned to a
  specific layer digest. Reproducible, but **frozen at the MAF version
  that was in the image when the digest was computed**.

After a MAF 1.4.0 rebuild, agents using `:latest` work; agents using
`@sha256:…` digests from the 1.3.x era keep hitting the old token scope
and fail. **Re-import every hosted agent version** (or pin by digest to
the freshly-built 1.4.0 image) as part of the upgrade. `azd ai agent
deploy` does this automatically if the YAML references `:latest`.

### Rebuild recipe (copy-paste)

```bash
# 1. Upgrade orchestrator pyproject.toml + regenerate uv.lock
sed -i.bak 's/"agent-framework-core[^"]*"/"agent-framework-core~=1.4.0"/' src/orchestrator/pyproject.toml
sed -i.bak 's/"agent-framework-foundry[^"]*"/"agent-framework-foundry~=1.4.0"/' src/orchestrator/pyproject.toml
sed -i.bak 's/"agent-framework-foundry-hosting[^"]*"/"agent-framework-foundry-hosting==1.0.0a260514"/' src/orchestrator/pyproject.toml
(cd src/orchestrator && uv lock)

# 2. ACR remote build — tag with :latest AND a date-pinned tag for forensics
ACR=tl
az acr build --registry "$ACR" \
  --image "tl-maf-orchestrator:maf14-$(date +%Y%m%d%H%M)" \
  --image "tl-maf-orchestrator:latest" \
  --file src/orchestrator/dockerfile src/orchestrator/

# 3. Re-deploy + re-import each agent so it picks up the fresh image
azd deploy agents
(cd infra/scripts && uv run deploy_job.py)
# Then: azd ai agent show — confirm new image digest under each version
```

> **Why date-pinned tags matter.** `:latest` is fine for the agent
> reference but leaves zero forensic trail in ACR's image history.
> The `maf14-YYYYMMDDHHMM` tag lets you correlate "which agent
> version is running which MAF build?" months later when a regression
> bisect needs it.

## MAF 1.6.0 update (May 2026)

`agent-framework-core` 1.6.0 and `agent-framework-foundry-hosting`
1.0.0a260521 ship with **instrumentation enabled by default**. The
hosting package transitively pulls `azure-ai-agentserver-core>=2.0.0b3`
which depends on `microsoft-opentelemetry>=1.0.0` (resolves to 1.1.0),
bundling all OTel instrumentors:

| Bundled instrumentor | What it captures |
|---|---|
| `opentelemetry-instrumentation-openai-v2==2.3b0` | gen_ai spans: model name, token usage, latency for every OpenAI/Responses call |
| `opentelemetry-instrumentation-openai-agents-v2==0.1.0` | Agent-level invocation spans |
| `opentelemetry-instrumentation-httpx` | HTTP dependency spans |
| `azure-core-tracing-opentelemetry` | Azure SDK call tracing |

### What this means for `container.py`

**Remove all manual OTel code.** No `configure_azure_monitor()`, no
`OpenAIInstrumentor().instrument()`, no custom `TracerProvider`. The
platform handles everything. Your container code only needs:

```python
# Ensure env var is set (deploy.py passthrough for O-012 workaround)
cs = os.getenv("APPLICATION_INSIGHTS_CONNECTION_STRING") or \
     os.getenv("APPLICATIONINSIGHTS_CONNECTION_STRING")
if cs:
    os.environ["APPLICATIONINSIGHTS_CONNECTION_STRING"] = cs

# Optionally: SDK-level telemetry setup (after FoundryChatClient creation)
try:
    await client.configure_azure_monitor(enable_sensitive_data=True)
except Exception:
    pass  # Agent works without telemetry
```

### gen_ai spans appear under `cloud_RoleName = "agent_framework"`

**Important for KQL queries:** gen_ai dependency spans use
`cloud_RoleName = "agent_framework"`, NOT `agent-{jobId}-maf`.
Queries filtering `cloud_RoleName startswith "agent-"` will miss them.

```kql
// gen_ai spans — model, tokens, latency
dependencies
| where timestamp > ago(1h)
| where cloud_RoleName == "agent_framework"
| project timestamp, name, duration,
    model=tostring(customDimensions['gen_ai.response.model']),
    input_tokens=tostring(customDimensions['gen_ai.usage.input_tokens']),
    output_tokens=tostring(customDimensions['gen_ai.usage.output_tokens']),
    op=tostring(customDimensions['gen_ai.operation.name'])
| order by timestamp desc
```

### Upgrade recipe (→ 1.7.0)

```bash
sed -i.bak 's/"agent-framework-core[^"]*"/"agent-framework-core~=1.7.0"/' pyproject.toml
sed -i.bak 's/"agent-framework-foundry[^"]*"/"agent-framework-foundry~=1.7.0"/' pyproject.toml
sed -i.bak 's/"agent-framework-foundry-hosting[^"]*"/"agent-framework-foundry-hosting==1.0.0a260528"/' pyproject.toml
# Remove explicit OTel deps — now bundled via hosting package:
# azure-monitor-opentelemetry, opentelemetry-sdk, opentelemetry-instrumentation-*
```

### `create_version` deduplication trap

Foundry deduplicates `create_version` when environment variables and
metadata are **identical** — even if the container image tag/digest
differs. This is SEPARATE from the ACR layer cache trap below. After
a base image rebuild, new code never reaches the container because
Foundry returns the existing version.

**Fix:** Add a changing environment variable to force a new version:
```python
import time
env_vars["_BUILD_TS"] = str(int(time.time()))
```

### ACR layer cache trap (per-job images built via `DockerBuildRequest`)

When per-job images are built via the Azure Container Registry
`DockerBuildRequest` API (e.g., from `deploy.py`), ACR Tasks uses
**layer caching by default**. If the per-job Dockerfile's `COPY . ./`
content hasn't changed (only the base image changed), ACR produces
the **identical digest** — and Foundry deduplicates the
`create_version` call, silently returning the existing version.
New base-image code never reaches the container.

**Fix (both are required):**

1. **`no_cache=True`** on `DockerBuildRequest` — forces ACR to
   rebuild all layers from scratch.
2. **A used `ARG`** in the generated Dockerfile — Docker only
   invalidates cache when an ARG changes AND is consumed:
   ```dockerfile
   ARG BASE_IMAGE=hosted-agent-base-maf:v6
   FROM ${BASE_IMAGE}
   ARG BUILD_TS=1716400000
   RUN echo "build=$BUILD_TS" > /app/.build_ts
   # ... rest of Dockerfile
   ```
   Without the `RUN` line, Docker ignores the ARG for caching.

Discovered May 2026: base image rebuild with `SkillsProvider.from_paths()`
fix + observability fix never reached any agent until both guards were
applied.

---

## MAF 1.8.0 update (June 2026)

`agent-framework-core` and `agent-framework-foundry` 1.8.x land one
hosted-agent-relevant new knob and two `[BREAKING]` markers — both
non-impact for this skill.

- **`FoundryAgent(timeout=...)`** — new HTTP timeout kwarg on the
  caller-side `agent_framework.foundry.FoundryAgent` class. Overrides
  the underlying OpenAI-SDK default (connect: 5s, total: 600s). Useful
  when orchestrator code calling INTO a hosted agent wants a bounded
  ceiling. See [§ below](#foundryagent-timeout-parameter-maf-180).

### MAF 1.8.0 breaking markers — non-impact analysis

MAF 1.8.0 ships two `[BREAKING]` markers; neither affects hosted-agent code:

1. **`agent-framework-github-copilot` sub-package internal rename** —
   this skill does not pin or import `agent-framework-github-copilot`. **N/A.**
2. **Experimental `Skill` abstract-class refactor in `agent-framework-core`** —
   this skill uses the high-level `SkillsProvider.from_paths(...)` facade
   (see § Skill Loading below), not the experimental `Skill` ABC.
   Hosted-agent containers continue to work unchanged. **Non-impact.**

### FoundryAgent timeout parameter (MAF 1.8.0)

MAF 1.8.0 adds `timeout: float | None = None` to
`agent_framework.foundry.FoundryAgent.__init__`. This is an **HTTP-layer
timeout** that overrides the OpenAI SDK's default (connect: 5s, total: 600s)
for ALL requests made by this agent instance. Per the upstream docstring:
*"HTTP timeout in seconds for requests. When not provided, the OpenAI SDK
default is used (connect: 5s, total: 600s)."*

Use it to:

- **Lower the ceiling** for short-running agents that should fail fast
  (e.g. `timeout=30` for tool-routing agents with user-facing chat budgets).
- **Raise the ceiling** for long-running agents where 600s isn't enough
  (e.g. `timeout=1800` for multi-step research loops).

Scope note: this kwarg lives on the CALLER-side `FoundryAgent` class
(orchestrator code calling a hosted agent). The CONTAINER-side runtime
covered by [§ Runtime Pattern](#runtime-pattern-maf-variant) uses
`FoundryChatClient` directly and does NOT accept `timeout=` in 1.8.0 —
apply HTTP-layer transport timeouts on the underlying client instead.

> **MUST:** Copy verbatim from
> [`references/python/foundry_agent_timeout.py`](references/python/foundry_agent_timeout.py).
> Do NOT redefine inline — the validator enforces single-source-of-truth.

> **See also (MAF 1.8.0, experimental):**
> - `AgentFileStore` — new abstract base class for agent file-management
>   backends. Re-exported as `from agent_framework import AgentFileStore`.
>   Concrete implementations + usage patterns live in `foundry-memory` and
>   `foundry-toolbox`. NOT consumed by the hosted-agents runtime patterns
>   documented in this skill.

### Upgrade recipe (→ 1.8.0)

```bash
sed -i.bak 's/"agent-framework-core[^"]*"/"agent-framework-core~=1.8.0"/' pyproject.toml
sed -i.bak 's/"agent-framework-foundry[^"]*"/"agent-framework-foundry~=1.8.0"/' pyproject.toml
sed -i.bak 's/"agent-framework-foundry-hosting[^"]*"/"agent-framework-foundry-hosting==1.0.0a260528"/' pyproject.toml
# Telemetry bundling from MAF 1.6.0 is unchanged in 1.8.0 — no other deps to touch.
```

Note: the hosting package is pinned **exact** (`==1.0.0a260528`), not
compatible-release (`~=`). PEP 440 treats `~=1.0.0aN` as
`>=1.0.0aN,  **Status (verified against Microsoft Learn, Jul 2026).**
> **Container-based** hosted agent deploy reached **GA in early July
> 2026** — that is the stable path documented throughout the rest of
> this skill (Bicep + Dockerfile + `azd up`). The deltas in THIS
> section are the **source-code / VNext** surface, which **shipped as
> preview**, NOT GA:
>
> - `--deploy-mode code`, the platform-managed runtimes,
>   `--dep-resolution`, and the `invocations_ws` WebSocket are all
>   **preview**. Source-code deploy is explicitly labelled preview:
>   *"Functionality, region availability, and APIs might change before
>   general availability."*
> - Mutating REST calls (Create/Update/Delete) on the source-code path
>   require the preview feature header
>   `Foundry-Features: CodeAgents=V1Preview,HostedAgents=V1Preview`.
> - The Python hosting SDK `agent-framework-foundry-hosting` is still
>   **alpha** (no stable `1.0.0`) — pin exact per the pin file.
>
> For production today, prefer the **container** (GA) path. Adopt the
> preview deltas below only with the preview caveat in mind.
>
> Sources:
> [container deploy — GA](https://learn.microsoft.com/azure/foundry/agents/how-to/deploy-hosted-agent),
> [source-code deploy — preview](https://learn.microsoft.com/azure/foundry/agents/how-to/deploy-hosted-agent-code),
> [quicks

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [aiappsgbb](https://github.com/aiappsgbb)
- **Source:** [aiappsgbb/awesome-gbb](https://github.com/aiappsgbb/awesome-gbb)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** yes
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: flagged — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-aiappsgbb-awesome-gbb-foundry-hosted-agents
- Seller: https://agentstack.voostack.com/s/aiappsgbb
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
