Skill 180 · Foundry Agentic Loop
Subchapter 180.4
references/copilot-sdk-foundry.mdMarkdown17 KBView on GitHub
Read only for Copilot SDK agents, SDK/identity troubleshooting, or the dependency-packaging fallback. Continue using Microsoft Agent Framework when the architecture selects it; this recipe does not change framework defaults.
Recipe revision 3 (2026-10-19). Configuration/signatures were source-checked against and re-inspected on the installed packages below. The “v1.0.14” labels mark the first checked release, not an upper bound. This is not a claim that every SDK/runtime pair has passed a hosted deployment. Keep the project’s approved compatible pins; record and validate its actual combination at the .
github/copilot-sdk| Surface used by this recipe | 1.0.14 | 1.0.20 (installed check) | Later |
|---|---|---|---|
create_session(*, provider=..., mcp_servers=...) keyword-only | yes | yes | inspect |
send_and_wait(prompt: str, *, request_headers=...) | yes | yes | inspect |
ProviderConfig.bearer_token_provider refresh callback | yes | yes | inspect |
MCPHTTPServerConfig.headers static dict (no refresh callback) | yes | yes | inspect |
CopilotClient(telemetry=TelemetryConfig) and get_status().version | yes | yes | inspect |
azure-ai-agentserver-responses ResponseEventStream items (hosted agent) | 2.2.0 | 2.3.0 | inspect |
For an unlisted version, inspect these signatures before coding and record the result with the compatibility record.
Before troubleshooting, record Python (retain 3.13), github-copilot-sdk, Azure Identity, project/hosting-adapter versions, lockfile hash and the running Copilot runtime version. In v1.0.14, after client startup, (await client.get_status()).version reports that runtime; importlib.metadata.version("github-copilot-sdk") reports the Python package. Do not assume the developer shell’s copilot --version is the deployed runtime.
Inspect installed signatures/types, particularly CopilotClient.create_session, CopilotSession.send_and_wait, ProviderConfig and the hosting request-context accessor. The v1.0.14 Python interface uses keyword arguments for create_session, a string prompt for send_and_wait, and request_headers for per-turn model headers. Older examples accepting config/message dictionaries are not interchangeable. The bearer-token callback surface is experimental in this release: check the runtime supports its protocol; never silently replace an unsupported refresh callback with a static token. Use the SDK-supported runtime distribution and pin the validated combination.
For the discovered OpenAI-compatible /openai/v1/ base, use type: "openai" and wire_api: "responses". Keep the Basic sample’s actual deployment or the user’s existing deployment; a model catalog name is not necessarily its deployment name. Do not mix this route with the native type: "azure" host-only recipe or with the hosted-agent Responses endpoint.
The following provider fragment uses v1.0.14 fields. credential must be the approved deployed workload async credential in hosted runs; select it through current microsoft-foundry guidance. token_scope comes from the verified endpoint contract (the documented Foundry path uses https://ai.azure.com/.default); verify the audience for a different Azure endpoint rather than assuming it.
from azure.core.credentials_async import AsyncTokenCredential
from copilot.session import ProviderConfig, ProviderTokenArgs
def model_provider(
base_url: str,
token_scope: str,
credential: AsyncTokenCredential,
) -> ProviderConfig:
async def refresh_token(_request: ProviderTokenArgs) -> str:
return (await credential.get_token(token_scope)).token
return {
"type": "openai",
"base_url": base_url,
"wire_api": "responses",
"bearer_token_provider": refresh_token,
}The SDK requests a token before outbound provider calls; Azure Identity handles caching/renewal. Keep that credential alive for its owning client and close it on shutdown. Do not capture one startup token in bearer_token, .env or a long-lived Authorization header. Test another request after simulated expiry; no new deployment should be needed.
Create the session with the verified provider and deployment using an explicit least-privilege permission handler, not an unrestricted approval shortcut. Subscribe to errors and execution events before sending; send_and_wait/session.idle alone is not the hosted Responses success condition. Its timeout does not abort in-flight work: explicitly cancel/clean up the task-owned request through supported lifecycle APIs. Use the checkpoint’s terminal-status and schema validation.
Use the current hosting adapter’s request-context accessor; verify its installed API instead of guessing imports. For container protocol 2.0.0, the documented outbound caller-context header is x-agent-foundry-call-id. Forward its opaque value unchanged to the configured Foundry model and toolbox requests. The platform-generated x-agent-user-id is local partitioning context and must not be forwarded outbound. Do not copy all inbound headers, trust caller-supplied x-client-* as identity, or propagate incoming Authorization/cookies.
session.send_and_wait(prompt, request_headers=foundry_headers) for the model request in v1.0.14. For adapters using provider headers, isolate the provider/session per request; never mutate a global header dictionary.tools/list, skill-loading and tools/call requests. Use the platform-aware toolbox client or a request-local MCP adapter that acquires/refreshes its own token for the toolbox audience. A Copilot model bearer_token_provider does not refresh MCP credentials.headers as a static dictionary, not a token-refresh callback. Do not invent a callback field or keep a startup bearer token there indefinitely. Use a supported refreshing MCP transport, or a request-scoped connection with explicit token lifetime/reconnect handling; retain least-privilege tool approval.Verify actual outbound header presence and request association with redacted instrumentation, including two concurrent requests and token renewal. Do not merely inspect configuration or use an SDK mock as proof that the hosted runtime forwarded it.
Configure telemetry in the minimal slice, before deployment. The Python hosting adapter and Copilot runtime are separate telemetry producers: a project connection or Python exporter alone does not prove that runtime model/tool spans are exported.
Record the route. Add the hosting/Copilot SDK and running runtime versions, export paths, discovered receiver endpoints and exporting identities/scopes to the root README.md (rapid) or docs/implementation.md (SDD), and the Application Insights resource ID to .azure/deployment-plan.md. Keep connection configuration in the appropriate deployment settings and Git-ignored .env, not diagnostic logs. Resolve the selected project’s linked Application Insights through microsoft-foundry; prefer its azd-owned values over duplicated metadata. Do not silently use a different destination.
Configure the host once. Load telemetry configuration before constructing the hosting adapter. Current AgentServer Core initializes observability through microsoft-opentelemetry, supports Azure Monitor export with APPLICATIONINSIGHTS_CONNECTION_STRING, and supports OTLP export with OTEL_EXPORTER_OTLP_ENDPOINT. Inspect the installed adapter’s behavior before using these settings; do not independently initialize a duplicate global provider/exporter. Generic HTTP-client instrumentation is disabled by default in the inspected source: enable only needed supported instrumentation or add explicit spans at owned MCP/application boundaries.
Configure the runtime explicitly. Merge the following v1.0.14 fragment into the existing client’s configuration rather than creating a second runtime. COPILOT_OTLP_ENDPOINT is an application-defined variable containing a verified, reachable OTLP HTTP receiver base URL, not an Application Insights connection string or ordinary ingestion URL.
import os
from copilot import CopilotClient
client = CopilotClient(
telemetry={
"otlp_endpoint": os.environ["COPILOT_OTLP_ENDPOINT"],
"otlp_protocol": "http/protobuf",
"exporter_type": "otlp-http",
"capture_content": False,
},
)Verify the receiver’s supported onward export to the intended Azure Monitor/Application Insights destination. Reuse a working route; obtain approval before provisioning a collector or other ingestion resources. Native Azure Monitor OTLP ingestion requires its discovered endpoints, Data Collection Rule (DCR) and authentication; an ordinary connection string does not configure that route. Do not point an HTTP/protobuf client at a gRPC-only receiver or assume the Python host is an OTLP receiver.
Check the actual exporter identity. Verify supported authentication for the installed version and chosen route. Current Core source supports APPLICATIONINSIGHTS_AUTH_MODE=entra using managed identity for Azure Monitor export. Direct authenticated Application Insights ingestion requires the actual emitter’s applicable permission on the component; native OTLP collector ingestion requires the collector identity’s Monitoring Metrics Publisher role on the DCR. A remote collector can have a different identity from the agent. Recheck actual principals after deployment and defer current role/resource mechanics to microsoft-foundry; do not grant both scopes indiscriminately.
Preserve distributed trace context. Keep session creation/resume/send under the intended active hosting-request span. With OpenTelemetry available and configured, v1.0.14 Python propagates W3C traceparent/tracestate into the runtime and restores context around tool callbacks. Preserve context through supported MCP transports or owned boundary instrumentation. Test concurrent requests for correct trace/parent association; never use a global mutable trace-header dictionary. W3C tracing is separate from x-agent-foundry-call-id authentication context; neither substitutes for the other. Do not fabricate remote child spans as execution evidence.
Keep content private. Keep Copilot capture_content=False and hosting sensitive-data capture disabled unless explicitly approved with redaction and retention controls. These options do not sanitize explicit application, adapter or tool logs: avoid prompts, completions, tool arguments/results, credentials and opaque caller-context values. Record safe identifiers, status, duration and available usage metadata instead.
Verify delivery and lifecycle. Start instrumentation before requests and shut down the runtime/flush owned exporters through supported graceful-lifecycle APIs. Record sampling and ensure checkpoint probes are retained without silently changing production policy. Allow bounded batching/ingestion delay, then require the deployed probe’s hosting, model and required tool spans with correct trace/parent links and agent/version identification. Follow the Foundry trace workflow: for its Application Insights schema, start from requests filtered by agent name and join dependencies by operation_Id. Verify the actual schema for native OTLP ingestion instead of assuming those tables. Host-only spans, exporter startup and local mocks do not satisfy the checkpoint.
If spans are missing, inspect producer enablement, active context, receiver reachability, destination/authentication, sampling and flush/ingestion delay before changing code or infrastructure. Report the failed boundary and keep the checkpoint blocked; do not redeploy an unchanged artifact to wait for telemetry.
Remote build remains the default. When server-side dependency resolution fails, first capture the restore error and correct invalid/incompatible requirements. For access/feed failures, follow the approved package-feed guidance; do not disable TLS or integrity checks. If remote resolution still cannot work, use the supported bundled source-code deployment mode through microsoft-foundry, not an unrequested container-image migration.
Export the approved lockfile to a fully pinned, hashed requirements file, including transitive dependencies and the target Linux wheel hashes. Preserve private feeds/pins. Work in a fresh, task-owned staging directory; do not reuse a macOS/Windows virtualenv.
For the currently documented Linux x86_64 Python 3.13 runtime, install extracted modules into packages/:
python3.13 -m pip install --require-hashes -r requirements.lock \
--target packages/ --platform manylinux2014_x86_64 \
--python-version 3.13 --implementation cp --only-binary=:all:requirements.lock here is the exported pip-compatible hash-locked file. If required wheels are unavailable, report the package/platform blocker; use a reproducible matching Linux build with verified artifacts rather than silently dropping hashes or shipping host-native binaries.
Validate imports and runtime startup in matching Linux/Python 3.13. The Copilot runtime/native assets are separate from Python wheels: resolve the SDK-supported Linux runtime, preserve executable permissions, and record its version/checksum. Do not bundle the developer’s desktop binary or depend on an untested first-start download.
Produce a flat deployment archive with entry point, application sources, locked requirements, required assets and extracted packages/ at the root. Set runtime: python_3_13 and dependency_resolution: bundled using the current platform/tool schema. Inspect the archive and confirm packaging ignore rules did not remove packages/; compute and verify the deployed ZIP hash.
Exclude staging directories, packages/, runtime binaries and deployment archives from Git and pytest discovery (testpaths/norecursedirs or the repository’s equivalent), but explicitly include required artifacts in the deployment ZIP. Keep the lockfile and packaging instructions in Git. Exclude .env, caches, credentials and unrelated files from the archive.
Redeploy this changed artifact once, rerun the hosted checkpoint, and record the reason for bundling plus versions/hash. A successful upload or active state is not acceptance. Remove only task-created staging/debug resources afterward.