* fix: require explicit TokenCredential in AddFoundryToolboxes
The AddFoundryToolboxes extension methods now require callers to
pass a TokenCredential explicitly rather than relying on an
internally-created default credential. This makes the credential
choice intentional and avoids non-deterministic credential probing
in production environments.
Breaking change (experimental API):
- AddFoundryToolboxes(IServiceCollection, params string[]) becomes
AddFoundryToolboxes(IServiceCollection, TokenCredential, params string[])
- AddFoundryToolboxes(IServiceCollection, Action?, params string[]) becomes
AddFoundryToolboxes(IServiceCollection, TokenCredential, Action?, params string[])
- Azure.Identity package dependency removed from Foundry.Hosting library.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: simplify redundant generic type argument (IDE0001)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: avoid duplicate FoundryToolboxService registration
Inject the AddFoundryToolboxes credential directly into the
FoundryToolboxService factory and fail early if the service was
already registered. This avoids registering TokenCredential in the
host DI container while preserving a single toolbox service instance
for both request handling and hosted startup.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: resolve CA1873 in GitHubCopilotAgent by using LoggerMessage source generator
Replace the direct logger.LogWarning() call (which eagerly evaluates
string.Join()) with a [LoggerMessage]-generated extension method in
GitHubCopilotAgentLogMessages.cs.
Fixes build error:
GitHubCopilotAgent.cs(580,13): error CA1873: Evaluation of this argument
may be expensive and unnecessary if logging is disabled
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fixing more dotnet samples
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Ben Thomas <25218250+alliscode@users.noreply.github.com>
* .NET: Bump Azure.AI.Projects to 2.1.0-alpha.20260629.1
Bumps Azure.AI.Projects beta.3 to alpha.20260629.1 and aligns transitive deps (System.ClientModel 1.14.0, Azure.Core 1.59.0, Msal 4.84.2). Adapts to renamed AgentSessionFiles APIs (Upload/GetAll/Delete, scoped GetAgentSessionFiles, SizeInBytes), AgentToolboxes (CreateVersion/Delete), and strongly typed toolbox tools (WebSearchToolboxTool, MCPToolboxTool). Adds azure-sdk public dev feed for prerelease restore.
* Use positional arg for AgentSessionFiles.DeleteAsync cleanup
* Move to Azure.AI.Projects 2.1.0-beta.4 (released beta)
Swaps the alpha daily build for the published 2.1.0-beta.4. Drops the azure-sdk public dev feed since beta.4 and its deps are on nuget.org. Beta.4 requires Azure.Core 1.60.0, which cascades the 10.0.8 servicing packages (Microsoft.Bcl.AsyncInterfaces, System.Diagnostics.DiagnosticSource, System.Text.Json, System.Threading.Channels, Microsoft.Extensions.DependencyInjection.Abstractions, Microsoft.Extensions.Logging.Abstractions) to 10.0.9.
* Reconcile Azure.Core 1.60.0 bump with merged main
Reverts the over-eager System.Threading.Channels 10.0.9 bump back to 10.0.8 (it was not part of the Azure.Core 1.60.0 cascade and caused a net472 MSB3277 conflict against the 10.0.8 that Microsoft.Extensions.AI pulls). Drops the now-obsolete Azure.Core VersionOverride=1.59.0 in HostedWorkflowHandoff (added on main to satisfy AgentServer while the central pin was lower); the central pin is now 1.60.0 which already satisfies the >=1.59.0 floor, and the override was downgrading this project below sibling projects (CS1705).
* Fix Hyperlight workspace link staging
Reject symlinks, Windows junctions, and reparse points during Hyperlight input staging, and harden output collection/cleanup against the same link types.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address Hyperlight staging review
Anchor workspace enumeration to the resolved root and avoid following links while classifying output cleanup entries.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Improve Hyperlight path resolve errors
Handle RuntimeError from path resolution alongside OSError when validating Hyperlight sandbox paths and report the source-root validation context in the error message.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Mark Hyperlight real sandbox tests as integration
Ensure Windows unit CI excludes real Hyperlight sandbox tests by applying the integration marker consistently.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Clean up Hyperlight integration sandboxes
Close real sandbox fixtures and provider-owned registries in Hyperlight integration tests so they do not rely on process teardown.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* .NET: Make Foundry Hosting resilient to missing user identity in local runs
AgentFrameworkResponseHandler threw InvalidOperationException (surfaced as a
500 on every request) when the isolation-key provider returned null, which
always happens locally because the platform x-agent-user-id header is absent.
Running a hosted image outside Foundry therefore failed out of the box.
The handler now branches on FoundryEnvironment.IsHosted: hosted stays strict
(null identity is still a hard error), but non-hosted (local docker run /
dotnet run) tolerates a null identity - per-user isolation is simply not
triggered, the request proceeds with userId null (no partition), and no
hosted context is stamped or validated.
Because local runs no longer need a fallback, the sample-side
DevTemporaryLocalUserIdProvider and AddDevTemporaryLocalContributorSetup are
removed from Hosted_Shared_Contributor_Setup and all sample Program.cs files.
To simulate distinct users locally, send an x-agent-user-id request header;
the default provider reads it exactly as it reads the platform-injected value.
The Memory sample smoke script now drives alice/bob against one container via
that header. AGENT_NAME defaults added to Hosted-ChatClientAgent and
Hosted-MemoryAgent so a hosted deploy (where AGENT_* is a reserved env var)
does not crash at startup.
Updates the two affected unit tests to assert the local-success path and
amends ADR 0031.
* Address review: correct isolation-guarantee and Memory-sample local docs
- AgentFrameworkResponseHandler: note the null/local case is unscoped/shared,
not fully partitioned per user.
- HostedSessionIsolationKeyProvider XML docs: phrase the non-null UserId rule as
a constraint on the returned-context case, since null is now allowed locally.
- Hosted-MemoryAgent: the PerUser() memory scope requires a resolved user, so a
local run needs an x-agent-user-id header; corrected the Program.cs comment
and README (removed the inaccurate "shared bucket locally" claim).
- Test: assert absence of any u-* per-user directory via a wildcard search
rather than checking for a literal "u-" directory.
* Python: [BREAKING] Extract caching from SkillsProvider into CachingSkillsSource decorator
Adds a composable CachingSkillsSource(DelegatingSkillsSource) decorator that caches the inner source's skills list, and rewires SkillsProvider to wrap its resolved source in it by default (skipped when disable_caching=True). Removes the provider's baked-in caching (_cached_context field and _get_or_create_context). Mirrors .NET #6768.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add ty ignore for dynamic _test_context attribute in skills test helper
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Stop skill discovery at skill boundaries
File-based skill discovery kept descending after finding a SKILL.md, which treated content nested beneath a skill boundary as an independent skill root. Return immediately after recording a directory that contains SKILL.md so everything below it stays part of that skill, and add a regression test with a nested SKILL.md.
Fixes#6682
* Python: Attach nested skill content to the parent skill
Removing the SKILL.md subdirectory skip in resource and script scanning so that content beneath a skill boundary is attached to that skill, and update the discovery docstring and the nested-skill test to match. Complements the discovery early-return so a nested SKILL.md is never treated as an independent skill root.
* .NET: Consolidate skill-source caching and make skill sources disposable
Move all caching into the generic CachingAgentSkillsSource decorator and
remove the duplicate inline cache from AgentMcpSkillsSource, so a single
cache layer governs skill fetching. Add RefreshInterval-based expiry to
CachingAgentSkillsSourceOptions.
Make AgentSkillsSource (and its decorators) IDisposable so pipelines can
release owned resources, and give AgentSkillsProvider an ownsSource flag
controlling whether it disposes the source it wraps. Provider convenience
constructors and the builder set ownsSource: true.
Serialize ArchiveEntryLoader's reconcile/extract/read of the shared on-disk
directory with a per-instance lock to prevent concurrent corruption.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* .NET: Fix IDE0032 by using an auto-property in test source
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* .NET: Make cancellation cache test deterministic
Ensure the first caller owns the fetch before the second caller queues, so
the cancellation-restart assertion is no longer race-prone.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* .NET: Throw ObjectDisposedException from CachingAgentSkillsSource after disposal
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* .NET: Document AgentSkillsProviderBuilder source ownership and single-build contract
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* .NET: Update API compatibility suppressions for AgentSkillsProvider ctor change
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* .NET: Add test asserting archive skill updates are observed after reconcile
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: SergeyMenshykh <SergeMenshikh@outlook.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Allow custom argument marshaling for skill scripts
Add an optional argument_marshaler hook so callers can plug in their own argument conversion logic for inline skill scripts. Supplied at the InlineSkillScript, InlineSkill, and ClassSkill levels; when omitted, behavior is unchanged. This supports backends (e.g. vLLM) that send tool-call arguments in a non-conforming shape such as a JSON string.
Port of .NET PR #6498. Closes#6543.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review feedback on skill argument marshaling
- Widen InlineSkillScript.run args to accept a raw str (the one place a marshaler-converted value is valid), and drop the now-unneeded type: ignore markers in tests.
- Constrain the SkillScriptArgumentMarshaler output type to dict | None so the type enforces the inline-script contract instead of a docstring note.
- Add a clear TypeError when a str reaches an inline script with no marshaler configured.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Rename SkillScriptArgumentMarshaler to SkillScriptArgumentParser
In Python 'marshalling' specifically connotes the stdlib marshal module, so the term is misleading here. Rename the type alias, the argument_parser parameter/attribute, docstrings, exports, and tests accordingly.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fold argument_parser docstring into Args section
The skill constructors are fully keyword-only, so name/description/function are already documented under Args. Singling out argument_parser into its own Keyword Args section was inconsistent; merge it into Args for InlineSkillScript, InlineSkill, and ClassSkill.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The auto-injection of InMemoryHistoryProvider was gated on there being no
context providers at all, so registering any non-history provider (e.g.
SkillsProvider, FileAccessProvider, or a RAG memory provider) suppressed local
history. On stateless clients this dropped prior messages across turns — most
visibly the tool-approval resume turn lost the prior assistant function_call,
causing a 400 "Expected toolResult blocks" error.
Gate the injection on the absence of a loading HistoryProvider instead, matching
the pattern already used in _workflows/_agent.py. Add regression tests covering
a non-history provider, an existing loading provider, and a persist-only
provider.
Fixes#5672
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Fix GeminiChatClient dropping image/file content
GeminiChatClient._convert_message_contents only handled text and function_call content, so data/uri (image, PDF, audio) parts were silently dropped and never reached Gemini. Convert data URIs to inline_data Parts and external URIs to file_data Parts, warning on genuinely unconvertible content. Adds tests for the multimodal conversion paths.
Fixes#6688
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review: strip data-URI mime params and handle non-inferable URIs
Strip parameters (e.g. charset) from a data URI media type before passing it to Gemini, and wrap types.Part.from_uri so a URI with no media_type and no guessable extension is passed through as file_data without a mime type instead of raising ValueError. Adds tests for both paths.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review: reuse shared data-URI helpers
Reuse _get_data_bytes and detect_media_type_from_base64 from agent_framework instead of reimplementing base64 extraction/decoding and data-URI header parsing in the Gemini client. This also removes the manual header parsing that previously needed charset-parameter stripping. Updates tests accordingly.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Hosting packages (hosting, hosting-responses, hosting-telegram) were excluded
from the 1.10.0 release but their entries remained in the CHANGELOG.
Also removes the core hosting channel entry since it's unreachable without
the hosting packages.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add per-agent and per-user session storage isolation for Foundry Hosting
Partitions hosted session and checkpoint files as {root}/a-{agentName}/u-{userId}/c-{contextId}.json so a container that serves multiple agents and multiple users cannot leak state across tenants. The user layer collapses to a-{agent}/c-{conv}.json when no x-agent-user-id is present (raw local). Adds a reject-style path-traversal guard (CWE-22) for the untrusted user id plus a resolve-and-assert-under-root containment check, and keeps the strict-resume 403 identity check as a second defense layer.
AgentSessionStore.GetSessionAsync/SaveSessionAsync take a required (nullable) userId so a caller can never silently persist a session unscoped; the handler resolves the user id before loading the session and threads it to both. Tool approvals ride in the session checkpoint (ToolApprovalIdMap to AgentSessionStateBag), so the partitioned path covers them and no separate approval store is needed. Renames the sample HOSTED_USER_ISOLATION_KEY env var to HOSTED_USER_ID and DevTemporaryLocalSessionIsolationKeyProvider to DevTemporaryLocalUserIdProvider. Documents the design in ADR 0031. Adds handler-driven multi-agent/multi-user file-system tests and store-level traversal/isolation tests.
* Fail fast with a clear 501 when hosted container is served responses protocol 1.0.0
A 2.0.0-only hosted image served container protocol 1.0.0 (no x-agent-foundry-call-id
header) previously threw and surfaced an opaque 500 on every request. It now returns a
clear 501 "unsupported_container_protocol_version" naming the required protocol.
* HostedProtocolCompatibility gate keyed on FoundryEnvironment.IsHosted plus
PlatformContext.CallId (the 2.0.0 exclusive marker); invoked before isolation resolution
* HostedProtocolCompatibilityTests unit coverage; AgentFrameworkResponseHandlerTests note
clarifies the non-hosted path
* UnsupportedProtocolHostedAgentTests integration test deploys a dedicated
it-unsupported-protocol agent as 1.0.0 and asserts the 501 (validated live on cace)
* TestContainer recognizes the unsupported-protocol scenario
* it-bootstrap-agents.ps1 placeholder default raised to responses 2.0.0 and adds the
it-unsupported-protocol agent; HostedAgentFixture protocol version is overridable
* Address PR review: whitespace protocol gate and InMemory store agent keying
* HostedProtocolCompatibility treats a whitespace-only x-agent-foundry-call-id as
absent (IsNullOrWhiteSpace) so a proxy injecting whitespace cannot bypass the gate;
unit test covers empty, spaces and tab
* InMemoryAgentSessionStore keys sessions by agent.Name (omitting the agent segment
when Name is unset), mirroring FileSystemAgentSessionStore, so session continuity
survives a recreated or transient agent rather than keying on the per-instance agent.Id
* Add AgentSkillsSourceContext to AgentSkillsSource.GetSkillsAsync
Pass agent/session context through the skills retrieval pipeline so
sources, filters, and caching can make context-aware decisions.
- AgentSkillsSourceContext (Agent, Session) is built by AgentSkillsProvider
from the InvokingContext and flows through all sources and decorators.
- FilteringAgentSkillsSource predicate now receives an AgentSkillFilterContext
bundling the skill and the source context.
- CachingAgentSkillsSource supports per-key isolation via
CachingAgentSkillsSourceOptions.CacheIsolationKeySelector; a null selector
preserves the shared-cache behavior.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Make AgentSkillsSourceContext constructor public and harden cache key
- Make the AgentSkillsSourceContext constructor public so external callers
can invoke AgentSkillsSource.GetSkillsAsync directly; drop the
Mcp.UnitTests InternalsVisibleTo entry it required.
- Use a dedicated sentinel cache key for the shared bucket so an isolation
selector returning an empty string gets its own bucket.
- Document cache-key cardinality guidance and baseline the experimental
API breaking changes in CompatibilitySuppressions.xml.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Drop AgentSkillFilterContext in favor of a two-argument filter predicate
Replace the AgentSkillFilterContext bundle with a
Func<AgentSkill, AgentSkillsSourceContext, bool> predicate in
FilteringAgentSkillsSource and AgentSkillsProviderBuilder.UseFilter, and
update the tests accordingly.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: SergeyMenshykh <SergeMenshikh@outlook.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: align GitHub Copilot approval to SDK on_pre_tool_use hook
Replace the bespoke on_function_approval enforcement in the GitHub Copilot provider with the Copilot SDK's native on_pre_tool_use hook. When no caller hook is supplied, a default hook returns 'ask' for approval_mode='always_require' tools (routed to on_permission_request) and defers others; a caller-supplied on_pre_tool_use takes precedence and logs a warning for any unenforced approval tool.
Fixes#6746
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix type-checker errors and restore load_dotenv in sample
Use a complete PreToolUseHookInput in on_pre_tool_use hook tests so pyright/pyrefly/ty/zuban no longer report missing required TypedDict keys. Restore load_dotenv() in the function-approval sample for consistency with the other GitHub Copilot samples (PR review feedback).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Deprecate on_function_approval instead of removing it
Per PR review feedback, keep the on_function_approval callback working (still enforced in the tool handler for approval_mode='always_require' tools) but emit a DeprecationWarning at construction, so existing users get a signal rather than a silent behavior change. The default on_pre_tool_use ask-hook is not installed when on_function_approval is set, avoiding double-gating. Precedence: user on_pre_tool_use > on_function_approval > default ask-hook. Adds tests for the deprecated path and documents it in the package README.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Make on_function_approval and on_pre_tool_use mutually exclusive
Per automated review feedback, instead of a precedence ordering between the deprecated on_function_approval callback and the new on_pre_tool_use hook (which silently double-gated when both were set), raise ValueError if both are supplied - at construction (both in default_options) or per run (per-run on_pre_tool_use with a construction-time on_function_approval). This matches the repo convention for deprecated-vs-new params (see _workflows/_workflow.py) and removes the flag-threading. Updates tests and the package README.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace dotnet/nuget/icon.png with the new Microsoft Foundry Agent Framework color logo (resized to 128x128, the NuGet-recommended icon size). Source: docs/assets/PNG/Microsoft Foundry Agent Framework - Color.png.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the direct logger.LogWarning() call (which eagerly evaluates
string.Join()) with a [LoggerMessage]-generated extension method in
GitHubCopilotAgentLogMessages.cs.
Fixes build error:
GitHubCopilotAgent.cs(580,13): error CA1873: Evaluation of this argument
may be expensive and unnecessary if logging is disabled
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the direct logger.LogWarning() call (which eagerly evaluates
string.Join()) with a [LoggerMessage]-generated extension method in
GitHubCopilotAgentLogMessages.cs.
Fixes build error:
GitHubCopilotAgent.cs(580,13): error CA1873: Evaluation of this argument
may be expensive and unnecessary if logging is disabled
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* .NET: Bump Azure.AI.AgentServer to 2.0.0 protocol and migrate Foundry.Hosting
Bumps Core .25->.26, Invocations .4->.5, Responses .5->.6 and adopts the 2.0.0 container protocol.
Breaking change: IsolationContext (UserIsolationKey + ChatIsolationKey) is replaced by PlatformContext (UserIdKey from x-agent-user-id, CallId from x-agent-foundry-call-id). The per-chat key is gone; HostedSessionContext is now user-only and the per-request CallId is forwarded outbound to Foundry first-party services (toolbox/MCP).
Also fixes a real call-id egress bug: AsyncLocal writes inside the streaming response iterator are reverted across yield boundaries, so the call id was dropped before the toolbox/MCP egress ran. The handler now re-applies HostedCallContext.CallId before each egress point.
Adds HostedConversationKey to map a request to a stable MAF AgentSession via conversation_id, else the partition key embedded in previous_response_id, else the minted response id. This keeps store=false previous_response_id chains and conversation_id forks on a single hosted MAF session without using the container session id.
Sample manifests bump the responses protocol to 2.0.0 (invocations stays 1.0.0). Integration tests split store/session semantics into HostedResponsesStoreConfigTests with its own scenario, read stored responses through the per-agent endpoint client, and inject the model deployment into the container.
* Pin Azure.Core 1.59.0 for Hosted-Workflow-Handoff sample
AgentServer 1.0.0-beta.26 (pulled transitively via Foundry.Hosting) requires Azure.Core 1.59.0. This sample disables transitive pinning and references Azure.Core directly, so override just this project to the SDK-required version without moving the solution-wide central pin.
* Add guard test for request-scoped call-id cleanup
Asserts HostedCallContext.CallId does not leak into the caller's execution context after CreateAsync's stream completes, while confirming the agent run still observed the call id. Documents the request-scoped contract and guards against stale-header leakage across requests handled on the same thread.
* Refresh hosting READMEs for AgentServer 2.0 migration
Updates stale docs to match the shipped code: the MemoryAgent README now describes the x-agent-user-id user-identity header (chat isolation key removed) feeding HostedSessionContext.UserId; the IntegrationTests README corrects the scenario count (six to eleven), adds the missing memory scenario row, and stops claiming all scenarios are skipped now that several are validated and active.
* Add ADR 0030 superseding 0026 for AgentServer 2.0 platform context
Documents the migration from ResponseContext.Isolation (UserIsolationKey/ChatIsolationKey) to ResponseContext.PlatformContext (UserIdKey/CallId): user-only HostedSessionContext, the request-scoped HostedCallContext call-id forwarded on egress, HostedConversationKey session keying, and removal of the PerChat/PerUserAndChat memory scopes. Marks ADR 0026 as superseded.
* Add breaking-change v2.0-only disclaimer to package metadata
Augments the package Description and adds PackageReleaseNotes stating this release targets the Foundry Responses container protocol v2.0 only, is not compatible with v1, and directs consumers to a previous release for the v1 protocol definition.
* Address review comments: dead chat-key surface and weak test assertions
Fixes the automated review findings: the MemoryAgent/AgentSkills .env.example now say one variable (only HOSTED_USER_ISOLATION_KEY remains); the MemoryAgent smoke script drops the unused ChatKey parameter and its call-site arguments; HostedConversationKey null test now exercises a real null (and whitespace); and the reuse-one-session test asserts an exact SessionCount of 1 instead of <= 1.
* Python: Add include_detailed_errors option for skill script execution
Port the .NET fix from #6680. SkillsProvider previously swallowed
exceptions from skill script execution and resource reading, returning a
generic error string so the model could not self-correct.
- Add an include_detailed_errors option to SkillsProvider.__init__ and
from_paths. When True, script-execution failures return an error string
with the exception message appended; when False (default), the exception
is logged and re-raised, delegating to the function-invocation pipeline's
own include_detailed_errors policy.
- _read_skill_resource now logs and re-raises instead of returning a
generic error string. Resources take no model arguments, so a swallowed
generic error is not actionable by the model.
- Update and add tests covering the new propagation and detailed-error
behavior.
Fixes#6681
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Re-raise skill script/resource errors instead of adding a provider option
Address PR review: returning a plain error string from the skill provider
bypassed the shared tool-error contract (no exception metadata, not counted
toward consecutive-error limits), risking infinite retries.
Instead of porting the .NET provider-level IncludeDetailedErrors option,
_run_skill_script and _read_skill_resource now always log and re-raise on
failure. This delegates error handling to the function-invocation pipeline,
whose existing include_detailed_errors policy is the Python equivalent of
.NET's FunctionInvokingChatClient.IncludeDetailedErrors and correctly
preserves exception metadata and consecutive-error counting.
Validation failures (empty/unknown skill, script, or resource names) still
return user-facing error strings. Tests updated accordingly.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: [BREAKING] Make all SkillsProvider tools require approval by default
All tools exposed by SkillsProvider (load_skill, read_skill_resource,
run_skill_script) now require approval by default. Previously only
run_skill_script could be gated, and only when require_script_approval=True.
- Register all three tools with approval_mode="always_require"
- Add read_only_tools_auto_approval_rule and all_tools_auto_approval_rule
static rules plus tool-name constants (mirrors FileAccessProvider)
- Remove the require_script_approval option from __init__ and from_paths
- Add skills_auto_approval sample; update script_approval sample/docs
Closes#6728
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR review: batch skill approval responses and tidy sample
- Collect a response for every approval request and send them in a single
agent.run so the approval loop always makes progress (no infinite loop when
a request lacks a function_call); reject non-function requests instead of
skipping them. Applied to both the skills_auto_approval and script_approval
samples.
- Extract ToolApprovalMiddleware into a local variable in skills_auto_approval
for readability.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR review: add approval handling to remaining skills samples
The secure-by-default change makes all SkillsProvider tools require approval,
which left the other skills samples emitting approval requests instead of the
documented answers. Add ToolApprovalMiddleware with the all-tools auto-approval
rule (and a session, which the middleware requires) so these samples run
unattended as before:
- code_defined_skill, file_based_skill, class_based_skill, mixed_skills,
skill_filtering, mcp_based_skill
- providers/foundry/foundry_chat_client_with_toolbox_skills
The dedicated script_approval (manual) and skills_auto_approval (selective)
samples continue to demonstrate interactive approval handling.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR review: simplify "host approval" wording to "approval"
Apply maintainer suggestions dropping "host" from the skill-approval
docstrings, and align the matching SkillsProvider docstring/AGENTS.md note for
consistency.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update agent-framework-azure-ai-search to work across the stable/GA azure-search-documents SDK (12.0.0, api-version 2026-04-01) and the preview SDK (12.1.0b1, api-version 2026-05-01-preview) for both semantic and agentic modes.
- Bump the dependency to azure-search-documents>=12.0.0,<13 and the package to 1.0.0b260618.
- Add an api_version parameter (threaded into SearchClient, SearchIndexClient, and KnowledgeBaseRetrievalClient) plus STABLE_API_VERSION/PREVIEW_API_VERSION constants, re-exported from agent_framework.azure.
- Auto-detect preview-only agentic features (output mode, low/medium reasoning effort) via _preview_features_active(), which requires both the preview SDK and a preview api-version; defaults (extractive + minimal) work on both channels and preview-only options raise an actionable error otherwise.
- Make knowledge-base imports SDK-version resilient and fix the 12.x surface (k -> k_nearest_neighbors, defensive additional_properties).
- Update tests (pass on both SDKs), docs, samples, CHANGELOG, and uv.lock.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add samples for the harness blog part 2
* Address PR comments
* Fix blog links.
* Address PR comments
* Fix bug where mode was incorrectly defaulted when reading the mode before the first run.
* Add reference to new sample readme
Ollama's `format` param only accepts '', 'json', or a JSON-schema dict, so
passing a Pydantic model class (the form OpenAIChatClient/FoundryChatClient and
create_harness_agent plan mode use) raised a ValidationError while building the
request. Convert a model class to its JSON schema when mapping response_format
-> format, keeping the original class for typed response parsing.
* Python: add GitHub MCP security label sample
* modified samples to create devui auth token, support debugging with security, and change context label only using the labels of unhidden result from tools
* FIDES: secure MCP labeling, _meta IFC parsing, and docs updates
* FIDES: secure MCP labeling, _meta IFC parsing, and docs updates
* modified docs
* fixed PR comments, simplified github_mcp example
* commented github_mcp example
* remove the parse_github_mcp_labels and fix the user_identity label propogation
* fix: use standard GitHub MCP endpoint with X-MCP-Features: ifc_labels instead of /insiders
- Switch MCP_URL from /mcp/insiders to /mcp/ in github_mcp_example.py
- Add MCP_HEADERS constant with X-MCP-Features: ifc_labels to opt-in to
server-side IFC label emission in _meta payloads
- Fix SecureMCPToolProxy to pass headers via httpx.AsyncClient so they are
included on session.initialize(), not just on tool calls (was causing 401
to silently surface as anyio cancel-scope CancelledError)
- Update README, FIDES_DEVELOPER_GUIDE, FIDES_IMPLEMENTATION_SUMMARY, and
0024-prompt-injection-defense.md to remove all /insiders references
* address PR comments
* Simplify GitHub MCP security sample to DevUI-only; document SecureAgentConfig quarantine client global behavior
* minor PR comments
* fixing failed checks
* fixing failed checks
---------
Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
* Python: bump package versions for 1.10.0 release
- Released cohort (core, openai, foundry, root): 1.9.0/1.8.2 -> 1.10.0
- agent-framework-ag-ui: rc5 -> rc6 (tool history replay fix)
- Beta/alpha packages with changes: anthropic, azurefunctions, bedrock,
durabletask, hyperlight, purview, foundry-hosting, gemini, hosting,
hosting-responses, hosting-telegram, tools bumped to new date stamp (260625)
- Inter-package dependency bounds updated for changed packages
- CHANGELOG.md updated with [1.10.0] section and compare links
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: update stale hosting dependency pins in hosting-responses and hosting-telegram
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* CI: cap xdist workers at 4 for Azure OpenAI and Functions integration jobs
The Azure OpenAI and Functions+Durable Task integration jobs ran with
`-n logical` (~20 workers on the hosted runner), oversubscribing the box and
collapsing the whole pytest session (all workers reporting `node down: Not
properly terminated`) in the merge queue. Pin these two jobs to `-n 4` in
python-merge-tests.yml and python-integration-tests.yml to remove the
oversubscription while keeping full coverage.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test: temporarily skip flaky Python integration tests crashing the merge queue
Revert the `-n 4` xdist experiment (it did not prevent the runner crash) and
instead skip the integration tests that collapse the pytest-xdist runner in the
merge queue (all workers report `node down: Not properly terminated`):
- Azure OpenAI: flip the per-file `skip_if_azure_openai_integration_tests_disabled`
guard to an unconditional skip (integration tests only; unit tests still run).
- Azure Functions / Durable Task: skip the four specific failing tests
(test_weather_agent, test_parallel_workflow_end_to_end, test_weather_agent_with_tool,
test_conditional_branching).
Tracked for re-enablement in #6777.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test: skip flaky test_math_agent_with_tool (durabletask integration)
Same empty-AgentResponse flakiness as test_weather_agent_with_tool in the same
file (AssertionError: assert 0 > 0 / empty .text). Skip it in the merge queue.
Tracked in #6777.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* .NET: Enforce ApprovalRequiredAIFunction in GitHub Copilot provider
The GitHub Copilot SDK owns the tool-calling loop and invokes registered
custom functions directly, so the standard FunctionInvokingChatClient
approval round-trip never runs for this provider. As a result a tool wrapped
in ApprovalRequiredAIFunction (only a marker) could execute without any
Agent Framework approval.
Add an agent-level onFunctionApproval callback and wrap approval-required
tools in an ApprovalGatedAIFunction that enforces approval before invoking
the underlying function. Secure-by-default: with no callback, or when the
callback denies or throws, execution is denied. The gate forwards tool
metadata (including the Copilot skip_permission flag) so it stays
transparent to the SDK. This mirrors the Python provider's behavior.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* .NET: Propagate cancellation from GitHub Copilot approval callback
Let OperationCanceledException propagate from the approval callback instead
of swallowing it into a denial, so cooperative cancellation is honored.
Other callback failures still deny by default. Added a unit test.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* .NET: Enforce ApprovalRequiredAIFunction via Copilot SDK OnPreToolUse hook
Replace the custom approval enforcement (ApprovalGatedAIFunction wrapper +
onFunctionApproval callback) with the GitHub Copilot SDK's native OnPreToolUse
hook, which the SDK already provides for pre-execution gating.
When a tool wrapped in ApprovalRequiredAIFunction is registered and the caller
hasn't supplied their own OnPreToolUse hook, the agent installs a default hook
that returns "ask" for those tools (routing the decision to OnPermissionRequest)
and defers (null) for all other tools, preserving today's behavior for
non-approval tools. If the caller supplies their own OnPreToolUse hook, it takes
precedence and they own approval handling; the agent logs a warning naming any
approval-required tool that will not be auto-gated, and the behavior is
documented. Adds an optional ILoggerFactory parameter for the warning.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* .NET: Address PR review feedback on GitHub Copilot approval hook
- Build the approval-required tool-name HashSet directly instead of via an
intermediate List.
- Remove the redundant MEAI001 NoWarn suppression (tests already suppress it via
.editorconfig and the source project builds clean without it).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Include the optional description attribute on <resource> and <script>
elements within <available_resources> and <available_scripts> blocks,
aligning .NET with the Python implementation. The description is emitted
only when non-null/non-empty and is XML-escaped.
Co-authored-by: Marco Minerva <marco.minerva@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move the cache-once-then-replay logic out of AgentSkillsProvider into a
new CachingAgentSkillsSource decorator following the DelegatingAgentSkillsSource
pattern used by DeduplicatingAgentSkillsSource and FilteringAgentSkillsSource.
- Add internal CachingAgentSkillsSource (lock-free, thread-safe; clears on failure)
- AgentSkillsProviderBuilder applies caching after aggregation, before filter/dedup
- Add builder DisableCaching() opt-out method
- Convenience constructors wrap with CachingAgentSkillsSource before dedup
- Remove DisableCaching from AgentSkillsProviderOptions
- Add CachingAgentSkillsSourceTests
Co-authored-by: SergeyMenshykh <SergeMenshikh@outlook.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fixing some samples and sample verification.
* Workaround for continuation token moved to sample.
* Address PR review comments: reset _stdinEof on reuse, null-guard modelId, format
- WorkflowRunner: reset _stdinEof=false at start of ExecuteAsync so reused
instances don't exit immediately on the next external request
- 04_memory: throw clear InvalidOperationException when DefaultModelId is null
rather than silently sending null to the Foundry Responses API
- dotnet format: no code changes, formatting only
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Improving memory sample by not creating an agent just to get a chat client.
---------
Co-authored-by: Ben Thomas <25218250+alliscode@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Prefer allocated HTTPS endpoints when resolving Aspire DevUI backends and fall back to HTTP for existing services. Update the DevUI Aspire sample so WriterAgent exercises HTTPS redirection.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Switch the remaining MAF-specific [Experimental(OPENAI001)] usages in Microsoft.Agents.AI.Foundry.Hosting to MAAI001 (AgentsAIExperiments). None of these public types surface an OpenAI experimental type, so OPENAI001 was a copy-paste inconsistency; MAAI001 is the correct id for MAF hosting/agent abstractions.
Fixes#6742
* .NET: Foundry hosted-agent toolbox OAuth consent support
Add per-user OAuth (MCP CONSENT_REQUIRED) support for Foundry hosted agents.
* Defer hard toolbox startup failures so a per-user OAuth-gated toolbox no
longer bricks the container at startup (new Degraded status, retried per
request). The container stays routable and surfaces consent on the first
user request.
* Emit the platform-canonical oauth_consent_request output item (instead of
mcp_approval_request) for toolbox OAuth consent, matching the Python
implementation and how the Foundry platform heads render consent.
* Parse the toolbox CONSENT_REQUIRED (-32006) error and surface the consent
link; resume by re-sending the prompt with no reply item needed.
* Add the Hosted-Toolbox-AuthPaths OAuth consent REPL client sample that
detects oauth_consent_request, prints the consent link, and re-sends.
* Add tests for the consent parser, startup deferral, and oauth_consent_request
emission.
Fixes#6562
* .NET: Address review feedback on toolbox OAuth consent
* Make RecomputeStatus the single source that refreshes ConsentRequiredToolboxNames
from the pending-consent set, so a per-request marker that records consent via
GetToolboxToolsAsync no longer leaves ConsentRequiredToolboxNames stale (which
made ResolvePendingConsentsAsync skip surfacing it).
* Surface lazy / per-request marker consent in the same request: after resolving
markers the handler now emits oauth_consent_request + incomplete when a marker
hit CONSENT_REQUIRED, instead of silently running without that toolbox.
* Add FoundryToolboxService.GetPendingConsents() snapshot accessor.
* Fix stale ToolboxConsentParser doc comment (mcp_approval_request -> oauth_consent_request).
* .NET: Harden toolbox consent paths from code review
* Thread-safety: GetPendingConsents() now returns an immutable snapshot rebuilt
in RecomputeStatus under the lock, instead of enumerating the live
_pendingConsents dictionary off-lock (which could throw under concurrent requests).
* Resource leak: OpenToolboxAsync builds the endpoint Uri before allocating the
HttpClient and now disposes the HttpClient when McpClient.CreateAsync throws
(the unreachable/deferred case retried per request), not only when ListToolsAsync fails.
* StrictMode now gates on the pre-registered ToolboxNames set rather than the
opened-toolbox cache, so a registered-but-deferred toolbox is no longer rejected
as unknown.
* Sample REPL: the legacy approval-args consent fallback only reads the explicit
consent_url key, so a normal function-tool approval carrying a URL argument is
not misread as an OAuth consent request.
* .NET: Scope per-request toolbox marker consent to the request
Addresses review feedback that a marker-originated toolbox could leak into global
scope after consent. GetToolboxToolsAsync now returns a request-scoped
ToolboxResolution (tools or consent requirements) instead of recording marker
consent in the container-global _pendingConsents and appending resolved tools to
the service-wide Tools list.
* Marker consent is surfaced as oauth_consent_request for the requesting turn only
and collected in the handler's marker loop; it no longer injects tools into, or
raises a consent prompt on, a later request that did not reference the marker.
* Marker resolution no longer flips the container StartupStatus to ConsentRequired
(per-request markers must not affect readiness, per the StartupStatus contract).
* Remove the now-unused GetPendingConsents()/snapshot path; _pendingConsents is once
again exclusively the pre-registered/startup consent set.
* .NET: Add consent request-scoping UTs and an OAuth consent integration test
Unit tests (Microsoft.Agents.AI.Foundry.Hosting.UnitTests):
* New FoundryToolboxMarkerScopingTests proves per-request marker resolution is
request-scoped: a marker consent is returned to the caller without mutating
ConsentRequiredToolboxNames, StartupStatus, or the service-wide Tools cache, and
marker-resolved tools are returned to the caller rather than injected globally
(so a request with no marker sees neither the tools nor the consent).
* Adds a test-only ToolboxOpener seam on FoundryToolboxService so the consent/tools
resolution can be exercised without a live MCP proxy. Makes ToolboxOpenResult and
CachedToolbox internal (CachedToolbox.Client nullable, guarded at dispose).
Integration test (Foundry.Hosting.IntegrationTests):
* New toolbox-oauth-consent scenario wired into the TestContainer (pre-registers a
Foundry toolbox via AddFoundryToolboxes from IT_TOOLBOX_NAME), a
ToolboxOAuthConsentHostedAgentFixture, and a ToolboxOAuthConsentHostedAgentTests
that invokes the deployed agent and asserts the consumer captures an
oauth_consent_request consent link (container stays routable, no 424). Skipped by
default per the IT convention; documents the consent-gated toolbox prerequisite.
* Adds the scenario to it-bootstrap-agents.ps1 and the README scenario table.
* Refactor runner/workflow responsibilities, add concurrency guards, and fix checkpoint ancestry bug
Move runner-state ownership out of Workflow into Runner for clearer responsibilities. Add a weakref-based concurrent-run guard in Workflow and fix the stream-drop race in run_until_convergence. Fix the checkpoint ancestry bug by tracking the previous checkpoint id as runner instance state so parent pointers persist across resumed runs. Move Runner to a deprecated lazy __getattr__ export (backward-compatible with DeprecationWarning) and export CheckpointID.
* Scope runtime checkpoint storage to its owning run
Close the stream-drop race where a dropped run's deferred async-generator finalizer could leave a runtime checkpoint storage override set (inherited by a new run) or clear a successor run's storage. run() now defensively clears any stale override before starting, and _run_core only clears the override if this run still owns it (mirroring the _active_run ownership guard). Adds regression tests for both the inheritance and clobber cases.
* Collapse runtime-storage ownership into the active-run weakref
_runtime_storage_owner always held the same weakref as _active_run, so the two ownership conditions were equivalent. Derive ownership from a single owns_run = (_active_run is my_active_run) captured before the active-run clear, and remove the redundant field. No behavior change.
* Nest runtime-storage clear under the owns_run guard
Both the active-run release and the runtime-storage clear are gated on owns_run, so fold the storage clear inside the if owns_run block. No behavior change.
* Reset resume flag in a finally so it can't leak across runs
_resumed_from_checkpoint was only cleared on the success path of run_until_convergence, so a failure during a resumed run (e.g. executor failure) left it True. The next fresh run then skipped the superstep-0 checkpoint and parented later checkpoints to the stale resume point. Move the reset into a finally. Add a regression test that fails a resumed run via an executor error and asserts the next fresh run creates the superstep-0 checkpoint.
* Fix tests and formatting
* Fix formatting
* Address comments
* Update type ignore statements
* Make all AgentSkillsProvider tools require approval by default
- Wrap all tools (load_skill, read_skill_resource, run_skill_script) with
ApprovalRequiredAIFunction unconditionally
- Add ReadOnlyToolsAutoApprovalRule and AllToolsAutoApprovalRule static
properties following the FileAccessProvider pattern
- Remove ScriptApproval from AgentSkillsProviderOptions and
UseScriptApproval from AgentSkillsProviderBuilder
- Add Agent_Step07_SkillsAutoApproval sample
Closes#6727
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add UseToolApproval to hosted AgentSkills scenarios
Wire AllToolsAutoApprovalRule into the integration test container and
the Hosted-AgentSkills sample so skill tools execute without blocking
on approval when no interactive approval handler is configured.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add API compatibility suppressions for removed ScriptApproval members
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: SergeyMenshykh <SergeMenshikh@outlook.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Embed resource and script instruction text directly in the default
prompt template instead of using placeholder substitution. Custom
templates now only need the {skills} placeholder.
Co-authored-by: SergeyMenshykh <SergeMenshikh@outlook.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Add Telegram channel for agent-framework-hosting
- Add agent-framework-hosting-telegram package with TelegramChannel
supporting polling and webhook transports, streaming edits with
Telegram Bot API rate limiting, per-chat serial workers, and
multi-modal inbound/outbound (text, photo, document, voice)
- Add local_telegram sample demonstrating multi-channel hosting with
a TelegramChannel alongside ResponsesChannel, using per-chat
FileHistoryProvider and a run_hook for Telegram persona temperature
- Fix test layout: move tests to tests/hosting_telegram/ (no __init__.py)
- Remove old [tool.mypy] section and mypy poe task; source type-checking
is handled by pyright via shared_tasks
- Update uv.lock, pyproject.toml workspace sources, and PACKAGE_STATUS.md
Fixes#6588
Refs #6265
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Address Telegram channel CI failures and review feedback
- Fix webhook secret validation to use constant-time compare_digest
- Harden webhook update parsing: require integer chat IDs and guard slash-only commands
- Fix streaming edge cases in TelegramChannel:
- prevent edit worker deadlocks when text exceeds 4096 chars
- prevent deadlock when placeholder send fails (message_id stays None)
- enforce edit throttling with minimum interval sleep
- honor send_typing_action=False in streaming mode
- always forward final multimodal output (e.g. images), while avoiding duplicate text sends
- Expand Telegram tests for slash-only command handling, non-int chat IDs, and streaming behavior (long text, final images, typing toggle)
- Fix sample/docs feedback:
- rename sample package to agent-framework-hosting-sample-local-telegram
- switch sample uv.sources from feature branch to main
- align docs/tool names with lookup_weather
- fix broken links and server run instructions in README/call_server.py
- align local_telegram app docstrings with reasoning hook behavior and strip model in responses_hook
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Fix TelegramChannel streaming to iterate contents for multimodal support
- Remove stale PR reference from module docstring
- Add Google-style docstring to TelegramChannel.__init__ documenting all keyword args
- Fix _stream_to_chat to iterate update.contents instead of using
getattr(update, 'text', None); text chunks are extracted from Content
items with type='text', non-text content in updates is correctly
ignored (images etc. are forwarded via the final response)
- Update _FakeStreamUpdate test helper to use contents list matching the
real AgentResponseUpdate API; add from_text/from_image class methods
- Update _FakeResponseStream to accept _FakeStreamUpdate objects directly
- Add test verifying multimodal stream updates don't corrupt text accumulator
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Split local_telegram into simple Telegram-only and new multi-channel sample
local_telegram is now a focused Telegram-only sample:
- Removes ResponsesChannel and all responses_hook code
- Removes call_server.py (no HTTP endpoint to call)
- Uses a deterministic lookup_weather tool (hash-based, not random)
- Single run_hook that strips model and raises reasoning effort
- Drops agent-framework-hosting-responses dependency
New local_multi_channel sample shows running both channels at once:
- ResponsesChannel + TelegramChannel sharing a FileHistoryProvider
- Cross-channel session resumption via previous_response_id
- call_server.py moved here (the Responses endpoint lives here now)
- Demonstrates the multi-channel coordination story
Update README table to list both samples with clear descriptions.
Also delete personal_assistant/.venv which was not tracked but caused
pyright to crawl the entire installed venv (thousands of files),
making sample pyright checks hang indefinitely.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Fallback when Telegram final edit fails
- only mark final edit as sent after a confirmed 2xx edit response
- fall back to sendMessage when final edit returns a non-success status
- add regression test covering failed final edit fallback behavior
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Fix optional await_args typing in telegram test
- assert await_args is not None before reading kwargs in streaming fallback test
- resolves test-typing failures across mypy/pyright/ty/zuban for hosting-telegram
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: [Breaking] Refactor FileSkillsSource for depth-based discovery and predicate filters
Refactors FileSkillsSource to make script and resource discovery more flexible.
## Changes
- **Drops** resource_directories / script_directories options (preconfigured
directory whitelists).
- **Adds** search_depth option (>= 1, default 2): controls how deep the
recursive scan goes within each skill directory.
- **Adds** script_filter / resource_filter predicate options that receive a
FileSkillFilterContext (skill_name + relative_file_path), allowing
whitelist/blacklist filtering by file path.
- **Adds** FileSkillFilterContext class exported from agent_framework.
## Notes
- The Skills API is marked @experimental -- the option removals are intentional
breaking changes within the experimental surface.
- Security checks (path containment, symlink detection) are preserved and
continue to use the skill root directory as the trusted boundary.
- Ports the same refactoring from .NET PR #6109 while following Python
conventions (instance methods, Callable type hints, __slots__).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR feedback: clarify depth constants and skip nested skill directories
- Add clarifying comments distinguishing MAX_SEARCH_DEPTH (SKILL.md
discovery) from DEFAULT_SEARCH_DEPTH (per-skill resource/script scanning).
- Stop recursing into subdirectories that contain their own SKILL.md,
preventing child skill files from being attached to the parent skill.
- Add test verifying nested skill boundary is respected.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Remove __slots__ from FileSkillFilterContext and add type-ignore comments
- Remove __slots__ from FileSkillFilterContext per reviewer feedback —
the optimization is negligible and inconsistent with sibling classes.
- Add type: ignore[attr-defined] / ty: ignore[unresolved-attribute]
comments to test lines accessing private _resources/_scripts attributes,
matching the convention established on main.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Simplify filter predicates: remove FileSkillFilterContext, use Callable[[str, str], bool]
Address reviewer feedback:
- Remove FileSkillFilterContext class — a dedicated class for two strings
is overkill in Python. Filters now receive (skill_name, relative_file_path)
directly as positional args.
- Update docstrings to describe behavior instead of referencing private
instance attributes.
- Remove FileSkillFilterContext from exports and __all__.
- Update all test lambdas and remove TestFileSkillFilterContext class.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Use DEFAULT_SEARCH_DEPTH as default argument directly
Instead of accepting int | None and resolving None to the default
internally, use DEFAULT_SEARCH_DEPTH as the parameter default value
on both FileSkillsSource.__init__() and SkillsProvider.from_paths().
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Migrate 02-agents/Agents samples to AIProjectClient (Foundry)
Replace AzureOpenAIClient with AIProjectClient as the AI provider in all
02-agents/Agents samples, aligning with the Foundry-first approach.
Changes:
- 19 Program.cs files migrated to use AIProjectClient.AsAIAgent()
- 19 .csproj files updated (Azure.AI.OpenAI -> Microsoft.Agents.AI.Foundry)
- Environment variables: AZURE_OPENAI_* -> FOUNDRY_PROJECT_ENDPOINT/FOUNDRY_MODEL
- Updated description comments to reflect Foundry backend
- Provider-specific samples in AgentsWithFoundry/ intentionally unchanged
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Migrate 02-agents/AgentSkills, AgentWithMemory, AgentWithRAG, AgentOpenTelemetry to AIProjectClient
Replace AzureOpenAIClient with AIProjectClient as the AI provider.
Environment variables: AZURE_OPENAI_* -> FOUNDRY_PROJECT_ENDPOINT/FOUNDRY_MODEL.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Migrate 03-workflows samples to AIProjectClient (Foundry)
Replace AzureOpenAIClient with AIProjectClient as the AI provider in
all 03-workflows samples that use an AI model.
Environment variables: AZURE_OPENAI_* -> FOUNDRY_PROJECT_ENDPOINT/FOUNDRY_MODEL.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix PR 6557 build breaks and align Foundry client usage
- Add explicit Azure.Identity package references to migrated sample projects
that use DefaultAzureCredential
- Fix AgentWithRAG_Step05_Neo4jGraphRAG to use AIProjectClient.AsAIAgent()
with ChatOptions.ModelId instead of AIProjectClient.AsIChatClient()
- Keep migrated samples on AIProjectClient pattern (no FoundryAgent/AzureOpenAIClient)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR 6557 Foundry review follow-ups
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix post-rebase sample build and format regressions
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Updates to fix issues from switching to Responses.
* Fixing more tests and deleting checkpoint directories created for samples.
* Fixing formatting
* Restore DefaultAzureCredential warnings in agents samples
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* .NET: Fix hosted agent crash after tool call by rooting session store under $HOME
FileSystemAgentSessionStore.CreateDefault rooted the hosted session store at the
filesystem root "/.checkpoints", which is read-only inside a Foundry hosted
container. After a local tool call the response handler persists the session, so
the write to "/.checkpoints" threw IOException and tore down the container, which
the platform surfaced as "mount: /app: mount failed: No such file or directory.".
Root the hosted store at $HOME (default /home/session), the only writable and
durable location per the container image spec. Persistence failures stay fatal but
are now wrapped in a clear, actionable IOException instead of the opaque raw error.
Add unit tests covering hosted and local path resolution plus the clear error, and
enable the ToolCalling Foundry Hosted Agents integration tests (verified live).
Fixes#6231
* .NET: Harden hosted session store against a filesystem-root HOME
Address review feedback on #6714: a misconfigured HOME pointing at a filesystem
root (e.g. "/") resolved back to "/.checkpoints" and would reintroduce the original
read-only-root crash. CreateDefault now falls back to the default session-data
directory (/home/session) when HOME is missing, blank, a filesystem root, or an
unnormalizable path. Adds a unit test locking in the "never the filesystem root"
behavior for a hosted HOME of "/".
Related #6231
* Python: surface Gemini cached and thinking token counts in usage details
* Python: surface Bedrock cache token counts in usage details
* Python: surface Gemini cached and thinking token counts in usage details
* Python: surface Bedrock cache token counts in usage details
* Return None from Bedrock _parse_usage when no token counts are present
Matches the UsageDetails | None return annotation and the Gemini
connector's behavior, so a usage payload with no recognized keys no
longer propagates an empty mapping. Adds a regression test.
Skill content now always emits <available_resources> and <available_scripts>
blocks, using self-closing elements when empty, so models receive an
authoritative list per category and do not hallucinate resource/script names.
FileSkill now also emits its resources block.
Closes#6348
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* .NET Foundry: add CreateMcpTool projectConnectionId overload
Adds FoundryAITool.CreateMcpTool(serverLabel, serverUri, projectConnectionId, ...)
so hosted MCP tools can authenticate through a Foundry project connection, matching
the Python FoundryChatClient.get_mcp_tool(..., project_connection_id=...) factory.
The connection id is applied via the McpTool.ProjectConnectionId extension that ships
in Azure.AI.Projects.Agents (patches project_connection_id), already referenced by the
Foundry package. Includes unit tests and sample/README guidance plus the existing
FromResponseTool workaround.
* Fold projectConnectionId into existing CreateMcpTool overload
Replaces the separate project-connection overload with an optional
projectConnectionId parameter on the existing serverUri CreateMcpTool, so all
settings (authorizationToken, headers, allowedTools, ...) stay available and there
is no positional overload ambiguity. Adds tests for the default (no connection)
path and for preserving other settings. Sample/README now show only the supported
overload.
* NET: Support archive-type skills in AgentMcpSkillsSource
Add archive-type skill discovery to the MCP skills source. Index entries
are dispatched to per-type loaders (skill-md and archive) via a new
IMcpSkillEntryLoader strategy. The archive loader downloads, safely
unpacks, and serves packaged skills through an internal file skills
source, while ensuring MCP-delivered scripts are never executed.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix CS0121 ambiguity in UseSource null test
Cast null! to AgentSkillsSource to disambiguate from the new
Func<ILoggerFactory?, AgentSkillsSource> overload.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR review: fix misleading comment and catch UnauthorizedAccessException in Dispose
- Remove hardcoded '50' from test comment; it now says 'default cap'
without citing a specific number that can drift from the constant.
- Catch UnauthorizedAccessException alongside IOException in test
Dispose for robust cleanup.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Decouple shared refresh from per-caller cancellation
Use CancellationToken.None for the shared refresh so one caller's
cancellation does not abort work for all concurrent waiters. Waiters
use WaitAsync(cancellationToken) to cancel independently. The refresh
owner checks its own token after publishing the result.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix file encoding: add UTF-8 BOM to archive tests
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix file encoding: add UTF-8 BOM to ArchiveFormat.cs
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Clarify pruning doc: covers non-actionable entries too
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add branch-coverage tests and drop [Experimental] attribute
- Add 5 unit tests covering FilterValidEntries/download condition branches
(missing name, invalid name chars, missing url, unsupported format, text-only blob)
- Remove [Experimental] attribute from AgentMcpSkillsSourceOptions (alpha package suffices)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: SergeyMenshykh <SergeMenshikh@outlook.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: propagate skill script/resource exceptions instead of swallowing them
Stop catching and returning generic error strings in RunSkillScriptAsync and
ReadSkillResourceAsync. Exceptions are now logged and rethrown so that
FunctionInvokingChatClient can decide whether to surface details to the model
via its existing IncludeDetailedErrors option (default: safe generic message).
Fixesmicrosoft/agent-framework#6304
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add IncludeDetailedErrors option for skill script execution
Add an IncludeDetailedErrors option to AgentSkillsProviderOptions. When enabled,
RunSkillScriptAsync appends the exception message to the error returned to the
model so it can self-correct (e.g. retry with different arguments). When
disabled (default), the exception is logged and rethrown, letting
FunctionInvokingChatClient apply its own IncludeDetailedErrors policy.
ReadSkillResourceAsync now logs and rethrows as well, since resources take no
arguments and a generic swallowed error is not actionable by the model.
Fixesmicrosoft/agent-framework#6304
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add prompt-injection caution to IncludeDetailedErrors doc
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: SergeyMenshykh <SergeMenshikh@outlook.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix SearchDirectoriesForSkills to stop recursing after finding SKILL.md
When a directory contains SKILL.md, subdirectories are part of that skill
and should not be treated as independent skill roots. Add a return after
adding the directory to results to prevent incorrect recursion.
Also adds a regression test verifying nested SKILL.md files are not
discovered as separate skills.
Fixesmicrosoft/agent-framework#6683
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix test: use matching directory name so nested SKILL.md would pass validation
The child skill's frontmatter name must match its directory name,
otherwise it gets rejected by validation regardless of the recursion fix.
This ensures the test actually validates the stop-recursing behavior.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: SergeyMenshykh <SergeMenshikh@outlook.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Purview: prefer token principal for user identity
Align Purview middleware identity resolution so user-token principals are preferred before supplied message identities, while app-token flows continue to use validated fallback user IDs. Also fix the content activities user route and add regression coverage for identity precedence and route construction.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* .NET: Fix user ID resolution logic in ScopedContentProcessor and add unit test for empty token user ID
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add sample for per-run refreshable MCP authentication headers
Adds a Foundry RAPI sample that attaches per-run, refreshable authentication headers to MCP requests using existing primitives: a DelegatingHandler on the MCP transport's HttpClient plus an AsyncLocal run scope. The same agent runs under two contexts, each minting a fresh token, proving the header is per run rather than bound at agent or connection creation time.
The handler attaches the bearer only over HTTPS to the MCP server's own origin, logs the non-secret label only, disables cookies, and checks certificate revocation. The README covers security considerations and production notes.
Fixes#1631
* Address PR review: harden redirect handling, nest-safe scope, README env vars
Disable AllowAutoRedirect on the shared handler so a redirect cannot carry the bearer past the origin check. Save and restore the prior run scope instead of clearing to null so the helper is safe under nesting. Note the Foundry env vars in the samples folder README row and update the sample README security notes.
* Require approvals for file-access and expose auto approval funcs for it
* Scope file-access auto-approval rules to local tools; fix base-Agent sample
Address PR #6599 review feedback:
- read_only/all_tools auto-approval rules now reject any call carrying a
server_label so they stay scoped to FileAccessProvider's local tools and
never auto-approve a same-named hosted tool.
- Expand the FileAccessProvider docstring to explain the runtime effect of
approval_mode="always_require" and point to ToolApprovalMiddleware /
create_harness_agent.
- Fix the base-Agent file_access_data_processing sample, which would otherwise
stop executing file tools under the new always_require defaults, by adding
ToolApprovalMiddleware with all_tools_auto_approval_rule.
- Add tests covering hosted (server_label) calls and update docs.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Clean up comments
* Update sample after merge
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* .NET: Explicitly emit available_resources and available_scripts in skill content
AgentInlineSkillContentBuilder now always emits <available_resources> and
<available_scripts> elements, using self-closing tags when a skill has no
resources or scripts. This signals to the model exactly what is callable so it
does not hallucinate non-existent resource or script names. Script parameter
schemas are wrapped in a nested <parameters_schema> element.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* .NET: Emit available_resources block for file-backed skills
Align AgentFileSkill with inline/class skills by surfacing discovered
resources in the loaded skill content. AgentFileSkill.GetContentAsync now
appends an <available_resources> block (before <available_scripts>) listing
resource names so the model has an authoritative list and does not
hallucinate resource names. Extracted a reusable BuildAvailableResourcesBlock
helper in AgentInlineSkillContentBuilder.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: SergeyMenshykh <SergeMenshikh@outlook.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Change A2A default session store to NoopAgentSessionStore
Align the A2A hosting layer default session store with the AG-UI
sibling by using NoopAgentSessionStore, making persistence an explicit
opt-in choice.
Update samples to document how to register a persistent session store
for multi-turn conversations.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Clarify test name to specify session store default
Rename test to FallsBackToNoopSessionStoreDefaultAsync to avoid
implying all stores default to noop (task store still uses InMemory).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
OpenTelemetryAgent auto-wired OpenTelemetryChatClient above FICC, producing
OTel(FICC(leaf)). FICC resolved its ActivitySource at construction time as null,
so execute_tool spans were never emitted for tool-calling agents.
This repositions OTel below FICC, producing FICC(OTel(leaf)), via a deferred
NoOp slot pre-placed as the innermost decorator in WithDefaultAgentMiddleware
and activated once at the agent level.
- Add internal DeferredOpenTelemetryChatClient: inert DelegatingChatClient whose
Activate(sourceName) swaps its target to inner.AsBuilder().UseOpenTelemetry().Build().
- WithDefaultAgentMiddleware always registers the slot innermost so it lands below FICC.
- OpenTelemetryAgent activates the slot once in its constructor and forwards run
options straight through, removing the per-run ChatClientFactory outer wrap.
- Add and update unit tests, including a proof that execute_tool spans are emitted
on the agent source and parented under invoke_agent.
* Add samples for harness blog post part 1
* Add readme for python samples
* Update python instructions to match dotnet instructions
* Address PR comments
* Add link to blog posts
* Fix blog post naming.
* Add more blog post links
* Project ToolExecution events as FunctionCallContent/FunctionResultContent
GitHubCopilotAgent's event-dispatch switch previously had no case for
ToolExecutionStartEvent or ToolExecutionCompleteEvent. Both fell through
to the default case and were wrapped as opaque AIContent with
RawRepresentation, preventing downstream consumers and models from
recognizing tool call results.
Add explicit cases that project:
- ToolExecutionStartEvent → FunctionCallContent (role: Assistant)
- ToolExecutionCompleteEvent → FunctionResultContent (role: Tool)
This mirrors the Python fix already shipped in #4734/#4814/#4828.
Fixes#5897
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(#5897): Address review feedback for ParseArguments robustness
- Handle non-generic IDictionary variants (Hashtable, etc.) that don't
match IDictionary<string, object?> due to generic invariance
- Return null for empty/whitespace string arguments instead of wrapping
them in a spurious { value = "" } dictionary, aligning with
ParseFunctionArgumentsObject convention elsewhere in the repo
- Add test coverage for Dictionary, Hashtable, and JsonElement argument
types
- Add edge-case test for Success=true with null Result
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix non-generic IDictionary key handling in ParseArguments (#5897)
Use direct (string) cast for dictionary keys instead of ToString()
coercion, matching the established pattern in ObjectExtensions and
PortableValueExtensions. This validates keys are actually strings
rather than silently accepting and coercing non-string keys.
Add test verifying non-string dictionary keys throw InvalidCastException.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix missing 'using System' in ToolExecutionEventProjectionTests
Add the missing 'using System' directive needed for InvalidCastException
reference at line 375 of ToolExecutionEventProjectionTests.cs.
Fixes#5897
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Use source-generated JsonTypeInfo for AOT-safe argument deserialization
Replace reflection-based JsonSerializer.Deserialize<T>() calls with the
JsonTypeInfo overload that uses source-generated metadata, eliminating
IL2026/IL3050 trimming and AOT warnings without suppressions.
Changes:
- Register Dictionary<string, object?> in GitHubCopilotJsonUtilities JsonContext
- Add JsonSerializerOptions constructor parameter (defaults to
GitHubCopilotJsonUtilities.DefaultOptions)
- Use GetTypeInfo()-based Deserialize overload in ParseArguments
- Remove [UnconditionalSuppressMessage] attributes
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix dotnet format: add 'this.' qualification to instance method call
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Adapt to GitHub.Copilot.SDK 1.0.0 API after merge with main
- Update ToolExecutionEventProjectionTests: Arguments is now JsonElement?
(not object?), remove tests for string/Dictionary/Hashtable arguments
- Remove AutoStart option (removed in 1.0.0)
- Simplify ParseArguments to handle JsonElement primarily
- Add tests for empty object and nested JSON arguments
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(hosting): emit url_citation annotation events from streamed AI Search responses
OutputConverter.ConvertUpdatesToEventsAsync accumulated text content deltas but
silently dropped CitationAnnotation metadata from TextContent.Annotations. As a
result, hosted agents that use CreateAzureAISearchTool emitted citation markers in
text (e.g. 【5:0†source】) but produced empty annotations arrays and no
response.output_text.annotation.added SSE events.
The fix accumulates UrlCitationBody SDK annotations across all TextContent updates
for a message and emits them via TextContentBuilder.EmitAnnotationAdded after
EmitTextDone (as required by the SDK lifecycle) and before EmitDone. Non-citation
and region-less annotations are silently skipped, matching the existing OpenAI
ChatCompletions path in AgentResponseExtensions.
Adds 7 unit tests (N-01–N-07) covering: basic emission, ordering constraints,
multiple annotations, multi-update accumulation, and skip conditions.
Fixes#6641
* test: convert annotation test comments to XmlDoc and group in region
* fix: remove redundant long casts on annotation region indices
* test: assert done events carry url_citation annotation metadata
---------
Co-authored-by: Roger Barreto <19890735+RogerBarreto@users.noreply.github.com>
* Bring Hosted-Toolbox sample to parity with sibling hosting samples
Adds the standard scaffolding files (.env.example, agent.yaml, agent.manifest.yaml,
Dockerfile, Dockerfile.contributor) that every other 04-hosting Foundry sample ships
but Hosted-Toolbox lacked.
Fixes the toolbox name environment variable: reads TOOLBOX_NAME instead of the
platform reserved FOUNDRY_TOOLBOX_NAME so it survives agent create, and aligns the
default to my-toolset.
Rewrites the README to the standard section layout with PowerShell fenced commands,
and adds Using-Samples READMEs documenting why the client REPLs exist.
Renames Azure AI Foundry to Foundry across the 04-hosting sample READMEs and comments
for consistent product naming.
* Address PR review: accurate docs and TOOLBOX_NAME in ToolboxMcpSkills
- SimpleAgent README: correct the demo banner to the real per-agent URL the
client prints (https scheme and the /api/projects/<project> segment).
- Hosted-Toolbox Program.cs: move FOUNDRY_MODEL out of the Required block into
Optional since it has a gpt-4o default and an AZURE_AI_MODEL_DEPLOYMENT_NAME
fallback.
- Hosted-ToolboxMcpSkills: switch the toolbox name from the reserved
FOUNDRY_TOOLBOX_NAME to TOOLBOX_NAME across Program.cs, .env.example,
agent.yaml, agent.manifest.yaml and README so it is deployable via the
manifest, matching the other toolbox samples.
* Python: harden Hyperlight output capture against symlinks
Mirror the input-staging symlink hardening on the output-capture path of
HyperlightExecuteCodeTool. Output discovery now walks via the symlink-safe
_iter_real_entries instead of rglob, per-file collection validates that no
path component is a symlink and the final entry is a regular file, and file
reads use os.O_NOFOLLOW. Adds regression tests for the output path.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review: reject traversal, fix listing test, harden read
- _is_safe_output_file now rejects '.'/'..' components (lexical relative_to
could otherwise escape root without a symlink)
- _read_output_file_bytes adds a cross-platform TOCTOU guard (lstat/fstat
st_dev+st_ino identity check) since O_NOFOLLOW is absent on Windows
- fix intermediate-dir-symlink test to use a relative listing path so it
exercises normalization + validation; add a parent-traversal unit test
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(durabletask): host MAF workflows on a standalone Durable Task worker
Add a host-agnostic workflow execution engine to agent-framework-durabletask so a MAF Workflow can run as a durable orchestration outside Azure Functions:
- WorkflowOrchestrationContext protocol + DurableTaskWorkflowContext adapter, the superstep orchestrator, serialization helpers, capturing runner context, and the shared non-agent activity body (including the yield-output classifier so intermediate executors are not surfaced as final outputs).
- DurableAIAgentWorker.configure_workflow auto-registers agent executors as entities, non-agent executors as activities, and the workflow orchestrator.
- plan_workflow_registration centralizes the 'what to register' decision so it can be shared across hosts.
- run_agent_coroutine runs all agent coroutines on one persistent event loop, fixing a cross-loop hang when shared chat clients/credentials bind their asyncio primitives to a dead loop.
- DurableWorkflowClient (start/await workflow + HITL discover/respond); DurableAIAgentClient stays agent-only.
* refactor(azurefunctions): delegate workflow execution to agent-framework-durabletask
AgentFunctionApp now reuses the shared orchestrator, activity body, and registration planner from agent_framework_durabletask instead of maintaining its own copies; _workflow.py becomes a thin host-specific adapter (AzureFunctionsWorkflowContext).
- Run agent entity coroutines on the shared persistent event loop, fixing the cross-loop hang.
- Relocate state-diff unit tests to the durabletask package; update entity loop tests.
* feat(core): expose durabletask workflow symbols via agent_framework.azure
Lazily re-export WORKFLOW_ORCHESTRATOR_NAME and DurableWorkflowClient from the agent_framework.azure namespace so standalone hosts can import them without depending on internal module paths.
* docs(samples): add standalone durabletask workflow and HITL samples
Add two samples under samples/04-hosting/durabletask demonstrating MAF workflows on a standalone Durable Task worker (no Azure Functions):
- 08_workflow: conditional spam-detection workflow started via DurableWorkflowClient.start_workflow / await_workflow_output.
- 09_workflow_hitl: content-moderation workflow that pauses with ctx.request_info and is resumed via DurableWorkflowClient.get_pending_hitl_requests / send_hitl_response.
Also add the durabletask workflow integration test (test_08_dt_workflow).
* fix: address PR review feedback
- Sanitize HITL external-event responses with strip_pickle_markers in the orchestrator (defense-in-depth for callers that bypass DurableWorkflowClient).
- Raise WorkflowConvergenceException when max_iterations is reached with pending messages, matching the core WorkflowRunner instead of silently returning partial output.
- Route falsy 'sent' messages (use 'is not None' instead of truthiness).
- Normalize None shared_state_snapshot/source_executor_ids in execute_workflow_activity.
- Cast Any returns in AzureFunctionsWorkflowContext to satisfy mypy/pyright.
- Fix sample docstrings to reference DurableWorkflowClient.
* fix: resolve pyright Package Checks errors
- Use typed locals instead of cast in AzureFunctionsWorkflowContext (mypy sees Any, pyright sees concrete types -> avoid reportUnnecessaryCast).
- Annotate shared_state_snapshot and cast partially-typed durabletask SDK returns / HITL custom-status parsing to satisfy reportUnknownVariableType/reportUnknownMemberType.
- Drop the dead deserialize/serialize re-export in _workflow.py and mark the intentional private _extract_message_content re-export.
* fix(durabletask): agent-executor identity and typed workflow input
Register each workflow agent entity under the executor id that the orchestrator dispatches to (instead of the agent name), so AgentExecutor(agent, id=...) works when the id differs from agent.name. The azure-functions host mirrors this.
Reconstruct the start executor declared input type from the workflow initial JSON payload in the shared engine (mirroring in-process delivery) instead of string-coercing it per host. Untrusted input is stripped of pickle markers before reconstruction to prevent deserialization RCE.
* fix(samples): type durable workflow start executors for reconstructed input
The HITL and parallel workflow samples no longer hand-parse a JSON string. Their start executors now declare their real input type (ContentSubmission / DocumentInput), which the durable engine reconstructs from the client payload before delivery.
* test(durabletask): unit coverage for registration, client, worker, and input coercion
Add unit tests for plan_workflow_registration, DurableWorkflowClient, the agent-executor identity registration (entity keyed by executor id), and the typed initial-input coercion including pickle-marker neutralization.
* test(durabletask): HITL and parallel durable workflow integration tests
Add an integration test for the standalone durabletask HITL workflow sample via a new workflow_client fixture. Re-enable the Azure Functions parallel workflow test, consolidated into one end-to-end case so the work-stealing xdist scheduler cannot spawn multiple func hosts for this sample.
* refactor(durabletask): group workflow modules into a _workflows subpackage
Move the eight workflow modules into a private _workflows/ subpackage and drop the redundant _workflow_ prefix (orchestrator.py, registration.py, activity.py, client.py, context.py, dt_context.py, runner_context.py, serialization.py). The public API and __all__ are unchanged; only direct internal-module imports were repointed (package __init__, the worker, the azure-functions shared shim, and the affected unit tests).
* fix(durabletask): harden workflow type resolution and HITL response handling
- resolve_type returns only real classes (avoids issubclass TypeError in reconstruct_to_type)
- re-wait on HITL responses rejected by pickle-marker sanitization instead of dropping the request and losing the run
- American spelling in strip_pickle_markers docstring
- unit tests for resolve_type
* fix(durabletask): treat async edge conditions as not-matched on the synchronous host
The durabletask orchestrator evaluates edge conditions synchronously and does not support async edge conditions. Such an edge is now treated as not matched (the edge is not traversed) rather than assuming a result. Adds unit coverage; full async-condition support will be handled separately.
* fix(durabletask): reconstruct typed workflow outputs at the host boundary
await_workflow_output and the Azure Functions status endpoint now decode the checkpoint-encoded outputs the shared activity produces, via a shared deserialize_workflow_output helper. The client returns the original objects; the AF endpoint emits clean domain JSON instead of checkpoint-marker dicts, keeping the two hosts consistent.
* fix(durabletask): address review findings on workflow hosting
- AF: register workflow agents through add_agent(entity_id=...) so they remain tracked in app.agents / get_agent() (restores documented behavior) while keying by the executor id the orchestrator dispatches to; mirrors DurableAIAgentWorker.add_agent.
- async bridge: treat the shared loop as reusable only while its backing thread is alive, so a dead loop thread is replaced instead of hanging future.result() forever.
- client: add get_runtime_status; the standalone HITL sample now stops polling and reports the real terminal state instead of a generic timeout.
- tests: guard send_hitl_response pickle-marker stripping and add get_runtime_status coverage.
* fix(durabletask): wait indefinitely for HITL responses, matching core
The durable workflow host previously raced HITL responses against a 72h timer and failed the orchestration on elapse. MAF core's request_info has no timeout concept (it waits for the response), and the .NET durable host waits too, so the durable Python host now does the same: it stays paused until a response arrives. Removes the hitl_timeout_hours parameter and DEFAULT_HITL_TIMEOUT_HOURS constant from both hosts. A configurable timeout can be added later once core defines the contract (what happens on elapse).
* feat(durabletask): typed workflow event streaming and async client API
Add a brokerless workflow event stream to the durable host. Each non-agent executor runs inside a durable activity that captures its real WorkflowEvents (with data payloads); the orchestrator replays them into the orchestration custom status after each superstep, and the client streams them back as typed WorkflowEvent objects with reconstructed data. Agent executors contribute synthesized invoked/completed lifecycle events.
Add async client methods run_workflow (start with optional wait) and stream_workflow (typed event iterator), plus is_replaying plumbing through the orchestration context protocol and both host adapters so live status is published only on non-replay execution.
* docs(samples): standalone durabletask workflow streaming sample
Add sample 10_workflow_streaming demonstrating the async DurableWorkflowClient API on a standalone Durable Task worker: run_workflow(wait=False) to start without blocking, then stream_workflow to consume typed WorkflowEvent objects as a WriterAgent -> ReviewerAgent -> publish pipeline runs.
* refactor(durabletask): internal-only checkpoint codec and host-scoped workflow event streaming
Two related hardening changes to the durable workflow hosting layer, plus a
rebase-restored improvement.
Internal-only serialization codec (MSRC follow-up):
- Rename serialize_value/deserialize_value -> _serialize_value/_deserialize_value
in the shared durabletask serialization module and update all call sites, so the
pickle-backed checkpoint codec is unambiguously framework-internal. Untrusted
input is still neutralized with strip_pickle_markers at the HTTP boundary.
- Remove the duplicate agent_framework_azurefunctions._serialization module and
import strip_pickle_markers from the shared durabletask module instead. Move its
unique serialization/strip-marker tests into the durabletask test suite.
Scope workflow event streaming to hosts that can carry it:
- Add WorkflowOrchestrationContext.supports_event_streaming. The standalone
DurableTask host returns True (no custom-status size cap, has a stream_workflow
consumer); the Azure Functions host returns False.
- The orchestrator now accumulates and publishes the WorkflowEvent timeline to the
orchestration custom status only when the host supports streaming. On Azure
Functions the custom status returns to its pre-streaming shape
({state[, pending_requests]}), which fixes orchestrator failures with
"The size of the JSON-serialized payload must not exceed 16 KB" and stops leaking
pickle markers into the HTTP status response. The Azure Functions status endpoint
never consumed the event stream.
Workflow start endpoint:
- Accept text/plain raw request bodies (fall back from get_json to the raw body),
restoring an improvement from main that the rebase conflict resolution dropped.
* fix(azurefunctions): scope workflow status/respond endpoints to the workflow orchestrator
The workflow/status/{instanceId} and workflow/respond/{instanceId}/{requestId}
HTTP endpoints resolved durable instances by ID only. The durable client looks up
IDs across every orchestration in the task hub (agent entities, any
user-registered orchestrations, and other apps sharing the hub), so a caller
holding one instance ID could read another orchestration's status -- including
pending HITL request payloads -- or inject external events into it.
Add AgentFunctionApp._is_workflow_orchestration() and gate both endpoints on it:
an instance whose orchestration name is not WORKFLOW_ORCHESTRATOR_NAME now returns
404 instead of leaking state or accepting events. send_hitl_response now fetches
the orchestration status and validates ownership before raising the external
event. Legitimate workflow instances are unaffected.
Mirrors the .NET fix in PR #6608.
* fix(durabletask): resolve CI typing failures
- serialization: rename _serialize_value/_deserialize_value back to
serialize_value/deserialize_value to follow the package convention for
cross-module internal helpers (matches strip_pickle_markers, resolve_type).
The leading underscore tripped pyright reportPrivateUsage on cross-module
imports under the strict source gate; internal-only status is preserved by
not exporting them from the public API.
- Remove type-ignore comments pyright flags as unnecessary
(reportUnnecessaryTypeIgnoreComment) in _worker.py, orchestrator.py,
serialization.py.
- test_08_dt_workflow: add AgentClientFactoryProtocol and annotate the
agent_client_factory fixture as type[AgentClientFactoryProtocol] (matching
test_01-07) so mypy/ty stop reporting "type has no attribute create".
- samples (08_workflow, 09_workflow_hitl): pass structured output via
FoundryChatOptions[Any](response_format=...) instead of a plain dict so the
samples pyright (basic) config accepts default_options.
---------
Co-authored-by: Gavin Aguiar <80794152+gavin-aguiar@users.noreply.github.com>
* Migrate 01-get-started samples to Foundry as canonical default
Change canonical provider from Azure OpenAI to Microsoft Foundry Responses API:
Code changes:
- Updated all 01-get-started samples (01_hello_agent, 02_add_tools, 03_multi_turn,
04_memory, 06_host_your_agent) to use FoundryAgent or AIProjectClient.AsAIAgent()
- Updated environment variables: AZURE_OPENAI_* → FOUNDRY_PROJECT_ENDPOINT/FOUNDRY_MODEL
- Updated .csproj files to reference Microsoft.Agents.AI.Foundry instead of Azure.AI.OpenAI
- Added warning comments about DefaultAzureCredential production usage
- 05_first_workflow unchanged (workflow pattern only, no AI model)
Documentation changes:
- Updated AGENTS.md Default provider section to reflect Foundry as canonical
- Updated code example to use FoundryAgent constructor pattern
- Updated env var documentation
Note: 04_memory (AIContextProvider sample) extracts IChatClient from FoundryAgent
to maintain the memory pattern while using Foundry backend.
All samples verified to build successfully.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR 6555 review feedback and format failures
- Add Microsoft.Agents.AI.Foundry using to AGENTS.md Foundry snippet
- Update verify-samples GetStarted env vars to FOUNDRY_PROJECT_ENDPOINT/FOUNDRY_MODEL
- Remove unnecessary usings flagged by dotnet format in 01_get_started samples
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Switch 01-get-started samples from FoundryAgent to AIProjectClient.AsAIAgent()
Use AIProjectClient.AsAIAgent() as the canonical pattern for all 01-get-started
samples. Reserve FoundryAgent only for samples that specifically demonstrate the
Foundry-managed (prompt) agent — i.e. 02-agents/AgentsWithFoundry/.
Changes:
- 01_hello_agent, 02_add_tools, 03_multi_turn, 06_host_your_agent: swap
FoundryAgent constructor for AIProjectClient.AsAIAgent(model, instructions)
- 04_memory: get IChatClient via AIProjectClient.AsAIAgent(options).GetService()
instead of extracting from a throwaway FoundryAgent
- AGENTS.md: update default-provider snippet and note on when to use FoundryAgent
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix auto function calling stripping explicit null arguments (fixes#5934)
* fix: re-role trailing assistant message to user for Anthropic (fixes#5008)
* fix: address Copilot review feedback (exclude_unset, test coverage, synthetic user turn)
* fix: update docstring and extend exclude_unset to auto_invoke_function
* revert: remove unrelated core _tools.py changes from Anthropic PR
The exclude_none/exclude_unset changes in the core package are out of scope
for this Anthropic-specific fix. This PR now only contains the Anthropic
chat client docstring fix and the synthetic user turn append.
* fix: avoid appending user turn after Anthropic tool use
* Fix Anthropic tool-use type narrowing
Use object-typed content narrowing before checking Anthropic tool-use block types so strict Pyright no longer treats dynamic message content as Unknown.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Evan Mattson <evan.mattson@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Scope workflow status/respond endpoints to route workflow.
Validate that the orchestration instance belongs to the workflow
named in the route. Prevents cross-workflow access via runId.
* Add changelog.
* Address Copilot review feedback: fix duplicate XML doc, make IsOrchestrationOwnedByWorkflow non-throwing, drop misleading Async suffix in test name
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* .NET: feat(evals): RubricScore type + EvalScoreResult.Dimensions
Adds the core rubric-evaluator surface that mirrors the Python work in
PR #6101 (commit e45b934cc). Provider-agnostic types only — no Foundry
coupling. Subsequent commits will wire these into FoundryEvals.
- RubricScore: per-dimension score record (Id, Score?, Applicable, Weight, Reason).
- EvalScoreResult.Dimensions: optional init-only list of RubricScore.
Null for non-rubric (built-in) evaluators.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* .NET: feat(evals): GeneratedEvaluatorRef + assertion helpers
Adds the provider-agnostic surface for referencing a pre-existing rubric
evaluator and gating CI on per-item / per-dimension thresholds. Mirrors
Python PR #6101 commits e5830dd7f (ref type) and 4bc60462d (asserts).
- GeneratedEvaluatorRef: name + optional version/display-name, plus a
Latest(name) factory for versionless refs (discouraged for CI; consumers
should warn at run time).
- AgentEvaluationResults.AssertScoreAtLeast: walks DetailedItems[].Scores,
optionally filtered by evaluator name, recurses into SubResults.
- AgentEvaluationResults.AssertDimensionScoreAtLeast: walks each score's
Dimensions list, skips non-applicable dimensions by default, supports
requireApplicable to flip that, recurses into SubResults.
- AgentEvaluationResults.AssertNoFailedItems: walks DetailedItems for
fail/error statuses, recurses into SubResults.
All helpers throw InvalidOperationException (matches existing AssertAllPassed).
Truncates offender lists to the first 5 with a '+N more' suffix to keep
CI output readable, mirroring the Python helpers.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* .NET: feat(foundry-evals): accept GeneratedEvaluatorRef in evaluators=
Adds FoundryEvaluatorSpec, a readonly-struct union with implicit conversions
from both string and GeneratedEvaluatorRef so call sites can mix built-in
evaluator names with rubric evaluator references:
var evals = new FoundryEvals(
projectClient, model,
new GeneratedEvaluatorRef("policy-rubric", "3"),
FoundryEvals.Relevance,
FoundryEvals.Coherence);
FoundryEvals constructors (3 overloads), EvaluateTracesAsync, and
EvaluateFoundryTargetAsync now take FoundryEvaluatorSpec[]/params instead of
string[]/params. Existing call sites using string literals or string[] keep
working unchanged via implicit conversion.
FoundryEvalConverter.BuildTestingCriteria emits the documented Foundry wire
format for rubric refs:
{
"type": "azure_ai_evaluator",
"name": <DisplayName ?? Name>,
"evaluator_name": <Name>,
"evaluator_version": <Version>, // omitted when null
"initialization_parameters": { "deployment_name": <model> },
"data_mapping": { conversation arrays, optional tool_definitions }
}
WireTestingCriterion gains an optional EvaluatorVersion field. Rubric refs
are preserved through FilterToolEvaluators (tool-aware but not tool-required)
and ignored by FindMissingGroundTruthEvaluators. A versionless ref emits a
Trace.TraceWarning at criterion-build time so CI authors notice the floating
version (mirrors the Python warning).
Adds 6 new Foundry unit tests (3 BuildTestingCriteria rubric paths, 1
FindMissingGroundTruthEvaluators, 1 FilterToolEvaluators preservation, 1
mixed-order). 369/369 Foundry tests pass.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* .NET: feat(foundry-evals): parse rubric dimension_scores into RubricScore
Adds FoundryEvals.ParseRubricScores, called per result inside ParseDetailedItem.
Each EvalScoreResult now populates Dimensions when the evaluator's sample carries
a rubric breakdown.
Accepts three shapes for forward compatibility with provider SDK iterations:
1. sample.properties.dimension_scores (canonical Foundry runtime shape)
2. sample.properties.rubric_scores (preview/legacy key)
3. top-level sample.dimension_scores / sample.rubric_scores (defensive fallback)
Entries missing 'id', 'weight', or 'applicable' are skipped without invalidating
well-formed siblings. Non-applicable dimensions may omit 'score' (parsed as null).
Adds 6 unit tests covering canonical and legacy keys, top-level fallback, no-match
returns null, malformed-entry skipping, and the non-applicable null-score path.
375/375 Foundry tests pass.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* .NET: feat(samples): Evaluation_FoundryRubric end-to-end sample
Adds dotnet/samples/05-end-to-end/Evaluation/Evaluation_FoundryRubric mirroring
the Python evaluate_with_rubric_sample.py:
- Fetches a pre-existing Foundry agent via AgentAdministrationClient
(GetAgentAsync for latest, GetAgentVersionAsync when FOUNDRY_AGENT_VERSION
is pinned).
- References a rubric evaluator by GeneratedEvaluatorRef(name, version);
falls back to GeneratedEvaluatorRef.Latest(name) with the documented
floating-version warning.
- Mixes the rubric with FoundryEvals.Relevance and FoundryEvals.Coherence
in a single FoundryEvals run (implicit string-and-ref conversion).
- Prints per-dimension breakdowns from EvalScoreResult.Dimensions for each
item.
- Demonstrates a CI quality gate with AssertDimensionScoreAtLeast("general_quality", 3.0).
Documents the FOUNDRY_PROJECT_ENDPOINT footgun (must be project-scoped URL
.../api/projects/<project>, not the bare Azure OpenAI endpoint) and the
Eval-Definition-vs-Rubric-Evaluator distinction in the README. Ships a
.env.example with the FOUNDRY_* variables.
Registers the project in agent-framework-dotnet.slnx and cross-links from
the sibling Evaluation_Multimodal / Evaluation_ExpectedOutputs READMEs.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(foundry-evals): harden FoundryEvals public surface for review
Address PR #6267 review comments on the .NET FoundryEvals integration:
- Add source-compat overloads accepting `string[] evaluators` for `FoundryEvals` ctor, `EvaluateTracesAsync`, and `EvaluateFoundryTargetAsync` so existing callers passing string arrays keep compiling unchanged. New overloads forward via a private `ToSpecs` helper that wraps each name through the implicit `string -> FoundryEvaluatorSpec` conversion.
- Guard against `default(FoundryEvaluatorSpec)` entries (both `BuiltinName` and `GeneratedRef` null) that would NRE the downstream converter. Adds `FoundryEvaluatorSpec.IsValid` / `EnsureValid` plus an internal `EnsureAllSpecsValid` helper, wired into the main ctor and both static evaluation entry points.
- Add 6 unit tests covering the new validation surface.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(sample): set ExitCode=1 when rubric dimension gate trips
PR #6267 review comment: the FoundryRubric sample swallowed the AssertDimensionScoreAtLeast failure, so a CI run that included it as a quality gate would still exit 0 even when the rubric regressed. Set `System.Environment.ExitCode = 1` in the catch so CI fails while still letting the rest of the sample's logging complete cleanly.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(foundry-evals): search typed Sample directly for rubric scores
PR #6267 review comment: `_extract_rubric_scores` only searched the `properties` dict when the sample exposed one. When the Azure AI Projects typed SDK returns a Sample object that puts `dimension_scores` / `rubric_scores` directly on the instance (no `properties` wrapper), we missed them and surfaced no per-dimension scores.
Add an `else: containers.append(sample)` branch so non-dict typed samples are also inspected for the score keys. Covered by two new tests: one with `dimension_scores` directly on a typed Sample without a `properties` wrapper, and one with the legacy `rubric_scores` key in the same shape.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(evals): cover assert_score_at_least and assert_no_failed_items
PR #6267 review comments: both assertion helpers shipped without unit tests. Add `TestAssertScoreAtLeast` (above threshold, below w/ offenders, evaluator filter, sub_results recursion) and `TestAssertNoFailedItems` (all passing, failed/errored statuses, sub_results recursion) with a shared `_score_results` fixture builder.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs(samples): remove dead rubric-evaluator doc link from FoundryRubric sample
The Azure AI Foundry rubric evaluator concept doc page has not yet been published, so the link in the sample README and Program.cs comment 404s. Drop the references until the upstream doc is live.
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>
* Address PR 6267 review nits
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Ben Thomas <25218250+alliscode@users.noreply.github.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>
* Port FileMemoryProvider to python and integrate it and FileAccessProvider into the harness
* Address PR comments
* Address PR comments
* Create FileSystemAgentFileStore root lazily on first write
Construction no longer calls mkdir, so building a store (and therefore a
default create_harness_agent, which wires default file-memory and file-access
stores under the CWD) performs no filesystem writes and does not fail in
read-only working directories. The root directory is created on the first
write_file / create_directory call; all read/list/search operations already
tolerate a missing root. Updates docstrings and adds a regression test.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix typing
* Fixing typing errors
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Split type checkers by target (pyright source, 5 checkers on tests/samples)
Rework the typing setup along the lines of the 'too many type checkers'
approach:
- Pyright (strict) is now the sole source-code type checker; mypy is
removed from source and its [tool.mypy] block becomes a relaxed profile
used only for tests/samples.
- Tests are checked by all five checkers (pyright relaxed, mypy, pyrefly,
ty, zuban); samples by pyright, pyrefly, and ty. All run in a relaxed/
basic profile so authors aren't forced into over-annotation.
- Add pyrightconfig.tests.json and bump sample pyright configs to basic.
- Unify test/sample typing onto the same parallel fan-out used by source
pyright via run_command_items in task_runner.py.
- Make version-conditional imports symmetric: keep or drop the
'# type: ignore' on both branches so results match across interpreter
versions (local vs CI).
- Update SKILL.md, DEV_SETUP.md, and CODING_STANDARD.md for the five
gating checkers and pyright on source+tests+samples.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Fix merge regressions from main (typing + runtime)
Merging main into the type-checker split branch surfaced regressions that
the new five-checker test suite and unit tests caught:
Runtime fixes:
- anthropic: restore the dropped `cache_read_input_token_count` mapping in
_parse_usage_from_anthropic (lost during merge conflict resolution).
- gemini: _get_function_calling_mode test helper returned str(enum)
('FunctionCallingConfigMode.AUTO') instead of the enum value ('AUTO').
- openai: _response_id_from_token test helper was an infinite self-recursion;
return token['response_id'].
- orchestrations: reset output_events per approval iteration so the terminal
output assertion counts only the final run.
- core: drop a stale duplicate harness test whose message ('non-negative')
contradicted the source ('positive').
- purview: import PolicyLocation/PolicyScope/ProtectionScopeActivities/
ExecutionMode used by the processor tests.
Type-checker fixes (tests, relaxed profile):
- core: pyright/mypy/pyrefly/ty/zuban green-ups across the harness, MCP,
observability and types tests.
- anthropic/openai: route provider-namespaced UsageDetails keys through a
dict cast (extra_items TypedDict unsupported by mypy/ty).
- purview: typed model constructors and cache-mock casts.
- ag-ui: annotate WorkflowContext[Any, Any] so yield_output accepts test
payloads, guard Optional forwarded_props, and ty-ignore intentional bad args.
Source pyright (sole source checker) flagged unnecessary ignores newly
introduced by merged code in core _tools.py and declarative _declarative_base.py.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Isolate per-package mypy cache in test-typing fan-out
The parallel test-typing fan-out runs many mypy processes concurrently,
all defaulting to a single shared ./.mypy_cache. Concurrent writes corrupt
the cache and mypy aborts with INTERNAL ERROR (intermittently, depending on
worker timing) -- which is why CI's Test Typing job failed on a shifting set
of packages while a single-package run was fine.
Give each mypy invocation an isolated cache dir keyed by its target paths so
incremental caching still works per package without races. Other checkers
(zuban/pyrefly/ty/pyright) maintain their own caches and are unaffected.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Make lab pyright-only on source (drop source mypy)
Lab was the last package still running mypy on its source code, requiring
mypy-only `# type: ignore` comments that pyright (the sole source checker
everywhere else) flags as unnecessary. Align lab with the rest of the
monorepo:
- Remove the lab source mypy poe tasks (mypy-gaia/lightning/tau2) and the
now-dead strict [tool.mypy] config block.
- Drop the 'Run lab mypy' CI step; lab source is type-checked by pyright only.
Lab tests remain covered by the workspace test-typing fan-out (mypy, pyrefly,
ty, zuban, pyright over tests using the relaxed root config).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Fix test-typing regressions from latest main merge
A fresh merge from main brought in new test code never run under the
five-checker test-typing suite. Green up across the affected packages:
- core: narrow Optional span.attributes with 'and' guards in span filters
and assert+cast the json.loads(...attributes[...]) reads (test_observability);
match the existing as_agent ignore on the protocol-typed fixture (test_clients).
- openai: align new streaming tests with the established chat_options dict
pattern (ChatOptions TypedDict isn't assignable to dict), route Optional
.annotations[0] access through a small _first_annotation helper (mirrors the
file's assert-not-None convention), and annotate a mapped ResponseStream.
- foundry_hosting: annotate error: dict[str, Any] = body.get(...) or {}
(zuban needs the annotation).
- foundry: narrow ignores for the live AIProjectClient credential arg (pyrefly)
and connections.get_default (zuban) SDK type gaps.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* updated pyright version
* pyright fix
* Python: Fix source typing for pyright 1.1.410
Pyright 1.1.410 tightened several checks. Apply the same source fixes as
upstream PR #6275:
- anthropic: import AsyncAnthropicBedrock from anthropic.lib.bedrock and
AsyncAnthropicVertex from anthropic.lib.vertex (no longer re-exported from
the anthropic top-level package -> reportPrivateImportUsage).
- core _types.py: cast the transform-hook result to UpdateT (reportAssignmentType).
- core _workflows/_events.py: annotate the @contextmanager helper as
Generator[None] instead of Iterator[None] (reportDeprecated).
- redis: build the combined filter expression with an explicit loop instead of
reduce(and_, ...), which pyright could no longer fully type (drops the now
unused functools.reduce / operator.and_ imports).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Accept plain-text body in Azure Functions workflow/run endpoint
The workflow_orchestrator already accepts plain strings as well as JSON
objects via context.get_input(), but the start_workflow_orchestration HTTP
handler only accepted JSON and returned 400 for any non-JSON body. This made
the functions integration tests that POST text/plain to /api/workflow/run
(e.g. test_09_workflow_shared_state) fail consistently with 400 != 202.
Fall back to the raw request body (decoded as UTF-8) when the body is not
JSON, rejecting only a truly empty body. The JSON path is unchanged.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Bump Azure.AI.Projects to 2.1.0-beta.3
Updates Azure.AI.Projects from 2.1.0-beta.2 to 2.1.0-beta.3, together with the transitive Azure.Core (1.56.0 to 1.57.0) and System.ClientModel (1.12.0 to 1.13.0) pins that beta.3 requires (beta.3 forces System.ClientModel 1.13.0.0 via Azure.Core 1.57.0).
Migrates the affected samples and integration test to the beta.3 surface:
* MemorySearch sample: MemorySearchToolCallResponseItem renamed to MemorySearchToolCall, Results renamed to Memories, MemoryItem indirection removed.
* AgentSkills sample: skill provisioning/download API redesigned to a version based model (CreateSkillVersionFromFiles, GetSkillContent which now downloads and unzips), removing manual ZIP handling.
* Session files integration test: GetSessionFilesAsync now returns an async collection of SessionDirectoryEntry and renames the sessionId parameter to agentSessionId.
* Stream session file listing and short-circuit in integration test
Avoids materializing the entire session directory listing into a List. The test now streams GetSessionFilesAsync and breaks as soon as the expected entry is found, then asserts it was located.
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Adding an observer to the python harness for web search tools
* Escape dynamic strings with rich.markup.escape() in WebSearchDisplayObserver
Apply rich.markup.escape() to all user/tool-provided strings (queries, URLs,
titles, patterns) before interpolation into Rich-markup-enabled output. This
prevents characters like '['/']' from being interpreted as Rich markup tags.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Ensure an argument-scoped standing approval (the "always approve with exact
arguments" path) records an empty argument set rather than null when the
approved call has no arguments, so it matches only future no-argument calls.
null remains reserved exclusively for tool-level approvals, keeping the two
scopes distinct. This aligns the .NET behavior with the existing Python harness.
Adds regression tests covering the no-argument standing-approval flow, the
MatchesRule argument-scoping semantics, and empty-arguments rule serialization.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Hosted-AgentSkills sample and its mirrored unit-test helper gated ZIP
extraction on `StartsWith(destinationRoot)` OR `Equals(destinationRoot)`. The
second branch left an acceptance path not covered by the containment check, so
static analysis could not prove the extraction sink stays within the
destination. Make the single resolved-path StartsWith check the only gate to
extraction in both files and add a nested-entry regression test.
Closes#6564
Selective, CHANGELOG-driven version bumps for the 2026-06-18 release.
Released tier: agent-framework-core and the root agent-framework go to 1.9.0
(minor). Core ships new public APIs (agent-loop middleware, tool-approval
middleware and harness integration, shell-tool harness integration, AG-UI
thread snapshot persistence, context-provider telemetry) plus two behavioral
breaking changes on evolving surfaces: MCP sampling now denies server-initiated
requests by default, and the FileAccess tools were aligned with the .NET
implementation. These are treated as within-1.x changes because every package
caps core at <2; a major bump would require rewriting those caps. The foundry
and openai packages go to 1.8.2 (patch, bug fixes only). The root
agent-framework-core[all] pin was moved to 1.9.0 in lockstep with core.
Release-candidate tier: ag-ui to 1.0.0rc5 and declarative to 1.0.0rc2 for their
respective changes. orchestrations is promoted to stable 1.0.0; PACKAGE_STATUS
and the README install hint were updated accordingly.
Prerelease tier (new Pacific date stamp 260618): anthropic (beta),
azure-contentunderstanding (alpha) and foundry-hosting (alpha). No beta cohort
bump was applied; only packages with changes this cycle were stamped.
Dependency floors: following the established convention, the core floor was
raised to >=1.9.0 on every non-core package bumped this cycle, preserving the
existing <2 upper bound.
Also resolves two pre-existing failures in the dependency-bounds validator that
are unrelated to the version bumps. Hosted-environment detection now catches a
bare ImportError so optional Foundry hosting probing cannot crash user-agent
setup. The harness shell-tool integration, which lazily imports the separate
agent-framework-tools package to avoid a circular runtime dependency, is now
type-checked and tested in isolated environments via a core dev
dependency-group, with the shell-tool tests guarded to skip when that package
is absent.
* Refactor DocumentEntry model and update result handling
- Changed the type of `result` in DocumentEntry from dict to str to store LLM-ready text.
- Introduced `search_payload` in DocumentEntry for optional alternate rendering.
- Updated FileSearchConfig to include `include_fields` option for vector store uploads.
- Modified tests to reflect changes in DocumentEntry and FileSearchConfig.
- Adjusted integration tests to validate new result structure and rendering.
- Removed legacy format_result tests as rendering is now handled by the SDK.
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Add test to ensure page markers are preserved in LLM input
Co-authored-by: Copilot <copilot@github.com>
* fix(cu-context-provider): scope LLMStats telemetry filter to rai_warnings block
Address PR #5796 review comment: the previous defensive scrubber ran a global regex substitution over the full rendered string, so any markdown body bullet shaped like '- LLMStats: ...' would also be silently deleted.
Add a _strip_rai_telemetry helper that confines the substitution to the front-matter rai_warnings: YAML sub-block, leaving the body verbatim. Cover the new behavior with three tests (scoped strip, body preservation, and no-op branches).
* Sync uv.lock with azure-ai-contentunderstanding>=1.2.0b1 dependency bump
* Python: Drop search_payload/include_fields, single to_llm_input rendering (CU context provider)
Address PR #5796 review: remove the redundant search_payload field and _render_search_payload helper, drop the include_fields opt-in (already covered by output_sections), rename _resolve_pending_tokens -> _resolve_pending_analysis, and have _upload_to_vector_store read entry['result'] directly.
* Python: Adopt SDK 1.2.0b2 LLMStats filtering, drop local workaround (CU context provider)
azure-ai-contentunderstanding 1.2.0b2 filters LLMStats telemetry from rai_warnings and emits InputPageNumber page markers in to_llm_input, so the provider's local defense is redundant.
- Bump dependency to azure-ai-contentunderstanding>=1.2.0b2 (re-lock uv.lock)
- Remove _strip_rai_telemetry and its two regexes; _render_for_llm now returns to_llm_input(...) directly
- Delete 4 workaround unit tests for the removed helper
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: changjian-wang <v-changjwang@microsoft.com>
Co-authored-by: aluneth <wangchangjian1130@163.com>
* scope MCP threadId to the current agent
* Fix Async suffix on test methods and add CHANGELOG entries
- Rename three test methods to include Async suffix (IDE1006 fix)
- Add CHANGELOG entries for DurableTask and Hosting.AzureFunctions
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(dotnet): Add LocalCodeAct package scaffold
Create Microsoft.Agents.AI.LocalCodeAct package with:
- Project file with embedded Python resources
- ExecutionMode enum (Subprocess only)
- ProcessExecutionLimits record
- FileMount record and FileMountMode enum
- README.md documentation
- Embedded Python runner and validator scripts
This is the .NET equivalent of the Python agent-framework-local-codeact
package. Next: Implement process bridge and tool integration.
* feat(dotnet): Add embedded Python runner and validator
Copy Python runner and validator scripts from the Python implementation
as embedded resources for the .NET package.
* feat(dotnet): Add CodeValidator wrapper
Implement CodeValidator.cs that:
- Extracts embedded Python validator script to temp file
- Invokes Python validator with JSON request
- Passes custom allow/block lists
- Throws CodeValidationException on failures
- Cleans up temp files
Uses the embedded Resources/validator.py for AST validation.
* feat(dotnet): Add LocalExecuteCodeFunction
Implement LocalExecuteCodeFunction as AIFunction:
- Accepts Python executable path (required)
- Registers host tools for code to call
- Validates code via CodeValidator if custom lists provided
- Executes via ProcessBridge
- Converts result dict to ChatMessage list
- Builds dynamic description including available tools
Matches Python LocalExecuteCodeTool functionality.
* feat(dotnet): Add LocalCodeActProvider
Implement AIContextProvider that:
- Injects execute_code tool into context
- Adds CodeAct instructions
- Enforces single-provider-per-agent via StateKeys
- Wraps LocalExecuteCodeFunction lifecycle
Minimal provider implementation matching Python LocalCodeActProvider.
* feat(dotnet): Add tests and sample for LocalCodeAct
Add unit tests:
- LocalExecuteCodeFunctionTests (4 tests)
- ProcessExecutionLimitsTests (2 tests)
- FileMountTests (2 tests)
Add sample:
- LocalCodeAct/Program.cs - Demonstrates provider and function usage
- LocalCodeAct/README.md - Documentation and safety warnings
Tests verify basic construction, metadata, and disposal.
Sample shows provider creation, function setup, and configuration.
Note: Build requires .NET 10 SDK per global.json.
* feat(dotnet): Add LocalCodeAct sample project
Add sample demonstrating:
- LocalCodeActProvider creation and configuration
- LocalExecuteCodeFunction direct usage
- Execution modes and file mount configuration
- Safety warnings and prerequisites
Includes project file and README with security guidance.
* feat(dotnet): Add file mount support and integration tests
- Added FileMountHelper.cs for file mount normalization, snapshot, and capture
- Updated LocalExecuteCodeFunction to support file mounts parameter
- Added file snapshot before/after execution with capture logic
- Updated LocalCodeActProvider to pass file mounts through
- Created comprehensive IntegrationTests.cs with 10 test cases:
- Simple code execution
- Timeout handling
- Syntax error handling
- Blocked import validation
- Blocked builtin validation
- Custom allowed imports
- File mount read/write with capture
- Stdout capture
- Provider tool injection
All features from Python implementation now ported to .NET.
* Rewrite .NET LocalCodeAct to address all PR review comments
Complete rewrite that follows the Hyperlight package conventions
(see Microsoft.Agents.AI.Hyperlight) and addresses all 24 review
comments on PR #6105:
Architectural fixes:
* LocalCodeActProvider now uses options-class constructor pattern
matching HyperlightCodeActProvider.
* Override of ProvideAIContextAsync uses the correct
(InvokingContext, CancellationToken) signature returning
ValueTask<AIContext>.
* ExecuteCodeFunction follows the AIFunction Name/Description/JsonSchema
property pattern with InvokeCoreAsync override.
* Provider exposes AddTools/GetTools/RemoveTools/ClearTools and
AddFileMounts/GetFileMounts/RemoveFileMounts/ClearFileMounts CRUD
methods, with snapshot-at-invocation semantics under a lock.
Runtime/security fixes:
* Subprocess IPC uses JsonObject/JsonNode end-to-end (no
Dictionary<string, object?> casts that broke under JsonElement
deserialization).
* Validator runs in its own subprocess with a dedicated timeout
(ProcessExecutionLimits.ValidationTimeoutSeconds), never reuses
the runner script.
* Validation enabled by default; can be opt-ed out via
ValidationEnabled = false.
* validator.py has a __main__ entrypoint that reads JSON from
stdin and exits with structured errors.
* validator.py is now compatible with Python 3.9+ (Match nodes
added conditionally).
* call_id parsed as long to match Python id(kwargs) range.
Other:
* README rewritten with valid C# syntax (options-class, FileMount
constructor) and accurate descriptions of validator and file
capture behavior.
* Added integration tests that exercise the real subprocess and
validator (skipped gracefully when python3 is not on PATH).
* All 18 tests pass (15 unit + 3 integration) across net8/net9/net10.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Sync embedded validator.py with Python package allow-list enforcement
The embedded Python validator script used by the .NET LocalCodeAct
package now enforces the builtin allow-list, matching the latest
behavior of agent_framework_local_codeact._validator. Names that are
real Python builtins must appear in the allow-list, while unknown names
(user-defined functions, registered tools) remain allowed.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add Hosted-LocalCodeAct foundry hosted-agent sample
Mirrors the Python foundry_hosted_agent.py sample for the local-codeact
package: registers compute and fetch_data as sandbox-only host tools on
LocalCodeActProvider so the model only sees execute_code and reaches them
via await call_tool(...). Includes the standard hosted-agent supporting
files (agent.yaml, agent.manifest.yaml, Dockerfile, Dockerfile.contributor,
.env.example, README.md) and installs python3 in the container images so
the embedded runner and validator can execute.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(local-codeact-dotnet): sync validator os.* allow-list with Python
Mirror the Python package change: the embedded validator.py invoked by the
.NET ProcessBridge replaces the os.* deny-list with an allow-list of
{environ, path}. Add allowed_os_attrs parameter to validate_code and
_CodeValidator, and surface it via the stdin JSON request schema so the
.NET host can opt in to a broader allow-list when needed.
Default behavior tightens to match the documented contract: any os.*
attribute outside {environ, path} (for example os.listdir, os.open,
os.getcwd) is rejected.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(local-codeact-dotnet): address review + tighten validator
- validator.py: enforce os.* allow-list on `from os import X` so names like
`system`, `getcwd` cannot bypass the visit_Attribute restriction.
- ProcessBridge.ConfigureEnvironment: document that null Environment inherits
the parent env (matching real behavior) and update the public
LocalCodeActProviderOptions.Environment doc to describe the explicit
empty-dictionary opt-in for a scrubbed environment.
- Tests:
* FileMountHelperTests covers per-file, per-mount, and total
capture-limit branches that return TextContent omissions.
* Integration tests cover unknown-tool dispatch error, tool throwing
exception, and CodeValidator timeout that kills the process and
raises CodeValidationException.
- Sample: drop unused `Microsoft.Agents.AI.Foundry` using in
Hosted-LocalCodeAct/Program.cs to satisfy IDE0005 check-format.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chore(local-codeact-dotnet): remove stale orphan sample
The dotnet/samples/LocalCodeAct/ scaffolding sample referenced APIs
that don't exist in the current package (`ExecutionMode`, FileMount
object-initializer syntax, the old LocalExecuteCodeFunction
constructor signature, function.Metadata.*), produced a long list of
check-format violations (CHARSET, IMPORTS, IDE0073 header, IDE0005
unused using, IDE1006 Async suffix, RCS1037 trailing whitespace), and
did not match any of the documented sample layouts.
The hosted-agent example at
dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-LocalCodeAct
is the supported entry-point sample for this package.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* style(local-codeact-dotnet): satisfy check-format rules
- Add UTF-8 BOM to source files (CHARSET)
- Remove unused using directives (IDE0005)
- Simplify type names (IDE0001/IDE0002/IDE0090)
- Rename static field JsonOptions -> s_jsonOptions (IDE1006)
- Rename static field SyncRoot -> s_syncRoot (IDE1006)
- Add missing this. qualifications in ProcessBridge (IDE0009)
- Remove unused _options field from LocalCodeActProvider (IDE0052)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(local-codeact-dotnet): wire hosted sample into solution
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(local-codeact-dotnet): sync embedded Python scripts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(local-codeact-dotnet): exercise Python integration on Windows
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* .NET: Address LocalCodeAct API review feedback
Move the required Python executable path to LocalCodeAct constructors, invert the validation flag default, and apply small project/file mount cleanup suggestions.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* .NET: Address LocalCodeAct concurrency review
Surface unauthorized mount traversal errors and use concurrent provider registries for LocalCodeAct tool and file mount CRUD operations.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* .NET: Simplify LocalCodeAct function wrappers
Use AIFunctionFactory-created inner functions for LocalCodeAct execute_code wrappers and remove redundant script cache and JsonNode cloning logic.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* .NET: Update LocalCodeAct factory result tests
Handle JsonElement result values produced by AIFunctionFactory delegation in LocalCodeAct execute_code integration tests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
"Automated dependency bounds test mode failed before dependency upper-bound validation could run.",
"",
"The weekly dependency maintenance workflow kept only dev dependency updates for the generated PR, if any, and skipped dependency range updates for this run.",
"This automated update keeps Python dependency metadata coherent across the uv workspace. Python dependencies can be declared in multiple `pyproject.toml` files, but the workspace has one shared `python/uv.lock`, so dependency maintenance should update and validate them together instead of through per-manifest Dependabot PRs.",
"",
"### Description & Review Guide",
"",
"- **What are the major changes?** Refresh Python dev dependency pins, update package dependency ranges when the bounds tooling succeeds, and refresh `python/uv.lock`.",
"- **What is the impact of these changes?** Keeps the Python workspace dependency set current while producing at most one dependency PR for the week. If dependency range validation fails, this PR contains only the dev dependency updates that still pass final validation, and separate issues track failed range candidates.",
"- **What do you want reviewers to focus on?** Review the generated dependency metadata changes and any dependency-range updates for package-specific compatibility concerns.",
'<!-- NOTE FOR AUTOMATED/AI REVIEWERS: the "What do you want reviewers to focus on?"',
" item above is intended for human reviewers only. Automated/AI reviewers should",
" ignore it and review the entire change rather than narrowing scope to it. -->",
"",
"",
"### Related Issue",
"",
"No linked issue; this PR is generated by scheduled Python dependency maintenance.",
"",
"### Contribution Checklist",
"",
"- [x] The code builds clean without any errors or warnings",
"- [x] All unit tests pass, and I have added new tests where possible",
"- [x] The PR follows the [Contribution Guidelines](https://github.com/microsoft/agent-framework/blob/main/CONTRIBUTING.md)",
"- [ ] This PR is linked to an issue and there is no other open PR for this issue (see Related Issue above).",
'- [x] **This is not a breaking change.** If it _is_ a breaking change, add the `breaking change` label (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.',
].join("\n")
const prBodyFence = "```"
const command = [
"PR_BODY_FILE=\"$(mktemp)\"",
`cat > "$PR_BODY_FILE" <<'EOF'`,
prBody,
"EOF",
"gh pr create --repo microsoft/agent-framework --base main \\",
` --head ${owner}:${branch} \\`,
` --title "${prTitle}" \\`,
" --body-file \"$PR_BODY_FILE\"",
].join("\n")
const issueBody = [
"The Python dependency maintenance workflow generated and validated dependency updates, then pushed them to the automation branch.",
"",
`- Branch: \`${branch}\``,
`- Commit: \`${branchSha}\``,
`- Compare: ${compareUrl}`,
`- Workflow run: ${runUrl}`,
"",
"GitHub Actions is not permitted to create pull requests in this repository, so a maintainer needs to create the PR manually.",
# Probe the highest allowed dependency versions, then open issues/PRs from the passing updates.
name:Python - Dependency Range Validation
on:
workflow_dispatch:
permissions:
contents:write
issues:write
pull-requests:write
env:
UV_CACHE_DIR:/tmp/.uv-cache
jobs:
dependency-range-validation:
name:Dependency Range Validation
runs-on:ubuntu-latest
env:
# For now only run 3.13, if we do encounter situations where there are mismatches between packages and python versions (other then 3.10 and 3.14 which are known to not be able to install everything)
@@ -43,6 +43,11 @@ FIDES (Flow Integrity Deterministic Enforcement System) is a label-based securit
3.**Variable Indirection** — `ContentVariableStore` and `VariableReferenceContent` for physical isolation of untrusted content from the LLM context.
4.**Quarantined Execution** — `quarantined_llm` and `inspect_variable` tools for isolated processing of untrusted data with audit logging.
In addition, remote MCP integrations are secured through two mechanisms:
- **Hint-based tool auto-labeling**: MCP `ToolAnnotations` (`readOnlyHint`, `openWorldHint`, etc.) are mapped to FIDES tool properties (`source_integrity`, `accepts_untrusted`, `max_allowed_confidentiality`).
- **Server `_meta.ifc` result labels**: MCP result metadata is parsed into per-item `security_label` values, so provider-supplied IFC labels are enforced by middleware.
### Consequences
- Good, because it provides deterministic security guarantees about what untrusted content can influence.
- Attaches labels via `additional_properties` (no schema changes).
- Leverages `SerializationMixin` for label persistence.
- Integrates MCP hint/result metadata through `additional_properties` keys (`max_allowed_confidentiality`, `source_integrity`, `__mcp_result_meta__`) without transport-specific policy code in core middleware.
### MCP-Specific Security Notes
-`SecureMCPToolProxy` applies `apply_mcp_security_labels(...)` automatically when connecting an MCP tool or URL.
- For servers like the GitHub MCP server (with `X-MCP-Features: ifc_labels`), `_meta.ifc` labels are considered authoritative for per-result label assignment.
- Tools that are not explicitly `readOnlyHint=True` are treated as potential sinks and default to `max_allowed_confidentiality=PUBLIC` to prevent exfiltration.
status: superseded by [ADR-0030](0030-hosted-platform-context-agentserver-2.0.md)
contact: rogerbarreto
date: 2026-05-07
date: 2026-06-29
deciders: rogerbarreto
consulted: []
informed: []
@@ -9,6 +9,8 @@ informed: []
# Hosted session identity context for Foundry Hosting
> **Superseded by [ADR-0030](0030-hosted-platform-context-agentserver-2.0.md).** `Azure.AI.AgentServer.*` 2.0.0 (responses protocol `2.0.0`) replaced `ResponseContext.Isolation` (`UserIsolationKey` / `ChatIsolationKey`, headers `x-agent-user-isolation-key` / `x-agent-chat-isolation-key`) with `ResponseContext.PlatformContext` (`UserIdKey` / `CallId`, headers `x-agent-user-id` / `x-agent-foundry-call-id`). The chat isolation key was removed and `HostedSessionContext` is now user-only. This ADR is retained as the historical record of the original design.
## Context and Problem Statement
Server-hosted Foundry agents need a way to scope per-user state (most notably `FoundryMemoryProvider` memories) by the end user that initiated the request. The Foundry platform already injects `x-agent-user-isolation-key` and `x-agent-chat-isolation-key` headers on every Responses request, but the agent-framework hosting layer did not surface those values to `AIContextProvider` instances. The provider's `stateInitializer` only received an `AgentSession?` with no identity attached, so per-user scoping was impossible without out-of-band plumbing.
This document explores design options for two SEP-2640 features. The decisions are not yet finalized.
- **Part 1: MCP Resource Template Skills** - skills described by a URI template with variables that must be resolved before loading.
- **Part 2: Direct Skill References** - reading `skill://` URIs referenced directly (e.g., in server instructions) without being listed in the index.
## Part 1: MCP Resource Template Skills
### Context and Problem Statement
The `AgentMcpSkillsSource` currently only supports `skill-md` type entries from `skill://index.json` (support for `archive` type is planned). The SEP-2640 specification also defines `mcp-resource-template` entries: **parameterized skill namespaces** described by a URI template with variables (e.g., `{product}`) that resolve to concrete `SKILL.md` URIs. Rather than materializing every skill in the index, the template's variables must be resolved before a skill can be loaded.
| `url` | Concrete URI to `SKILL.md` | URI template with variables |
| `description` | Describes the skill | Describes the addressable skill space |
### Use Cases
Template skills address two scenarios where listing concrete skills is impractical:
- **Large skill catalogs** - too many skills to enumerate every entry in the index.
- **Dynamically generated skills** - skill content generated on the fly from parameters, so the set of valid skills is not known at index-creation time.
### How Template Skills Are Consumed
Per SEP-2640, the consumption flow relies on the MCP `completion/complete` method:
1.**Server registers a resource template** - The MCP server registers the same `url` value (e.g., `skill://docs/{product}/SKILL.md`) as an MCP [resource template](https://modelcontextprotocol.io/specification/2025-11-25/server/resources#resource-templates), wiring template variables to the [completion API](https://modelcontextprotocol.io/specification/2025-11-25/server/utilities/completion).
2.**Host reads `skill://index.json`** - Discovers the template entry with `type: "mcp-resource-template"`.
3.**Host surfaces template in UI** - Presents the template as an interactive discovery point where the user fills in variables.
4.**Host calls `completion/complete`** - For each template variable (e.g., `{product}`), the host calls the MCP completion API to get possible values from the server:
5. **User selects a value** - The user picks a value (e.g., `"billing"`) from the list.
6. **Host resolves the URI** - The template `skill://docs/{product}/SKILL.md` becomes the concrete URI `skill://docs/billing/SKILL.md`.
7. **Host reads the resolved skill** - Calls `resources/read` with the concrete URI and proceeds as with any `skill-md` skill.
### Potential Implementation Options
### Option 1: Callback on `AgentMcpSkillsSource` for Variable Value Selection
Add a callback to `AgentMcpSkillsSource` (or its options) that is invoked for each `mcp-resource-template` entry to let the caller select variable values.
return (selected, IncludeSkill: selected is not null);
};
})
.Build();
```
**Pros:**
- Simple implementation
- Easy to understand and use
**Cons:**
- Cannot be used in server-side scenarios where there is no interactive user at skill-discovery time
- Does not integrate with the agent's conversational flow
---
### Option 2: Integrate into Agent Conversation via `ChatClientAgent` Decorator
Model the template variable resolution as a request/response interaction within the agent's conversational loop.
**Flow:**
1. A `DelegatingAIAgent` decorator (e.g., `McpTemplateSkillResolutionAgent`) intercepts `RunAsync`/`RunStreamingAsync` calls and checks whether the inner agent has an `AgentSkillsProvider` with an `AgentMcpSkillsSource` containing unresolved template entries. The check is performed via `GetService<AgentMcpSkillsSource>()` on the `AgentSkillsProvider`, which delegates to a `GetService` method on the `AgentSkillsSource` base class.
2. The decorator calls an internal member on `AgentMcpSkillsSource` to get the list of `mcp-resource-template` entries from the index. The `AgentMcpSkillsSource` needs to be extended with an internal member that exposes unresolved template entries separately from concrete skills.
3. For each template entry, the decorator calls an internal member on `AgentMcpSkillsSource` to retrieve possible values for the template's variables via the MCP `completion/complete` API.
4. For each variable needing resolution, the decorator returns an `McpResourceTemplateValueRequestContent` (inherits from MEAI's `InputRequestContent`) in the agent response - bypassing the call to the inner agent. The content carries the template description, variable name, and possible values.
5. The user app receives the response, identifies the `McpResourceTemplateValueRequestContent` content type, and displays UI to the user showing the variable name and possible values, or forwards it further downstream if the user app is a service.
6. The user selects a value, and the user app calls the agent again with a corresponding `McpResourceTemplateValueResponseContent` (inherits from MEAI's `InputResponseContent`) containing the selected value. The `RequestId` property (inherited from the base classes) correlates the response with the original request.
7. The decorator identifies the response content and provides the resolved values to `AgentMcpSkillsSource` so it can use them when constructing concrete skills.
8. Having resolved all template variables, the decorator calls `RunAsync`/`RunStreamingAsync` on the inner agent.
9. The inner agent invokes the `AgentSkillsProvider`, which calls `AgentMcpSkillsSource.GetSkillsAsync()`. The source now has all resolved variable values and constructs concrete `AgentMcpSkill` instances from the resolved URIs, so it can provide the skill content if requested by the model.
**API sketch:**
```csharp
// New content types inheriting from MEAI's InputRequestContent/InputResponseContent:
public sealed class McpResourceTemplateValueRequestContent : InputRequestContent
{
public string TemplateDescription { get; }
public string VariableName { get; }
public IReadOnlyList<string> PossibleValues { get; }
public string TemplateUrl { get; }
}
public sealed class McpResourceTemplateValueResponseContent : InputResponseContent
{
public string SelectedValue { get; }
public string TemplateUrl { get; }
}
// Decorator usage:
var provider = new AgentSkillsProviderBuilder()
.UseMcpSkills(mcpClient)
.Build();
AIAgent agent = new ChatClientAgent(chatClient, new ChatClientAgentOptions
{
AIContextProviders = [provider],
});
agent = new McpTemplateSkillResolutionAgent(agent);
```
**Pros:**
- Works in server-side scenarios
- Fits the existing `DelegatingAIAgent` decorator pattern
- Can be composed with other decorators (tool approval, etc.)
**Cons:**
- Complex implementation
- Requires user app awareness of the new content types
- Users need to know that an additional decorator is required for handling MCP template skills, in addition to registering the MCP skills source
- Resolved template variable values must be persisted across conversation turns so the decorator does not re-prompt on subsequent agent runs within the same session
**Note:** This writeup is high-level and may miss details that could change the design. A POC would be needed to validate the approach.
### Open Questions
1. **Completion API limit** - The MCP completion API returns at most 100 values per request and provides no offset/cursor mechanism for enumeration. If a variable has more than 100 possible values, it's unclear how to retrieve the rest - the API only supports prefix-based filtering (typeahead), not bulk pagination.
2. **Multi-variable templates** - A template like `skill://{org}/{product}/SKILL.md` has multiple variables. Should they be resolved sequentially (org first, then product - since product values may depend on org) or presented together?
3. **Caching** - Should resolved template values be saved in the `AgentSession` so the user isn't re-prompted on every agent run? How should they be persisted between sessions?
---
## Part 2: Direct Skill References
This part covers how to let the model read `skill://` URIs referenced directly (e.g., in an MCP server's `instructions`, in a resource, or in another skill's content) without being listed in `skill://index.json`.
### How MCP Skills and Relative Links Work Today
The `AgentMcpSkillsSource` discovers skills by reading the well-known `skill://index.json` resource from the MCP server:
"description": "Convert between world currencies using live rates.",
"url": "skill://currency-converter/SKILL.md"
}
]
}
```
For each `skill-md` entry it creates an `AgentMcpSkill` instance - frontmatter (name/description) comes straight from the entry. The `AgentSkillsProvider` lists the discovered skills in the model's context (name + description):
```xml
<available_skills>
<skill>
<name>unit-converter</name>
<description>Convert between common units.</description>
</skill>
<skill>
<name>currency-converter</name>
<description>Convert between world currencies using live rates.</description>
</skill>
</available_skills>
```
It also provides functions to the model so it can load a skill and access its resources:
```csharp
// Loads the full content of a specific skill.
load_skill(string skillName)
// Reads a resource associated with a skill (references, assets, dynamic data).
The model calls `load_skill("unit-converter")` and receives the skill content:
```markdown
---
name: unit-converter
description: Convert between common units.
---
## Usage
For the full conversion table, see references/units-table.md.
```
The skill body references `references/units-table.md` by relative path. The model calls `read_skill_resource("unit-converter", "references/units-table.md")` and receives the resource content:
```markdown
# Unit Conversion Table
| From | To | Factor |
| miles | km | 1.60934 |
| kg | lbs | 2.20462 |
```
### Direct Reference Examples
A `skill://` URI can appear in any of these locations:
**Server instructions** - the MCP server advertises a skill the model should load:
```text
Follow our coding standards. Load skill://code-standards/SKILL.md for details.
```
**A skill body** - a skill's `SKILL.md` links to a sibling resource:
```markdown
---
name: code-standards
description: Coding standards and conventions.
---
## Naming
Follow the naming rules in skill://code-standards/references/naming.md.
```
**A resource** - the linked resource holds the actual content:
```markdown
# Naming Rules
- Use PascalCase for public members and type names.
- Use camelCase for locals and parameters.
- Prefix interfaces with `I` (e.g. `ISkillReader`).
- Suffix async methods with `Async`.
For examples, see skill://code-standards/references/naming-examples.md.
```
How can the model access content by direct reference?
### Function for Reading Direct Skill References
### Option 1: Extend existing `load_skill` and `read_skill_resource` functions
```csharp
// Added optional 'origin' and a direct skill:// URI is passed in 'skillName'.
| `skill://` link (skill) | `read_skill_uri(uri: "skill://commit-guidelines/SKILL.md", origin:"DirectRefServer")` |
| `skill://` link (resource) | `read_skill_uri(uri: "skill://commit-guidelines/examples/COMMIT_EXAMPLES.md", origin: "DirectRefServer")` |
**Pros:**
- Purely additive - no changes to existing functions needed; `read_skill_uri` can be deferred and added later when direct `skill://` reference support is needed.
- Granular approval: each function can have its own approval gate (like the existing `ScriptApproval` for `run_skill_script`), making per-operation approval for skill loading, resource reading, and direct URI access straightforward to add.
- Both `uri` and `origin` are required - no silent misuse through optional parameters.
- Clean split: `load_skill`/`read_skill_resource` for named skills, `read_skill_uri` for `skill://` links - no parameter ambiguity.
**Cons:**
- Three read functions (`load_skill`, `read_skill_resource`, `read_skill_uri`), not counting `run_skill_script`: larger tool surface than a single-function design.
### Option 3: Collapse `load_skill` and `read_skill_resource` into a single `read_resource` function
```csharp
// Single entrypoint for all skill content. 'uri' is required; 'origin' is optional.
read_resource(string uri, string? origin = null)
```
- `uri` - what to read: a skill name, a relative resource path, or a `skill://` link.
- `origin` - determines how `uri` is interpreted:
- **omitted** → load skill by name (`uri` is the skill name).
- **skill name** → read a relative resource (`uri` is the path within that skill).
- **server name** → read content by the `skill://` link (`uri` is handled by the source identified by the `[Origin: X]` marker).
Dispatch is ordered: null `origin` routes to Case 1; if `origin` names a known skill, routes to Case 2; otherwise tries to find an `ISkillUriReader` whose `CanRead` returns true for `origin` (Case 3).
| `skill://` link (skill) | `read_resource(uri: "skill://commit-guidelines/SKILL.md", origin: "DirectRefServer")` |
| `skill://` link (resource) | `read_resource(uri: "skill://commit-guidelines/examples/COMMIT_EXAMPLES.md", origin: "DirectRefServer")` |
**Pros:**
- Minimal tool surface: one read function instead of two or three (not counting `run_skill_script`) reduces token usage and gives the model fewer choices.
**Cons:**
- No per-operation approval: all cases (skill loading, resource reading, direct URI access) share one function, so approval cannot be scoped to individual operations.
- Unreliable on gpt-4.1-mini: omits `origin` when reading `skill://` links, passes skill name as `origin` when loading a plain skill (should be omitted), and hallucinates resource names (e.g. `API_SPECIFICATION.md`) that do not exist.
---
### Origin Marker
A `skill://` URI does not carry an origin, but the model needs to provide one when reading it. The `origin` is what routes the read call to the source that can handle the URI - the provider uses it to pick the matching source. Since the URI itself carries no such hint, the MCP source injects an `[Origin: ...]` marker wherever a `skill://` URI appears, so the model can read it back and pass it as the `origin` argument.
The marker is only added when the content actually contains `skill://` references. If a piece of content (server instructions, a skill body, or a resource) has no `skill://` URIs, there is nothing for the model to read back, so no marker is injected.
Into **server instructions**, which may mention `skill://` URIs directly:
```
[Origin: code-standards-server]
Follow our coding standards. Load skill://code-standards/SKILL.md for details.
```
Into **skill bodies**, since a `SKILL.md` may reference other `skill://` URIs (a resource file or a related skill):
```
[Origin: code-standards-server]
# Code Standards
For naming conventions, load skill://code-standards/references/naming.md.
```
Into **skill resources**, since a resource may itself reference further `skill://` URIs:
```
[Origin: code-standards-server]
# Naming Rules
- Use PascalCase for public members and type names.
- Use camelCase for locals and parameters.
For examples, see skill://code-standards/references/naming-examples.md.
```
---
### Read-by-URI Capability: Interface vs Base Class Virtual Methods
Now let's look at how an `AgentSkillsSource` can opt in to reading `skill://` URIs and signal that capability to the provider.
### Option 1: New `ISkillUriReader` interface
```csharp
public interface ISkillUriReader
{
// Returns true if this reader can handle the given skill:// URI from the given origin.
bool CanRead(string uri, string origin);
// Reads and returns the content for the given skill:// URI.
The provider may treat a source implementing `ISkillUriReader` as the signal to advertise `read_skill_uri`: if at least one registered source implements the interface, the function is exposed to the model; otherwise it is not.
### Option 2 (Proposed): Virtual methods on `AgentSkillsSource` base class
```csharp
public abstract class AgentSkillsSource
{
// New members for reading by URI.
// Whether this source can read by URI; drives whether read_skill_uri is advertised. Off by default.
public virtual bool SupportsReadByUri => false;
// Returns true if this source can handle the given skill:// URI from the given origin.
public virtual bool CanReadByUri(string uri, string origin) => false;
// Reads and returns the content for the given skill:// URI.
// Reads content by skill:// URI from the MCP server.
public override Task<string?> ReadByUriAsync(string uri, string origin, CancellationToken cancellationToken)
=> /* resolve uri via the MCP server identified by origin */;
```
All sources inherit the methods, so there is no type signal - `SupportsReadByUri` fills that role. The function is advertised when any registered source returns `true`.
### Comparison
| Aspect | Option 1: Interface | Option 2: Base class virtual methods |
| Discovery | Service locator | Direct call on source |
| Advertising signal | Interface implementation | `SupportsReadByUri` flag |
| Adding new members | Breaking change | Non-breaking |
| Complexity | Higher | Lower |
---
### Include MCP Server Instructions Into Agent Instructions
MCP server instructions may contain the `skill://` references the model needs, so we want to surface them in the agent's instructions. But they can also carry system prompts or behavioral directives irrelevant to the agent, polluting context - so inclusion is **opt-in** via the `IncludeServerInstructions` option:
```csharp
public sealed class AgentMcpSkillsSourceOptions
{
// When true, the MCP server's instructions are injected into the agent instructions. Off by default.
public bool IncludeServerInstructions { get; set; }
Following direct `skill://` references is **disabled by default** and activated via an option. When enabled, the provider advertises the read function to the model, and the source injects the `[Origin: ...]` marker into all content provided by the MCP server that contains `skill://` references. When disabled, no function is advertised and no marker is injected.
### Template Variable Resolution: Callback vs Decorator (Part 1)
**Postponed.** Deferring this decision until:
- We have a concrete list of scenarios that require template variable resolution.
- The skills-over-MCP spec is released (it is still a draft, so the design may change).
- There is a strong signal of demand from users or the ecosystem.
### Function for Reading Direct Skill References (Part 2)
**Postponed.** Leaning toward **Option 2 - dedicated `read_skill_uri` function alongside existing ones** (purely additive, and each function can have its own approval gate for granular per-operation approval), but deferring the decision until:
- The skills-over-MCP spec is released (it is still a draft, so the design may change).
- There is a strong signal of demand from users or the ecosystem.
### Read-by-URI Capability: Interface vs Base Class (Part 2)
**Postponed.** Leaning toward **Option 2 - virtual methods on `AgentSkillsSource`** (non-breaking, lower complexity, and a natural fit with the existing base class hierarchy), but deferring the decision until:
- The skills-over-MCP spec is released (it is still a draft, so the design may change).
- There is a strong signal of demand from users or the ecosystem.
The method naming (`SupportsReadByUri`, `CanReadByUri`, `ReadByUriAsync`) should also be abstracted a little more before adoption, so the same members can be reused when a similar direct-reference concept is needed for other skill types (e.g. file skills).
[ADR-0026](0026-hosted-session-identity-context.md) sourced the hosted-agent end-user identity from `ResponseContext.Isolation` (an `IsolationContext` typed `UserIsolationKey` / `ChatIsolationKey`), injected by the platform as the `x-agent-user-isolation-key` and `x-agent-chat-isolation-key` headers.
`Azure.AI.AgentServer.*` 2.0.0 (responses protocol `2.0.0`) removes that surface. `ResponseContext.Isolation` is gone; the platform now exposes `ResponseContext.PlatformContext` (a `PlatformContext` typed `UserIdKey` and `CallId`), populated from the `x-agent-user-id` and `x-agent-foundry-call-id` headers. The chat isolation key no longer exists, and a new per-request **call id** is introduced that first-party Foundry services (the toolbox proxy in particular) require on outbound calls to resolve the server-side-stored caller context. The hosting layer in `Microsoft.Agents.AI.Foundry.Hosting` had to migrate to this contract without changing the public shape that samples and providers depend on.
## Decision Drivers
- Track the breaking `Azure.AI.AgentServer.*` 2.0.0 surface (`PlatformContext` replacing `Isolation`) while keeping the same per-user partitioning guarantees from ADR-0026.
- Keep the change **internal**: existing hosted samples and `AIContextProvider`s must not need code changes. `session.GetHostedContext().UserId`, `HostedSessionIsolationKeyProvider`, and `AddFoundryResponses` stay source-compatible.
- Forward the new per-request call id verbatim on outbound calls to Foundry first-party services so per-user toolbox OAuth consent and other server-side caller-context lookups keep working.
- Remain resilient on protocol `1.0.0`: when only the legacy headers are present, `UserIdKey` still resolves and `CallId` is simply absent.
- Preserve the strict-resume tamper defense from ADR-0026 with identity now reduced to user only.
## Considered Options
For the identity source:
1.**Map `ResponseContext.PlatformContext.UserIdKey`** into the existing `HostedSessionContext` (user only), keeping ADR-0026's storage shape and read accessor.
2. Keep a `ChatId` slot on `HostedSessionContext` for backward source-compatibility, populated from `CallId` or left null.
For the call id propagation:
A. **A request-scoped ambient (`HostedCallContext`, an `AsyncLocal<string?>`)** set by the handler and re-applied before each egress point, read by the outbound delegating handler.
B. Thread the call id through every method signature down to the toolbox bearer handler.
For session keying (previously implied by the conversation/chat pairing):
I. **`HostedConversationKey`** resolving a stable partition from `conversation_id ?? partition(previous_response_id) ?? partition(responseId)`.
II. Continue keying on the container session id (`FOUNDRY_AGENT_SESSION_ID`).
## Decision Outcome
Chosen: **Option 1** for identity, **Option A** for call id, **Option I** for session keying.
Rationale:
- **`ChatId` dropped (Option 2 rejected).** The platform no longer supplies a chat key; carrying a synthetic one would invent identity the trust boundary does not provide. `HostedSessionContext` becomes user-only (`HostedSessionContext(string userId)` / `UserId`), and the strict-resume check validates `UserId` alone. The corresponding `HostedFoundryMemoryProviderScopes` values `PerChat` and `PerUserAndChat` are removed; `PerUser` is retained.
- **Ambient call id (Option B rejected).** Writing `HostedCallContext.CallId` inside the streaming `async IAsyncEnumerable` iterator is reverted across each `yield`, so a single up-front assignment is lost before the toolbox/MCP egress runs. The handler therefore captures `context.PlatformContext?.CallId` once and **re-applies it immediately before each egress point**; `FoundryToolboxBearerTokenHandler` forwards it as `x-agent-foundry-call-id`. The ambient is request-scoped and never leaks into the caller's execution context (guarded by a unit test).
- **`HostedConversationKey` (Option II rejected).** One container serves many conversations for its lifetime, so the container session id cannot key per-conversation state. The partition key is derived from the conversation/`previous_response_id`/minted response id instead.
Implementation summary in `Microsoft.Agents.AI.Foundry.Hosting`:
| Type | Visibility | Change vs ADR-0026 |
|---|---|---|
| `HostedSessionContext` | public sealed | Now user-only (`UserId`); `ChatId` removed. |
Package manifests bump the responses container protocol to `2.0.0` (invocations stays `1.0.0`).
## Consequences
Positive:
- Per-user memory partitioning and the strict-resume tamper defense from ADR-0026 are preserved with no public API churn for samples or providers.
- Per-user toolbox OAuth consent and other server-side caller-context lookups keep working because the per-request call id is forwarded on egress.
- Works unchanged on protocol `1.0.0` (no call id) and `2.0.0`.
Negative:
-`HostedSessionContext.ChatId` and the `PerChat` / `PerUserAndChat` memory scopes are removed; any out-of-tree consumer that referenced them must move to user-scoped partitioning.
- The call id must be re-applied before every egress point because of the async-iterator `AsyncLocal` revert; a missed re-apply silently drops the header. This is covered by unit tests.
## Out of scope
- HMAC tamper signatures over the persisted context remain unimplemented; equality comparison against `ResponseContext.PlatformContext` on every request is sufficient because the platform sets the header at the trust boundary.
- The per-request `User` field on `CreateResponse` is still intentionally not consumed.
FIDES now secures remote MCP integration end-to-end:
- **Tool labels from hints**: `apply_mcp_security_labels(...)` maps MCP hints (`readOnlyHint`, `openWorldHint`) to FIDES tool properties.
- **Safe sink defaults**: tools not explicitly marked `readOnlyHint=True` are treated as potential sinks and receive `max_allowed_confidentiality=public`.
- **Result labels from metadata**: MCP result `_meta` is propagated via `__mcp_result_meta__`; `_meta.ifc` is parsed into `security_label` per result item.
- **`SecureMCPToolProxy` convenience**: wraps MCP tools/URLs and applies this labeling automatically on connect.
This behavior is used with the GitHub MCP server when `X-MCP-Features: ifc_labels` is passed, which causes the server to return IFC labels in `_meta` (for example `{"ifc": {"integrity": "untrusted", "confidentiality": "public"}}`).
<PackageVersion Include="MessagePack" Version="3.1.7" /> <!-- Transitive dependency of Aspire pinned to newer version due to vulnerability in 2.5.192 -->
ExpectedOutputDescription=["The output should show a computer automation session processing simulated browser screenshots with iteration steps and a final response describing search results."],
Inputs=["My laptop won't start","The laptop is now working, thank you!"],
InputDelayMs=5000,
ExpectedOutputDescription=["The output should show a customer support workflow processing a laptop issue, with agent responses providing troubleshooting or support."],
},
@@ -405,26 +405,16 @@ internal static class WorkflowSamples
ExpectedOutputDescription=["The output should show a declarative workflow executing generated code, processing a math question and producing a result."],
ExpectedOutputDescription=["The output should show a workflow calling function tools (e.g. a menu plugin) to answer a question about restaurant specials."],
@@ -443,7 +433,7 @@ internal static class WorkflowSamples
ExpectedOutputDescription=["The output should show a workflow invoking a function tool (e.g. a menu plugin) to answer a question about the soup of the day."],
@@ -473,8 +463,8 @@ internal static class WorkflowSamples
Inputs=["How do I use Azure OpenAI with my data?"],
InputDelayMs=3000,
ExpectedOutputDescription=["The output should show a workflow using Foundry Toolbox MCP tools to search Microsoft Learn documentation and web search to provide a summary of results."],
@@ -484,8 +474,8 @@ internal static class WorkflowSamples
Inputs=["Search for .NET tutorials on Microsoft Learn"],
InputDelayMs=3000,
ExpectedOutputDescription=["The output should show a workflow using MCP tools to search Microsoft Learn documentation and provide a summary of results."],
@@ -495,8 +485,8 @@ internal static class WorkflowSamples
ExpectedOutputDescription=["The output should show a student-teacher workflow where a student asks a math question and a teacher provides the answer."],
@@ -517,8 +507,8 @@ internal static class WorkflowSamples
ExpectedOutputDescription=["The output should show a workflow using an MCP tool with approval to search Microsoft Learn, followed by an exit from the input loop."],
- **Thread IDs** (as `ConversationId`) for conversation context
- **Run IDs** (as `ResponseId`) for tracking individual executions
## Security considerations
`ConversationId` keeps request/response continuity. It is not proof that the caller owns that conversation. In multi-user deployments, authenticate each AG-UI request and authorize conversation access using your application's real boundary, such as the authenticated user, tenant, or workspace.
If your ASP.NET Core host shares session storage across users, pair `MapAGUI` with an isolation strategy such as `UseClaimsBasedSessionIsolation(...)` so the storage key includes a principal-specific dimension instead of relying on the conversation identifier alone.
varendpoint=Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")??thrownewInvalidOperationException("AZURE_OPENAI_ENDPOINT environment variable is not set.");
varendpoint=Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT")??thrownewInvalidOperationException("FOUNDRY_PROJECT_ENDPOINT environment variable is not set.");
# OpenTelemetry Aspire Demo with Microsoft Foundry
This demo showcases the integration of OpenTelemetry with the Microsoft Agent Framework using Azure OpenAI and .NET Aspire Dashboard for telemetry visualization.
This demo showcases the integration of OpenTelemetry with the Microsoft Agent Framework using Microsoft Foundry and the .NET Aspire Dashboard for telemetry visualization.
## Overview
@@ -15,7 +15,7 @@ The demo consists of three main components:
$env:FOUNDRY_MODEL="gpt-5.4-mini" # Optional, defaults to gpt-5.4-mini
```
**Note**: This demo uses Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Azure OpenAI resource.
**Note**: This demo uses Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Foundry project.
### [Optional] Application Insights Setup
Set the following environment variables:
@@ -56,7 +56,7 @@ The easiest way to run the demo is using the provided PowerShell script:
instructions:"You are a helpful assistant that can help with Microsoft documentation questions. Use the Microsoft Learn MCP tool to search for documentation.",
instructions:"You are a helpful assistant that can help with Microsoft documentation questions. Use the Microsoft Learn MCP tool to search for documentation. In the output, indicate which tool you used if any.",
conststringAgentInstructions="You are a personal math tutor. When asked a math question, write and run code using the python tool to answer the question.";
stringendpoint=Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT")??thrownewInvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set.");
conststringAgentInstructions="You are a helpful assistant that can use the countries API to retrieve information about countries by their currency code.";
conststringAgentInstructions="You are a helpful assistant that can retrieve the latest currency exchange rates using the Frankfurter API. Always call the API to get live data rather than guessing.";
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
stringendpoint=Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT")??thrownewInvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set.");
stringendpoint=Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT")??thrownewInvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set.");
stringendpoint=Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT")??thrownewInvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set.");
stringendpoint=Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT")??thrownewInvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set.");
This sample demonstrates how to configure **auto-approval rules** for skill tools using the `UseToolApproval` middleware and `AgentSkillsProvider`'s built-in approval rules.
It builds on the [file-based skills sample](../Agent_Step01_FileBasedSkills/) by adding `ToolApprovalAgent` middleware that auto-approves read-only skill operations while still prompting for script execution.
## What it demonstrates
- All tools exposed by `AgentSkillsProvider` (`load_skill`, `read_skill_resource`, `run_skill_script`) always require approval by default
- Multiple ways to configure auto-approval (see below)
- Handling approval prompts for script execution via `ToolApprovalRequestContent`
## Configuring Auto-Approval
Auto-approval rules are passed to `ToolApprovalAgentOptions.AutoApprovalRules` when calling `UseToolApproval`. Rules are evaluated in order; the first rule returning `true` auto-approves the call.
### Option 1: Built-in read-only rule
Auto-approves `load_skill` and `read_skill_resource` while still prompting for `run_skill_script`:
varguestPath=Environment.GetEnvironmentVariable("HYPERLIGHT_PYTHON_GUEST_PATH")??thrownewInvalidOperationException("HYPERLIGHT_PYTHON_GUEST_PATH is not set.");
@@ -19,13 +18,12 @@ using var codeAct = new HyperlightCodeActProvider(HyperlightCodeActProviderOptio
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIAgentagent=newAzureOpenAIClient(
AIAgentagent=newAIProjectClient(
newUri(endpoint),
newDefaultAzureCredential())
.GetChatClient(deploymentName)
.AsAIAgent(newChatClientAgentOptions()
{
ChatOptions=new(){Instructions="You are a helpful assistant. When the user asks something quantitative, write Python and call `execute_code` instead of guessing."},
ChatOptions=new(){ModelId=deploymentName,Instructions="You are a helpful assistant. When the user asks something quantitative, write Python and call `execute_code` instead of guessing."},
varguestPath=Environment.GetEnvironmentVariable("HYPERLIGHT_PYTHON_GUEST_PATH")??thrownewInvalidOperationException("HYPERLIGHT_PYTHON_GUEST_PATH is not set.");
AIFunctionfetchDocs=AIFunctionFactory.Create(
@@ -42,13 +41,12 @@ using var codeAct = new HyperlightCodeActProvider(options);
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIAgentagent=newAzureOpenAIClient(
AIAgentagent=newAIProjectClient(
newUri(endpoint),
newDefaultAzureCredential())
.GetChatClient(deploymentName)
.AsAIAgent(newChatClientAgentOptions()
{
ChatOptions=new(){Instructions="You are a helpful assistant. Prefer orchestrating your work in a single `execute_code` block using `call_tool(...)` over issuing many direct tool calls."},
ChatOptions=new(){ModelId=deploymentName,Instructions="You are a helpful assistant. Prefer orchestrating your work in a single `execute_code` block using `call_tool(...)` over issuing many direct tool calls."},
varguestPath=Environment.GetEnvironmentVariable("HYPERLIGHT_PYTHON_GUEST_PATH")??thrownewInvalidOperationException("HYPERLIGHT_PYTHON_GUEST_PATH is not set.");
AIFunctioncalculate=AIFunctionFactory.Create(
@@ -34,10 +33,9 @@ var instructions =
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIAgentagent=newAzureOpenAIClient(
newUri(endpoint),
newDefaultAzureCredential())
.GetChatClient(deploymentName)
AIAgentagent=aiProjectClient
.AsAIAgent(newChatClientAgentOptions()
{
ChatOptions=new(){Instructions="You are a friendly travel assistant. Use known memories about the user when responding, and do not invent details."},
ChatOptions=new(){ModelId=deploymentName,Instructions="You are a friendly travel assistant. Use known memories about the user when responding, and do not invent details."},
// The stateInitializer can be used to customize the Mem0 scope per session and it will be called each time a session
// is encountered by the Mem0Provider that does not already have Mem0Provider state stored on the session.
// If each session should have its own Mem0 scope, you can create a new id per session via the stateInitializer, e.g.:
// The TextSearchProvider runs a search against the vector store via the TextSearchStore before each model invocation and injects the results into the model context.
// The TextSearchStore is a sample store implementation that hardcodes a storage schema and uses the vector store to store and retrieve documents.
// Create the AI agent with the TextSearchProvider as the AI context provider.
AIAgentagent=azureOpenAIClient
.GetChatClient(deploymentName)
AIAgentagent=aiProjectClient
.AsAIAgent(newChatClientAgentOptions
{
ChatOptions=new(){Instructions="You are a helpful support specialist for Contoso Outdoors. Answer questions using the provided context and cite the source document when available."},
ChatOptions=new(){ModelId=deploymentName,Instructions="You are a helpful support specialist for Contoso Outdoors. Answer questions using the provided context and cite the source document when available."},
// While the sample is using Qdrant, it can easily be replaced with any other vector store that implements the Microsoft.Extensions.VectorData abstractions.
// The TextSearchProvider runs a search against the vector store before each model invocation and injects the results into the model context.
usingAzure.AI.OpenAI;
usingAzure.AI.Projects;
usingAzure.Identity;
usingMicrosoft.Agents.AI;
usingMicrosoft.Extensions.AI;
usingMicrosoft.Extensions.VectorData;
usingMicrosoft.SemanticKernel.Connectors.Qdrant;
usingOpenAI.Chat;
usingQdrant.Client;
varendpoint=Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")??thrownewInvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
// Create the AI agent with the TextSearchProvider as the AI context provider.
AIAgentagent=azureOpenAIClient
.GetChatClient(deploymentName)
AIAgentagent=aiProjectClient
.AsAIAgent(newChatClientAgentOptions
{
ChatOptions=new(){Instructions="You are a helpful support specialist for the Microsoft Agent Framework. Answer questions using the provided context and cite the source document when available. Keep responses brief."},
ChatOptions=new(){ModelId=deploymentName,Instructions="You are a helpful support specialist for the Microsoft Agent Framework. Answer questions using the provided context and cite the source document when available. Keep responses brief."},
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIAgentagent=newAzureOpenAIClient(
AIAgentagent=newAIProjectClient(
newUri(endpoint),
newDefaultAzureCredential())
.GetChatClient(deploymentName)
.AsAIAgent(newChatClientAgentOptions
{
ChatOptions=new(){Instructions="You are a helpful support specialist for Contoso Outdoors. Answer questions using the provided context and cite the source document when available."},
ChatOptions=new(){ModelId=deploymentName,Instructions="You are a helpful support specialist for Contoso Outdoors. Answer questions using the provided context and cite the source document when available."},
// Create a sample function tool that the agent can use.
[Description("Get the weather for a given location.")]
@@ -26,11 +28,10 @@ static string GetWeather([Description("The location to get the weather for.")] s
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIAgentagent=newAzureOpenAIClient(
AIAgentagent=newAIProjectClient(
newUri(endpoint),
newDefaultAzureCredential())
.GetChatClient(deploymentName)
.AsAIAgent(instructions:"You are a helpful assistant",tools:[newApprovalRequiredAIFunction(AIFunctionFactory.Create(GetWeather))]);
.AsAIAgent(model:deploymentName,instructions:"You are a helpful assistant",tools:[newApprovalRequiredAIFunction(AIFunctionFactory.Create(GetWeather))]);
// Call the agent and check if there are any function approval requests to handle.
// For simplicity, we are assuming here that only function approvals are pending.
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.