* 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>