Commit Graph

2174 Commits

  • Bump Python package versions for 1.7.0 release (#6142)
    Bumps the released 1.6.0 packages agent-framework, agent-framework-core, agent-framework-foundry, and agent-framework-openai to 1.7.0, with root continuing to exactly pin agent-framework-core[all]. Bumps the changed prerelease packages agent-framework-a2a, agent-framework-chatkit, agent-framework-declarative, agent-framework-devui, and agent-framework-foundry-hosting to the 260528 date stamp, raises core floors on the packages included in this release, raises Foundry's OpenAI floor alongside OpenAI, and raises ChatKit's openai-chatkit floor to the minimum version required by the current typed API usage. No beta cohort bump was applied; the absent mistal/mistral package was intentionally not bumped because no such package exists in this branch.
  • Python: [Breaking] Remove Python-only declarative actions and rename alias kinds to C# canonical names (#6126)
    * Remove Python-only declarative actions and rename alias kinds to C# canonical names
    
    * Address PR comments.
    
    * Address PR comments.
    
    * Reduce verbose and duplicate output from sample workflow.
  • Python: fix: pass Foundry agent default headers (#6040)
    * fix: pass Foundry agent default headers
    
    * test: loosen Foundry default header assertions
  • Python: Allow hosted checkpoints to restore MessageRole (#6049)
    * Python: Allow hosted checkpoints to restore MessageRole
    
    Allow Responses hosting checkpoint storage to deserialize the Azure Responses MessageRole enum that hosted workflows can persist inside Agent Framework Message objects.
    
    Add regression coverage for both direct load() and the hosted get_latest() restore path, including the plain-storage failure mode where list_checkpoints logs the blocked type and get_latest() returns None.
    
    Ruff also normalizes a duplicate contextlib import in the touched hosting module.
    
    * Address MessageRole checkpoint review comments
    
    * Cover hosted MessageRole checkpoint restore path
  • Python: Align c# and python TodoProvider tool names (#6107)
    * Align c# and python TodoProvider tool names
    
    * Potential fix for pull request finding
    
    Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
    
    * Address PR review: remove __slots__ and add typed schemas for tool params
    
    - Remove __slots__ from TodoItem, TodoInput, and TodoCompleteInput classes
      (not needed for low-instance-count objects and hinders dev scenarios)
    - Add _TodoAddItemSchema and _TodoCompleteItemSchema TypedDicts to provide
      proper JSON schema for todos_add and todos_complete tool parameters
    - Use typing_extensions for Python 3.10 compatibility
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: read headers defensively to support stream wrappers without .headers (#6028) (#6029)
    `OpenAIChatClient._inner_get_response()` reads `.headers` on the raw streaming
    response returned by `client.responses.with_raw_response.create(stream=True)`
    (and its three sibling call sites - retrieve-streaming, non-streaming create
    and background retrieve) to surface the `x-ms-served-model` Azure header,
    introduced in #5910.
    
    When `azure-ai-projects>=2.1.0` experimental GenAI tracing is enabled
    (`AZURE_EXPERIMENTAL_ENABLE_GENAI_TRACING=true`), the instrumentor wraps the
    raw streaming response in an inline `AsyncStreamWrapper` that exposes
    `.response` but not `.headers`. Reading `raw_create_response.headers` then
    raises `AttributeError: 'AsyncStreamWrapper' object has no attribute 'headers'`,
    which `FoundryChatClient` rethrows as a `ChatClientException` and breaks every
    streaming call (workflows and free chat).
    
    Fix: read the header dict via `getattr(raw_response, "headers", None)` at all
    four call sites. `_extract_served_model()` already short-circuits on `None`,
    so the served-model surfacing degrades gracefully (model stays the deployment
    alias) instead of crashing when the response is wrapped by an instrumentor
    that does not proxy `.headers`.
    
    Regression test added:
    `test_streaming_response_without_headers_attribute_does_not_crash`
    simulates a stream wrapper that raises `AttributeError` on `.headers` and
    asserts the stream still completes with the deployment alias as `update.model`.
    
    Fixes #6028
    
    Co-authored-by: Emilien Mottet <emilien.mottet@michelin.com>
  • feat(a2a): add A2AAgentSession with reference_task_ids and input-required support (#5980)
    * feat(a2a): link follow-up messages via reference_task_ids
    
    Track the task_id from A2A responses (task, status_update, artifact_update,
    and message payloads) on session.state and include it as reference_task_ids
    on subsequent outgoing messages. This enables remote agents to correlate
    follow-up messages as task refinements per the A2A spec.
    
    Resolves #5938
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * feat(a2a): add A2AAgentSession for typed protocol state tracking
    
    Introduce A2AAgentSession (subclass of AgentSession) with context_id,
    task_id, and task_state properties. This follows the DurableAgentSession
    pattern and mirrors the .NET A2AAgentSession design.
    
    - Track task_id, context_id, and task_state from all response payload types
    - Validate context_id consistency (raise on mismatch)
    - Auto-assign server-generated context_id when not set
    - Only A2AAgentSession gets reference tracking (no state dict fallback)
    - Plain AgentSession continues to work without reference tracking
    - Add serialization support (to_dict/from_dict)
    - Export via agent_framework.a2a and agent_framework_a2a
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * style: remove unnecessary string annotation (pyupgrade)
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix: use AgentSession.from_dict for state deserialization
    
    Avoids importing private _deserialize_state, matching the
    DurableAgentSession pattern.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix: track context_id from message payloads in A2AAgentSession
    
    Previously, context_id was only captured from task, status_update, and
    artifact_update payloads. Message-only responses (which carry context_id
    but may lack task_id) were silently lost. This fix:
    
    - Captures msg.context_id in the message handler
    - Persists session state when either last_task_id or last_context_id is
      present (not only when task_id is truthy)
    - Only updates task_id/task_state when a task_id was actually returned
    - Adds a test for message-only context_id tracking
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * addressed comments
    
    * Gate status content to INPUT_REQUIRED/terminal states (match .NET)
    
    Match .NET's GetUserInputRequests pattern: only emit TaskStatusUpdateEvent
    message content when state is INPUT_REQUIRED or terminal. Intermediate
    status text (WORKING, SUBMITTED) is no longer surfaced to callers.
    
    When state is INPUT_REQUIRED, set additional_properties['input_required']
    = True so callers can distinguish input requests from final responses.
    
    Closes #5937
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address review: remove message task_id tracking, defensive fallbacks, and input_required flag
    
    - Do not track task_id from Message payloads (simple interactions
      without task tracking)
    - Remove 'or last_task_id' fallback from status_update and
      artifact_update handlers (spec guarantees task_id is always set)
    - Remove additional_properties['input_required'] flag (content gating
      to INPUT_REQUIRED/terminal states is the signal itself)
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Fix deprecated asyncio.iscoroutinefunction usage in test_cleanup_hooks.py (#4563)
    Fixes #4522
    
    Replace deprecated `asyncio.iscoroutinefunction()` with `inspect.iscoroutinefunction()`
    to resolve Python 3.13+ deprecation warning.
    
    Changes:
    - Added `import inspect` to imports
    - Replaced `asyncio.iscoroutinefunction(hook)` with `inspect.iscoroutinefunction(hook)` on line 126
    - This makes the code consistent with other test methods in the same file (lines 201, 236)
    
    The rest of the file already uses `inspect.iscoroutinefunction()` correctly, making
    this change consistent with the existing codebase pattern.
    
    🤖 Generated with [Claude Code](https://claude.com/claude-code)
    
    Co-authored-by: Claude <noreply@anthropic.com>
    Co-authored-by: Tao Chen <taochen@microsoft.com>
  • .NET: [BREAKING] Remove Support for Code-Gen in Declarative Workflows (#6095)
    * Removed
    
    * Remove sample
    
    * Remove orphaned code-gen related code path
    
    * Remove remaining references to code gen.
    
    ---------
    
    Co-authored-by: Chris Rickman <crickman@microsoft.com>
    Co-authored-by: Chris <66376200+crickman@users.noreply.github.com>
  • Python: fix: keep citation get_url metadata (#6037)
    * fix: keep citation get_url metadata
    
    * fix: satisfy citation metadata mypy check
  • .NET: Add MCP-based skills support (skill-md type) (#6108)
    * Add MCP-based skills support
    
    - Add AgentMcpSkill, AgentMcpSkillResource, AgentMcpSkillsSource, and McpSkillIndex to Microsoft.Agents.AI.Mcp
    - Add AgentSkillsProviderBuilderMcpExtensions for DI integration
    - Add Agent_Step06_McpBasedSkills sample project
    - Add unit tests for AgentMcpSkillsSource
    - Update solution file and project references
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Remove unnecessary [Experimental] attributes from MCP package
    
    The package is already alpha, so the [Experimental] attribute is redundant.
    Removed from both AgentSkillsProviderBuilderMcpExtensions and
    AgentMcpSkillsSource classes.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Make Agent_Step06_McpBasedSkills self-contained and add to verify-samples
    
    Embed an internal MCP server (launched via --server flag as a child process)
    that serves skill://index.json and skill://unit-converter/SKILL.md resources,
    replacing the external MCP_SKILLS_ENDPOINT dependency. The sample now uses
    StdioClientTransport and a fixed prompt instead of an interactive loop.
    
    Added SampleDefinition to AgentsSamples.cs for automated verification.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Sort usings
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Add a HarnessAgent with available features and sample (#6041)
    * Add a HarnessAgent with available features and sample
    
    * Fix formatting
    
    * Address PR comments and fix mypy error
    
    * Add web search support to HarnessAgent
    
    * Fix build warning
    
    * Apply suggestions from code review
    
    Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
    
    * Address PR comments
    
    * Address PR comments
    
    * Address further PR comments.
    
    * Fix markdown broken link
    
    ---------
    
    Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
  • Python: feat(foundry): add to_prompt_agent / deploy_as_prompt_agent (experimental) (#5959)
    * feat(foundry): add experimental to_prompt_agent converter
    
    Adds `to_prompt_agent(agent)`, an experimental converter
    (`ExperimentalFeature.TO_PROMPT_AGENT`) that turns an Agent Framework
    `Agent` into a Foundry `PromptAgentDefinition` ready to publish via
    `AIProjectClient.agents.create_version(...)`.
    
    Behaviour:
    
    * `agent.client` must be a `FoundryChatClient` (or subclass); otherwise
      `TypeError` is raised. The model deployment name is lifted from the
      bound client so the same Agent definition used for local runs can be
      published as a hosted prompt agent without restating the model.
    * Foundry SDK tool instances (from `FoundryChatClient.get_*_tool()`) are
      passed through unchanged. AF `FunctionTool`s (and `@tool`-decorated
      callables) are emitted as Foundry `FunctionTool` declarations.
    * Local AF MCP tools cannot be expressed in a `PromptAgentDefinition`;
      the converter raises `ValueError` and points at
      `FoundryChatClient.get_mcp_tool()` for hosted MCP servers.
    * The converter walks both `agent.default_options["tools"]` and
      `agent.mcp_tools` because `normalize_tools()` splits local MCP off
      into its own list.
    
    Re-exported through the `agent_framework.foundry` lazy-loading namespace
    (updates both `__init__.py` and the `__init__.pyi` type stub).
    
    Adds a portable-agent sample showing the same `Agent` driven through
    both `agent.run(...)` and `to_prompt_agent(agent)`, and a README section
    covering the new converter.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * chore(samples): remove snippet tags from portable agent sample
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * chore(samples): inline FoundryChatClient and enable prompt-agent publish
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * chore(samples): drop async credential context manager
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * docs(foundry): trim README to_prompt_agent example to publish-only flow
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * docs(foundry): note FoundryAgent runs @tool callables for deployed prompt agents
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix(foundry): address review comments on to_prompt_agent converter
    
    * Construct `PromptAgentDefinition` `Tool` from a dict via `**tool_item`
      unpacking rather than the positional Mapping constructor \u2014 cleaner and
      matches the typical Pydantic / Azure SDK pattern.
    * Drop the redundant `isinstance(mcp_tool, MCPTool)` guard in
      `_convert_tools`; the parameter is already typed `Iterable[MCPTool]` so
      the second `raise` was unreachable. The remaining single `raise`
      fires for every entry as intended.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix(foundry): match Agent.__init__ model resolution in to_prompt_agent
    
    * Read the model from `agent.default_options.get("model")` first,
      falling back to `agent.client.model`. This mirrors the order
      `Agent.__init__` uses (`_agents.py:740`) when assembling
      default_options, so the model the agent runs with is the same model
      the converter publishes \u2014 e.g. when the caller passes
      `default_options={"model": "..."}` to override the bound client.
    * Updated the missing-model error message to point at both the client
      and the default_options paths.
    * Added tests:
      * tool-only agent with no `instructions` produces a definition
        where `instructions` is `None` and is omitted from the dict
        payload (`Agent.__init__` strips None values from default_options
        before storing them).
      * `default_options['model']` wins over the bound client's model.
      * Fallback to client.model when default_options has no model.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * feat(foundry): add deploy_as_prompt_agent helper + samples
    
    Adds `deploy_as_prompt_agent(agent)`, a convenience wrapper around
    `to_prompt_agent` that reuses the bound FoundryChatClient's project
    client to call `project_client.agents.create_version(...)`. Defaults
    `agent_name` / `description` from `agent.name` / `agent.description`
    so the Agent stays the single source of truth.
    
    * Exposed from `agent_framework_foundry` and the lazy-loading
      `agent_framework.foundry` namespace (including the .pyi stub).
    * Marked experimental with the existing
      `ExperimentalFeature.TO_PROMPT_AGENT` tag.
    * Tests cover the happy path, name/description defaulting, explicit
      override, no-name error, metadata + description forwarding, extra
      kwargs passthrough, and the experimental metadata.
    
    Samples:
    * Renamed the existing sample to `creating_prompt_agents.py`, drops
      'portable' wording, presents `deploy_as_prompt_agent` first as the
      recommended path and `to_prompt_agent` + `AIProjectClient` as the
      two-step alternative, and adds a cleanup step that deletes the
      published agent so re-runs stay idempotent.
    * New `using_prompt_agents.py` shows the end-to-end loop: deploy the
      agent, connect to it with `FoundryAgent` passing the same local
      `@tool` callable, run a query against the deployed prompt agent,
      then clean up.
    
    README updated to introduce `deploy_as_prompt_agent` as the
    recommended path and link to both runnable samples.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix(foundry): restore missing-model ValueError in to_prompt_agent
    
    The check was accidentally dropped while reworking docstrings in the
    previous commit. Test `test_to_prompt_agent_rejects_missing_model`
    exercises this path and was failing on CI as a result.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * refactor(foundry): rename deploy_as_prompt_agent -> create_prompt_agent
    
    Renames the helper across the foundry package, core lazy-loader stubs,
    tests, README and samples. The new name better matches the action
    performed (a prompt-agent definition is created in Foundry) and is
    consistent with the surrounding ''create_*'' API surface.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * refactor(foundry): drop create_prompt_agent, enrich to_prompt_agent params
    
    Remove the create_prompt_agent helper and consolidate on to_prompt_agent.
    Expose every PromptAgentDefinition parameter that has either an Agent
    Framework equivalent (sourced from default_options) or no equivalent
    (accepted as a keyword argument).
    
    * default_options-sourced (with kwarg overrides):
      temperature, top_p, string tool_choice
    * kwarg-only Foundry knobs:
      reasoning, text, structured_inputs, rai_config, ToolChoiceParam tool_choice
    
    Precedence is always: explicit keyword > default_options entry > unset.
    
    Tests cover every path (defaults, default_options, kwargs, kwarg override).
    Samples and README rewritten around the enriched to_prompt_agent.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * refactor(foundry): single source of truth for prompt-agent options
    
    Stop duplicating the generation-parameter surface between FoundryChatOptions
    and to_prompt_agent. Translate every field with an Agent Framework equivalent
    (temperature, top_p, tool_choice, reasoning, response_format/text/verbosity)
    from agent.default_options via a new RawFoundryChatClient helper
    _prepare_prompt_agent_options. Only Foundry-specific fields with no AF
    equivalent — structured_inputs and rai_config — remain as keyword arguments
    on to_prompt_agent.
    
    - tool_choice is dropped when there are no tools (mirrors _prepare_options
      semantics and avoids polluting tool-less prompt agents with Agent.__init__'s
      'auto' default).
    - response_format Pydantic models route through
      openai.lib._parsing._responses.type_to_text_format_param; dict shapes go
      through the existing _prepare_response_and_text_format helper.
    - default_options is not mutated; text dict is defensively copied.
    
    Tests, README, and creating_prompt_agents.py sample updated to reflect the
    new single-source model.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * docs(foundry): consolidate prompt-agent sample
    
    Drop creating_prompt_agents.py (the publish-only variant) and rename
    using_prompt_agents.py to foundry_prompt_agents.py so the single sample
    covers the full convert -> publish -> connect -> run loop. Update the
    README link list accordingly.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * docs(foundry): run local Agent + deployed agent in same sample
    
    Add an agent.run() call against the local Agent before publishing, then run
    the deployed prompt agent on the same query. Expand the docstring with a
    compare-and-contrast covering runtime/latency, configurability, and
    persistence/sharing differences between the two execution paths.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * test(foundry): cover conflicting response_format + text.format in to_prompt_agent
    
    Exercises the ValueError path when a Pydantic response_format would overwrite
    an explicit text.format mapping with a different shape. Lifts _chat_client.py
    coverage from 89% to 90%.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * refactor(foundry): move _prepare_prompt_agent_options into _to_prompt_agent
    
    Lift the translation helper off RawFoundryChatClient and into the
    _to_prompt_agent module as a module-private function that takes the client
    as its first argument. The chat client no longer needs to carry a method
    whose only consumer is the prompt-agent converter, while still serving as
    the source of the request-path helper (_prepare_response_and_text_format)
    that the converter reuses for dict-shaped response_format values.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * docs(python): codify GA terminology + post-run docs review
    
    Add two pieces of guidance to python/AGENTS.md:
    
    * Terminology - reserve 'GA' for hosted services; use 'released' or 'stable'
      for Agent Framework code/features to match the feature-lifecycle stages.
    * Maintaining Documentation - review AGENTS.md and skills at the end of every
      run and update any guidance the conversation made stale; before adding a
      new principle, ask the user to confirm it should be captured.
    
    Also pulls in a docstring fix in foundry_prompt_agents.py that swaps the
    stray 'GA' for 'released', applying the new terminology rule.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * address PR review: strict=True default, Tool._deserialize dispatch, sample cleanup safety
    
    - FunctionTool published as strict=True so the server-side schema validation
      matches what the local FoundryAgent(tools=[same_callable]) dispatcher
      enforces. AF FunctionTool has no 'strict' attribute, so the safer default
      is used uniformly instead of silently downgrading to a permissive contract.
    - _validate_mapping_tool now dispatches through ProjectsTool._deserialize so
      dict-shaped tools rehydrate to the concrete subclass (FunctionTool,
      WebSearchTool, ...) via the 'type' discriminator instead of returning a
      generic Tool. Added a test that asserts isinstance(WebSearchTool) and a
      new test for the function-typed dict path.
    - foundry_prompt_agents.py sample now wraps credential + project client in
      async with and the create_version / run flow in try/finally so a failure
      on connect or run still deletes the published prompt agent rather than
      leaving an orphaned, billable resource in the user's Foundry project.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix(ci): correct linkspector ignorePattern typo (./pulls -> ./pull)
    
    GitHub PR URLs use the singular segment /pull/N (compare to /issues/N
    for issues). The existing './pulls' ignore pattern never matched
    anything as a result, so legitimately stale PR links (e.g. PRs deleted
    from forks) surface as linkspector failures on unrelated PRs.
    
    This is the same convention the './issues' rule above already follows.
    Fixes the markdown-link-check failure on a dangling link in
    dotnet/src/Microsoft.Agents.AI.DurableTask/CHANGELOG.md.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Add a BackgroundAgentsProvider for python (#6069)
    * Add a BackgroundAgentsProvider for python
    
    * Address PR comments and fix linting warnings
    
    * Address PR comment
  • Python: Fix DevUI streaming memory growth regression (#6038)
    * Fix DevUI streaming memory growth regression
    
    Bounds retained streaming/debug state in DevUI and strengthens browser regression coverage for long streamed responses.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address DevUI memory review feedback
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fix DevUI bundle trailing whitespace
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: fix(openai): guard against null delta in streaming chunks from non-co… (#5734)
    * fix(openai): guard against null delta in streaming chunks from non-compliant providers (#5732)
    
    * chore: resolve nit and align with project style
    
    ---------
    
    Co-authored-by: Sergey Borisov <sergey.borisov@dataimpact.io>
    Co-authored-by: Giles Odigwe <79032838+giles17@users.noreply.github.com>
  • .NET: Updating version for dotnet release 1.7.0 (#6093)
    * Updating version for dotnet release 1.6.3
    
    * Change to minor version bump.
    
    ---------
    
    Co-authored-by: Ben Thomas <25218250+alliscode@users.noreply.github.com>
  • Python: Add Python parity sample for invoking Foundry Toolbox tools from declarative workflows (#5933)
    * Add Python parity sample for invoking Foundry Toolbox tools from declarative workflows
    
    * Python: address PR review on declarative toolbox sample
    
    Two security fixes for PR #5933:
    
    1. Add safe_mode flag to WorkflowFactory (default True) mirroring
       AgentFactory. Gates =Env.* exposure inside DeclarativeWorkflowState
       PowerFx symbols via _safe_mode_context, so workflow YAML loaded from
       untrusted sources no longer leaks the host's full os.environ snapshot
       into PowerFx evaluation. The flag is also forwarded to the
       internally-constructed AgentFactory so inline agent definitions
       follow the same policy.
    
    2. Pin the invoke_foundry_toolbox_mcp sample's _client_provider to the
       resolved toolbox endpoint. The bearer-authenticated httpx client is
       now only returned when MCPToolInvocation.server_url matches the
       toolbox URL case-insensitively; any other URL gets None (the default
       unauthenticated path), preventing the Foundry AAD bearer token from
       being attached to a mis-configured or injected server URL. Mirrors
       the .NET sample's httpClientProvider guard.
    
    The sample is updated to opt in to safe_mode=False because its YAML
    intentionally uses =Env.FOUNDRY_TOOLBOX_* to keep configuration in env
    vars under the developer's control.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fix pyright issues.
    
    * Addressed PR comments.
    
    * Fix CI pipelines.
    
    * Resolve PR comments
    
    * Revamped sample to address PR comments.
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Align ModeProvider tool names and instructions (#6071)
    * Align ModeProvider tool names and instructions
    
    * Address PR comments
  • .NET: [Breaking] Refactor AgentSkill API to async resource and script lookup (#6030)
    * .NET: Refactor AgentSkill API to async resource and script lookup
    
    Replace property-based AgentSkill.Content, Resources, and Scripts with
    async-by-name lookup methods plus boolean availability flags:
    
    - Content (string getter) -> GetContentAsync(CancellationToken)
    - Resources (full list) -> HasResources + GetResourceAsync(name, ct)
    - Scripts (full list) -> HasScripts + GetScriptAsync(name, ct)
    
    This makes the API friendlier for sources like MCP where enumerating all
    resources up front is expensive or impossible, and allows skill implementations
    to fetch content lazily.
    
    Subclass changes:
    - AgentFileSkill and AgentInlineSkill implement the new async API while
      preserving content caching.
    - AgentClassSkill<TSelf> keeps virtual Resources/Scripts properties for
      reflection-based discovery and seals the new HasResources/HasScripts/
      GetResourceAsync/GetScriptAsync overrides. Its previously non-thread-safe
      lazy initialization is replaced with Lazy<T> (default thread-safety) wired
      up in a new protected constructor, so concurrent first-access from multiple
      threads is safe.
    - AgentSkillsProvider calls the new async API and exposes 
    ead_skill_resource
      / load_skill / 
    un_skill_script tools that await the per-name lookups.
    
    Includes baseline CompatibilitySuppressions.xml entries for the removed
    property getters.
    
    Tests:
    - Direct coverage for HasResources, HasScripts, GetResourceAsync, and
      GetScriptAsync on all three skill implementations (positive, missing-name,
      and no-resources/no-scripts cases).
    - Thread-safety regression test for AgentClassSkill<TSelf> that exercises
      concurrent first-access to Resources, Scripts, and GetContentAsync from
      many tasks and asserts all observers see the same cached instance.
    - Provider-level coverage for the 
    ead_skill_resource tool (invocation +
      error paths) and for the previously untested error paths of load_skill
      and 
    un_skill_script (empty names, skill/resource/script not found).
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address PR review comments
    
    - Move GetScriptAsync inside try/catch in RunSkillScriptAsync for error-handling parity
    - Remove dead _reflectedResources branch from AgentSkillTestExtensions
    - Fix XML docs to reference virtual Resources/Scripts properties (not sealed methods)
    - Add Async suffix to async test methods per naming convention
    - Make no-await tests synchronous to eliminate CS1998
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fix formatting: add UTF-8 BOM and remove unused using
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fix XML cref: Resources/Scripts are on AgentClassSkill<TSelf>
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Remove HasResources and HasScripts properties from AgentSkill
    
    Drop the virtual HasResources and HasScripts properties from AgentSkill
    and all concrete subclasses (AgentFileSkill, AgentInlineSkill,
    AgentClassSkill). AgentSkillsProvider now always includes all three
    tools (load_skill, read_skill_resource, run_skill_script) and both
    instruction blocks, since the tools already handle missing
    resources/scripts gracefully.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Add blank line for readability in file-based skills sample
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fix HostedAgentSkillsPatternTests for always-included tools
    
    Update assertions to expect read_skill_resource and run_skill_script
    tools are always present, matching the new behavior.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • .NET: Add Hosted-AgentSkills sample with Foundry Skills integration (#6013)
    * .NET: Add Hosted-AgentSkills sample for Foundry Skills integration
    
    Add a new hosted agent sample that demonstrates how to load behavioral
    guidelines from Foundry Skills at startup using AgentSkillsProvider and
    the progressive disclosure pattern (advertise -> load on demand).
    
    The sample:
    - Downloads SKILL.md files from Foundry via ProjectAgentSkills SDK
    - Extracts ZIP archives with zip-slip protection
    - Wires skills into AgentSkillsProvider as an AIContextProvider
    - Hosts the agent via the Responses protocol
    
    Ships two Contoso Outdoors skills matching the Python sample (PR #5822):
    - support-style: tone, formatting, signature guidelines
    - escalation-policy: when and how to escalate tickets
    
    Includes convenience provisioning gated behind PROVISION_SAMPLE_SKILLS
    env var, clearly documented as NOT a production pattern.
    
    Closes #5776
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * .NET: Add unit tests and integration test for Hosted-AgentSkills
    
    Unit tests (14 tests, all passing):
    - ZIP extraction with zip-slip guard (valid archive, traversal attack,
      sibling-prefix attack, directory entries)
    - Skill name validation (rejects dots, separators, traversal patterns)
    - AgentSkillsProvider with downloaded skills (advertises both skills,
      load_skill returns canary tokens, unknown skill returns error)
    
    Container integration test:
    - New 'agent-skills' scenario in the test container that creates
      Contoso Outdoors skills on disk and wires AgentSkillsProvider
    - AgentSkillsHostedAgentFixture + 4 integration tests verifying:
      - Routine questions load support-style skill (STYLE-CANARY-3318)
      - Escalation triggers load escalation-policy (ESC-CANARY-7742)
      - Skills are advertised in system prompt
      - load_skill tool is invoked via FunctionCallContent
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * .NET: Add smoke test, bootstrap, and docs for agent-skills integration
    
    - Add scripts/smoke.ps1 for local Docker smoke testing: builds the
      contributor image, runs the container, verifies both skills are loaded
      via canary tokens (STYLE-CANARY-3318, ESC-CANARY-7742)
    - Add 'agent-skills' to the bootstrap script scenario list
    - Add agent-skills row to the integration test README scenarios table
    - Exclude HostedAgentSkillsPatternTests from net472 (uses net8.0+ APIs)
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * .NET: Update commented-out package versions to latest across all hosted samples
    
    Update the end-user PackageReference versions (in the commented-out
    sections) from 1.0.0 to the current latest NuGet versions:
    
    - Microsoft.Agents.AI: 1.6.1
    - Microsoft.Agents.AI.Foundry: 1.6.1-preview.260514.1
    - Microsoft.Agents.AI.Foundry.Hosting: 1.6.1-preview.260514.1
    - Microsoft.Agents.AI.Hosting: 1.6.1-preview.260514.1
    - Microsoft.Agents.AI.OpenAI: 1.6.1
    - Microsoft.Agents.AI.Workflows: 1.6.1
    
    Also adds explicit versions to Hosted-Workflow-Handoff which had bare
    PackageReference entries without Version attributes.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * .NET: Fix broken markdown links in Hosted-AgentSkills README
    
    Remove references to non-existent ../../README.md. Replace with
    inline instructions matching other hosted samples that don't have
    a parent README.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * .NET: Use OS-appropriate string comparison in zip-slip guard
    
    Use Ordinal on Unix (case-sensitive FS) and OrdinalIgnoreCase on
    Windows to prevent case-based path bypass on Linux containers.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • .NET: fix parallel tool call rendering in AGUI translation layer (#6009)
    Fix three interlocked bugs that prevent parallel tool calls from rendering
    correctly in AG-UI protocol clients:
    
    Bug #1: Scope synthetic MessageId fallback to text events only. The shared
    streamingMessageId was leaking into ToolCallStartEvent.ParentMessageId,
    causing all parallel tool calls to collapse into one FE card.
    
    Bug #2: Make ToolCallResultEvent.MessageId deterministically unique using
    result-{CallId} format. MEAI's FunctionInvokingChatClient batches all
    results with a shared MessageId, collapsing them in FE reconciliation.
    
    Bug #3: Coalesce consecutive assistant-tool-call messages in AsChatMessages.
    Once Bug #1 is fixed, the FE produces separate AGUIAssistantMessage per
    tool call. On multi-turn replay these become consecutive assistant messages
    without intervening tool results, triggering HTTP 400 from Azure OpenAI.
    
    Remove the now-dead ContainsToolResult helper introduced by PR #5800.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • .NET: HarnessConsole: Improve rendering perf / reduce flickering (#6014)
    * HarnessConsole: Improve rendering perf / reduce flickering
    
    * Address PR comments
  • .NET: Add MCP long-running task support for MCP client tools (#5994)
    * Add MCP long-running task support for MCP client tools
    
    * Fixed project file formatting issue.
    
    * Removed experimentation tag from MCP alpha project.
    
    * Addressed PR comments
  • .NET: Add Magentic Orchestration Sample (#5823)
    * Add Magentic orchestration sample scaffold
    
    Agent-Logs-Url: https://github.com/microsoft/agent-framework/sessions/8799740a-74d8-4100-b6f6-76dcd0418c87
    
    Co-authored-by: lokitoth <6936551+lokitoth@users.noreply.github.com>
    
    * Validate Magentic orchestration sample
    
    Agent-Logs-Url: https://github.com/microsoft/agent-framework/sessions/8799740a-74d8-4100-b6f6-76dcd0418c87
    
    Co-authored-by: lokitoth <6936551+lokitoth@users.noreply.github.com>
    
    * Document follow-up changes for the Magentic .NET sample
    
    Agent-Logs-Url: https://github.com/microsoft/agent-framework/sessions/caa3488f-d6f5-494d-a928-a45d6a98b3c3
    
    Co-authored-by: lokitoth <6936551+lokitoth@users.noreply.github.com>
    
    * Remove CHANGES.md from Magentic sample
    
    Agent-Logs-Url: https://github.com/microsoft/agent-framework/sessions/ffab38e2-37f9-4643-a782-20680573965a
    
    Co-authored-by: lokitoth <6936551+lokitoth@users.noreply.github.com>
    
    * Fix PauseIfInteractive to also skip when stdout is redirected
    
    Agent-Logs-Url: https://github.com/microsoft/agent-framework/sessions/07ddf735-29cc-4775-b588-fd71ca76fa58
    
    Co-authored-by: lokitoth <6936551+lokitoth@users.noreply.github.com>
    
    * fix: Update for PR Review Feedback
    
    * fix: Update Sample README for PR Feedback
    
    ---------
    
    Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
    Co-authored-by: lokitoth <6936551+lokitoth@users.noreply.github.com>
    Co-authored-by: Jacob Alber <jaalber@microsoft.com>
  • fix: populate MessageId from TaskStatusUpdateEvent.Status.Message (#6043)
    When A2AAgent receives a TaskStatusUpdateEvent during streaming,
    ConvertToAgentResponseUpdate now sets AgentResponseUpdate.MessageId
    from Status.Message.MessageId when the message is present.
    
    This fixes the missing message correlation metadata reported in
    microsoft/agent-framework#4987.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: fix(core): point @experimental warnings at user code, not stdlib internals (#5996)
    * fix(core): point @experimental warnings at user code, not stdlib internals
    
    Previously the wrappers installed by @experimental called warnings.warn
    with a fixed stacklevel=3. ABCMeta inserts an extra abc.__new__ frame
    when an experimental ABC is subclassed, so the warning landed inside
    abc.py (or <frozen abc>:106 on modern CPython) instead of the user's
    class Sub(...) line.
    
    Resolve the user frame by walking inspect.currentframe(), skipping
    frames whose module name is abc/functools/typing/contextlib (or
    submodules), then emit via warnings.warn_explicit so the recorded
    filename/lineno point at user code. Falls back to warnings.warn with
    stacklevel=2 if no user frame is found. Module-name matching is used
    because frozen stdlib modules report '<frozen abc>' as their filename.
    
    Also install a one-line warnings.formatwarning specifically for
    FeatureStageWarning so 'file:line: ExperimentalWarning: [ID] Name ...'
    prints without the secondary source-snippet line. Other categories
    delegate to the stdlib default formatter unchanged.
    
    Added a regression test that subclasses an @experimental ABC inside
    warnings.catch_warnings and asserts the recorded filename equals the
    test file.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix(core): address review feedback on @experimental warning fix
    
    - Make _install_feature_stage_formatter idempotent: tag the installed
      formatter with a marker attribute and short-circuit re-installation,
      so re-imports/reloads don't wrap the formatter on top of itself.
      Also expose the previous formatter via __wrapped__ for restoration.
    - Avoid leaking frame references in _resolve_user_frame: capture data
      into plain locals inside try and del frame/candidate in finally,
      per CPython's guidance on inspect.currentframe usage.
    - Drop redundant _WARNED_FEATURES.clear() in the new ABC subclass test
      (the autouse fixture already handles it).
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * changed query for foundry web search test
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: bump package versions for 1.6.0 release (#6017)
    * Python: bump package versions for 1.6.0 release
    
    - Released cohort (agent-framework, core, openai, foundry): 1.5.0 -> 1.6.0
    - Beta packages (21 packages): 1.0.0b260519 -> 1.0.0b260521
    - Alpha packages (azure-contentunderstanding, foundry-hosting, gemini, monty): 1.0.0a260518/19 -> 1.0.0a260521
    - ag-ui stays at 1.0.0rc2, orchestrations at 1.0.0rc1 (dependency bounds updated)
    - Inter-package dependency lower bounds updated (>=1.5.0,<2 -> >=1.6.0,<2)
    - Update CHANGELOG compare links
    - uv.lock refreshed
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address review: bump RC packages, add shell tool to changelog
    
    - ag-ui: 1.0.0rc2 -> 1.0.0rc3
    - orchestrations: 1.0.0rc1 -> 1.0.0rc2
    - Add shell tool (#5664) to CHANGELOG
    - uv.lock refreshed
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • .NET: Fix declarative workflow regressions for hosted agents (#5905)
    * Fix declarative workflow regressions for hosted agents
    
    Three regressions surfaced when running a declarative workflow as a
    Foundry hosted agent. Together they caused every condition group to fall
    through to elseActions and the raw agent JSON to leak to the caller.
    
    1. AgentProviderExtensions.InvokeAgentAsync forced autoSend to true
       whenever the agent ran on the workflow conversation, which overrode
       the explicit autoSend: false declared in workflow.yaml and streamed
       the raw structured-output JSON straight to the user. Honor the
       caller-supplied autoSend instead.
    
    2. IWorkflowContextExtensions.ReadState / QueueStateUpdateAsync /
       QueueStateResetAsync took the variable name and namespace alias
       directly from PropertyPath.VariableName / NamespaceAlias. Against
       Microsoft.Agents.ObjectModel 2026.2.4.1 those properties return null
       for a dotted reference such as `Local.Triage` even when
       SegmentCount == 2 and IsValid == true, so every assignment threw
       ArgumentNullException via Throw.IfNull. Fall back to Segments() to
       reconstruct the name and alias when the parser returns null.
    
    3. The same ObjectModel version no longer recognizes the user-facing
       `Local` scope alias: VariableScopeNames.IsValidName(`Local`)
       returns false and GetNamespaceFromName(`Local`) returns Unknown, so
       the declarative interpreter's IsManagedScope check fails and the
       State.Set call is silently skipped. Translate the `Local` alias to
       its canonical `Topic` form before forwarding to
       QueueStateUpdateAsync; WorkflowFormulaState.Bind continues to expose
       it as `Local` to PowerFx.
    
    Verified end-to-end against a deployed Foundry hosted agent: the
    declarative triage workflow now routes Technical / Billing / General
    inputs correctly and only the autoSend-eligible messages reach the
    caller.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Hosted-agent HITL: persist session across previous_response_id chains; run approved local AIFunctions
    
    Two regressions hit declarative workflows that use require_approval=true when
    the client chains turns via previous_response_id (no conversation_id):
    
    1. AgentFrameworkResponseHandler keyed the AgentSession store solely on
       conversation_id, so when only previous_response_id was present the
       StateBag (which holds ToolApprovalIdMap) was discarded after each turn.
       The next turn then threw 'No approval mapping recorded for wire id ...'
       in InputConverter.ConvertMcpApprovalResponse.
    
       Fix: fall back to previous_response_id on load and to context.ResponseId
       on save so the response-id chain becomes a valid session key. Conversation
       id remains preferred when present.
    
    2. InvokeFunctionToolExecutor.CaptureResponseAsync only acted on
       FunctionResultContent. In the hosted Foundry path the approval response
       arrives as a ToolApprovalResponseContent with no FunctionResultContent,
       so the local AIFunction never ran and downstream PropertyPath/SendActivity
       consumers (e.g. {Local.RefundResult}) saw empty values.
    
       Fix: when no FunctionResultContent matches but an approved
       ToolApprovalResponseContent does, look up the registered AIFunction by
       name on agentProvider.Functions and invoke it with the evaluated
       arguments, surfacing the result through the existing assignment path.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Apply PropertyPath workaround to initialization path; share + tidy helpers
    
    Address PR #5905 review feedback:
    
    * Move the PropertyPath VariableName/NamespaceAlias fallback and 'Local'
      -> 'Topic' scope remap into a shared internal PropertyPathExtensions
      helper. Materializes Segments() once, names the magic 'Local' alias
      as a const, and carries a TODO referencing the tracking issue.
    
    * Apply the same helper in WorkflowDiagnostics.InitializeDefaults so a
      declared default for a dotted variable like 'Local.Triage' is no
      longer silently skipped at workflow startup (closes the gap flagged
      by the reviewer: runtime ReadState/QueueStateUpdateAsync worked but
      state.Initialize did not).
    
    * Restore the previous strict failure mode on namespace alias by
      wrapping GetNamespaceAlias() in Throw.IfNull at call sites so a
      malformed single-segment path keeps failing fast rather than
      silently passing null to State.Get/Set.
    
    All 821 unit tests pass.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Add tests for AgentProviderExtensions.InvokeAgentAsync autoSend behavior
    
    Covers the autoSend regression fix: when the agent runs on the workflow conversation with autoSend=false, no AgentResponseUpdateEvent or AgentResponseEvent is added to the context. Also covers autoSend=true (events emitted) and autoSend=false on a non-workflow conversation.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Surface SendActivity output via AgentResponseUpdateEvent
    
    SendActivityExecutor previously only emitted the activity text via YieldOutputAsync, which the runtime converts to an AgentResponseEvent. WorkflowSession gates AgentResponseEvent behind includeWorkflowOutputsInResponse, so when a host opts out of summary outputs (the default for AsAIAgent) the SendActivity reply is silently dropped.
    
    Mirror the pattern used by AgentProviderExtensions for autoSend agent invocations: also emit an AgentResponseUpdateEvent, which WorkflowSession yields unconditionally. This makes SendActivity reliably reach chat-protocol clients without requiring includeWorkflowOutputsInResponse = true (which would also duplicate autoSend agent output).
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Revert previous_response_id session-key fallback
    
    The fallback let a session be keyed by an unbroken previous_response_id chain,
    but conversation_id is the right way to thread state across turns: it survives
    shared/branched chains (e.g. when another agent generates a response in between)
    and is the documented model for stateful clients. Restore conversation_id as the
    sole session key and rely on the client to thread it. The InvokeFunctionTool
    approval/local-function half of 1baf4af4d remains.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Set Foundry ProductContext per-executor instead of via PropertyPath workaround
    
    ObjectModel 2026.2.4.1 resolves PropertyPath.VariableName / NamespaceAlias and VariableScopeNames.IsValidName against AsyncLocal<ProductContext> at access time. In hosted-agent scenarios each HTTP request runs on a fresh async context where that AsyncLocal is default, so dotted refs like Local.Triage returned null and the Local scope alias was rejected.
    
    Replace the PropertyPathExtensions helper (which papered over both symptoms) with a single WorkflowDiagnostics.SetFoundryProduct() call at the entry of DeclarativeActionExecutor.HandleAsync. The set writes to the request's logical async context before any code reads PropertyPath, letting the existing parser and scope resolver work as designed.
    
    Validated: 824/824 declarative unit tests pass; technical/billing/general routes all dispatch correctly against a deployed Foundry hosted agent.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address review feedback on InvokeFunctionToolExecutor
    
    - Surface registered-function lookup failures and invocation exceptions via FunctionResultContent.Exception instead of returning the error text as a successful Result, so downstream {Local.X} assignments can distinguish failures from successes.
    
    - Use AIJsonUtilities.DefaultOptions to JSON-serialize non-string function results (matching FunctionInvokingChatClient / ToolBridge), so complex types stay consumable by PropertyPath consumers instead of degrading to Object.ToString().
    
    - Drop the explicit System. prefix on StringComparison / Exception now that the file imports System.
    
    - Add AutoSendTrueOnExternalConversationEmitsResponseEventsAndCopiesMessagesAsync to cover the (autoSend: true, external conversation) quadrant, asserting that response events are emitted and that messages are mirrored to the workflow conversation.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Honor AutoSendIsDefaultValue when computing autoSend
    
    AzureAgentOutput.AutoSend and InvokeToolOutput.AutoSend in
    Microsoft.Agents.ObjectModel 2026.2.4.1 are never null — they
    return a literal-false default when the YAML omits the field.
    The previous null check in Get/AutoSendValue therefore always
    fell through to evaluating the literal false, so every action
    whose YAML had any output block but no explicit autoSend was
    treated as autoSend = false. This was previously masked by
    `autoSend |= isWorkflowConversation` in AgentProviderExtensions
    (removed earlier in this PR to honor explicit autoSend: false),
    which silently re-enabled autoSend on the workflow conversation.
    
    Use AutoSendIsDefaultValue to distinguish an explicit autoSend
    value from the implicit default and treat the implicit default
    as true, restoring the historical behavior for ValidateCaseAsync
    InvokeAgent.yaml (3 InvokeAzureAgent actions, last one captures
    to Local.RatingResponse via output.messages with no autoSend
    specified) while keeping the hosted-agent fix that honors an
    explicit autoSend: false.
    
    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>
  • Python: Shell tool with support for local and Docker (#5664)
    * feat(tools): add cross-OS LocalShellTool in new agent-framework-tools package
    
    Introduces a safe, cross-OS local shell tool as the first citizen of a new
    
    agent-framework-tools workspace package. Supports persistent (default) and
    
    stateless modes across pwsh/powershell.exe/bash/sh, with policy denylist,
    
    allowlist, approval gating, process-tree kill on timeout, output truncation,
    
    and audit hooks. Integrates with existing provider get_shell_tool(func=...)
    
    factories via FunctionTool kind='shell'.
    
    See docs/decisions/0026-builtin-tools-local-shell.md for the full design.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * feat(tools): security hardening for LocalShellTool
    
    Codifies what LocalShellTool does and does not defend against, and
    
    delegates the security-relevant lifecycle primitive to a battle-tested
    
    library instead of hand-rolled per-OS code.
    
    Changes:
    
    - Adopt psutil for cross-OS process-tree termination (executor + session).
    
      Replaces hand-rolled taskkill/killpg with one canonical implementation.
    
    - Resolve taskkill.exe to absolute %SystemRoot%\System32 path so PATH
    
      poisoning cannot redirect us to an attacker-supplied binary.
    
    - Reframe ShellPolicy docstring + ADR + README: denylist is a guardrail,
    
      not a security boundary.
    
    - Require acknowledge_unsafe=True to set approval_mode='never_require',
    
      making the unsafe path explicitly opt-in with a self-documenting name.
    
    - Add tests/test_security.py codifying named CVE-style cases. Defenses
    
      we DO claim are asserted; non-defenses (denylist bypasses via
    
      backslash insertion, variable expansion, interpreter escape, base64,
    
      alternative tools, PowerShell-native verbs) are documented as
    
      expected-to-pass tests so residual risk stays visible.
    
    - Add Threat Model + Confidence Strategy sections to ADR 0026.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * feat(tools): add DockerShellTool sandboxed shell tier
    
    Adds a container-backed shell executor as the recommended pattern for untrusted-input shell workflows. The container provides the security boundary (--network none, non-root user, --read-only, --cap-drop ALL, no-new-privileges, memory/pids limits, tmpfs /tmp), so approval gating is optional unlike LocalShellTool.
    
    Also introduces a ShellExecutor Protocol so callers can plug in custom backends (Firecracker, SSH, WASI) without forking the framework.
    
    Removes the planned HyperlightShellExecutor follow-up from ADR 0026: Hyperlight is a WASM code sandbox with no kernel/userland/shell binary, so a Hyperlight-backed shell is not viable. Docker is the realistic sandbox tier for shell.
    
    Tests: 11 unit tests for argv builders + lifecycle (no Docker daemon required); 3 integration tests gated on is_docker_available().
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix(tools): backport shell-tool fixes from .NET parity review
    
    Applies the applicable subset of bug fixes accumulated during the
    .NET shell-tool PR review (microsoft/agent-framework#5604) to the
    Python shell tool.
    
    A1 - Quote workdir safely in _maybe_reanchor
    
      Previously _tool.py used double-quote interpolation when emitting
      the cd/Set-Location prefix, which expanded $VAR, $(), and backticks
      in the workdir path. A workdir containing shell metacharacters could
      trigger arbitrary command execution before the user command ran.
    
      Replaced with single-quote escaping helpers _quote_posix and
      _quote_powershell that emit literal-string forms safe for both
      hosts.
    
    A5/A6 - Consolidate truncation to a single byte-aware helper
    
      Extracted a shared truncate_head_tail / truncate_text_head_tail
      helper in _truncate.py. The new implementation distributes odd
      caps so head receives floor(cap/2) and tail receives ceil(cap/2)
      bytes, matching the .NET round-9 fix and ensuring no input bytes
      are silently dropped on the boundary.
    
      _session.py previously truncated by Python str length while the
      caller passed _max_output_bytes - the unit mismatch is now gone:
      raw byte buffers go through truncate_head_tail and decoded text
      goes through truncate_text_head_tail.
    
    Unit tests added for the truncate and quote helpers.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * docs(tools): tone down narrative and overconfident comments in shell tool
    
    The shell tool's docstrings and comments contained two patterns that
    the .NET review pushed back on:
    
    - Narrative framing about implementation history ("hard-won",
      "we sidestep", "design inspiration: ...", competitor framework
      name-drops in module docstrings).
    - Overstated security guarantees ("battle-tested",
      "reasonable for untrusted input", "recommended executor for any
      agent that runs commands from untrusted input",
      "destructive commands are blocked", "safe local shell tool",
      "blocks shell injection").
    
    Rewrites the affected docstrings and comments to describe what the
    code does in neutral terms. Behaviour is unchanged.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * feat(tools): add ShellEnvironmentProvider for the Python shell tool
    
    Ports the .NET ShellEnvironmentProvider as a Python ContextProvider
    so agents using LocalShellTool or DockerShellTool can be primed with
    an accurate description of the shell they're talking to (family,
    version, OS, working directory, and which CLIs are available).
    
    The provider runs probes through any ShellExecutor, caches the
    resulting snapshot, and on every before_run extends the session
    instructions with a markdown block describing the shell idiom to
    use. A failed first probe leaves the cache empty so the next call
    retries (no permanent poisoning).
    
    Probe failures from a narrow set of expected error types
    (ShellCommandError, ShellExecutionError, ShellTimeoutError, and
    asyncio.TimeoutError from the per-probe timeout) are recorded as
    None fields in the snapshot. Other exceptions propagate. Tool
    names are validated against ^[A-Za-z0-9._-]+$ before being
    interpolated into a probe command.
    
    Includes 12 unit tests covering happy path, stderr fallback,
    timeout handling, expected/unexpected exception paths, malicious
    tool name rejection, case-insensitive deduplication, retry after
    failure, concurrent first-callers sharing one probe, and the
    default and custom formatter paths.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * docs(tools): document ShellEnvironmentProvider and finish comment cleanup
    
    Add a README section introducing ShellEnvironmentProvider, soften two remaining overconfident security-boundary comments in _executor_base.py and the DockerShellTool class docstring, and add a sample (shell_with_environment_provider.py) that demonstrates the provider in stateless and persistent modes.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * refactor(tools): move shell samples to python/samples/02-agents/tools
    
    The repository convention is to host samples under python/samples/ rather than inside the package directory. Move the two net-new shell samples (allow-list and environment-provider) to python/samples/02-agents/tools/ and drop the in-package samples/ directory; the existing top-level providers/openai/client_with_local_shell.py already covers the basic LocalShellTool walkthrough.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * test(tools): cover confine_workdir default and ShellResult.format_for_model
    
    Two new tests in test_local_shell_tool.py exercise the default confine_workdir=True behaviour on POSIX and PowerShell, asserting that 'cd' inside one persistent-mode call does not leak into the next. A new test_shell_result.py module provides direct unit coverage for every conditional branch of ShellResult.format_for_model (stdout, truncated, stderr, timed_out, exit_code) so regressions in the LLM-facing format are caught immediately.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix(tools): address PR #5664 review feedback
    
    - _tool.py: detect PowerShell via is_powershell() helper instead of basename string match
    
    - _environment.py: use public ContextProvider import (no private _ prefix)
    
    - _session.py: trim _stdout_buf/_stderr_buf after copying to avoid unbounded retention across calls
    
    - _docker.py: short-circuit start()/close() in stateless mode; add configurable shell kwarg (default bash, e.g. 'sh' for alpine)
    
    - tests: parenthesized multi-line assert; alpine integration tests now pass shell='sh'
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix(tools): satisfy CI quality gates
    
    - pyupgrade: drop quoted self-class refs in __aenter__/method annotations
    
    - ruff format: reflow long lines per workspace style
    
    - pyright: assert psutil non-None in optional-import branch; lowercase mutable module globals; annotate _approval_mode as Literal so tool() Literal-typed kwarg is accepted; add ... body to ShellExecutor.run protocol; remove unused deprecated _kill_tree wrapper
    
    - tests: skip docker integration tests on win32 (Windows containers don't support --read-only / alpine images)
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Remove DEFAULT_DENYLIST; document single-session ownership; fix bandit findings
    
    Mirrors the .NET PR #5604 cleanup:
    
    - Remove DEFAULT_DENYLIST from ShellPolicy. ShellPolicy() now ships with an empty deny-list; operators opt into site-specific patterns explicitly. No major agent framework uses regex matching as a primary security control; AutoGen v2 removed theirs. Approval gating + sandbox tier remain the real boundaries.
    
    - Rewrite module / class docstrings to frame ShellPolicy as a UX pre-filter, not a security control.
    
    - Add Single-session ownership paragraphs to ShellExecutor, ShellSession, LocalShellTool, and DockerShellTool: a persistent-mode tool is owned by exactly one conversation / agent session; do not share across users or concurrent conversations.
    
    - Tests now supply explicit deny patterns instead of relying on a default.
    
    - Address Pre-commit Hooks (bandit) CI failures: convert internal-invariant asserts to explicit RuntimeError, annotate intentional subprocess/shell usage with # nosec, document container-internal /tmp paths.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address PR #5664 round-2 review feedback
    
    Deny-list documentation drift:
    
    - README and the OpenAI/local-shell sample no longer claim a built-in deny-list of destructive commands. ShellPolicy is described as an optional, operator-supplied UX pre-filter; the real boundaries remain approval gating and the sandbox tier.
    
    Behavioural fixes called out in review:
    
    - ShellPolicy.evaluate() now denies empty / whitespace-only commands explicitly instead of returning allow with no rationale.
    
    - truncate_head_tail() raises ValueError for cap <= 0 instead of silently returning the full input with truncated=False, which previously could defeat output-capping in callers that mis-configured the budget.
    
    - LocalShellTool.as_function() / DockerShellTool.as_function() return the ShellCommandError text directly so the model sees a single, non-redundant 'Command rejected by policy: …' message instead of the prior duplicated 'Command blocked by policy: Command rejected …' wrapping.
    
    - ShellSession POSIX sentinel trailer now snapshots and restores the prior errexit (set -e) state around the trailer, so a user 'set -e' in the persistent shell is no longer permanently disabled by the next run().
    
    Tests:
    
    - New test_shell_parse_rc.py covers the full _parse_rc() edge-case surface (zero, positive, negative, CRLF, no newline, missing prefix, empty input, non-digits, trailing garbage, partial digits).
    
    - test_policy.py asserts the new empty-command deny.
    
    - test_shell_truncate_and_quote.py asserts ValueError for cap=0 and cap<0.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address PR review feedback for shell tool
    
    - _resolve.py: reject empty/whitespace shell override string
    - _tool.py / _docker.py: mode-aware default tool description (persistent vs stateless)
    - _tool.py: fix misleading workdir docstring (re-anchor, not blocking)
    - _types.py: emit stream-agnostic [output truncated] marker
    - _policy.py: declare _denies/_allows as dataclass fields
    - _environment.py: use $(pwd) instead of $PWD in POSIX probe
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address PR review feedback: shell override flag + probe timeout safety
    
    - _resolve.py: in stateless mode, ensure shell overrides end with -c/-Command so commands aren't misinterpreted as script-file paths.
    - ShellExecutor.run / LocalShellTool.run / DockerShellTool.run now accept an optional 	imeout kwarg; ShellEnvironmentProvider drops the outer asyncio.wait_for and lets the executor enforce the probe timeout internally, so cancellation no longer risks leaving a hung subprocess or corrupted session.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address review feedback: docker isolation + lifecycle robustness
    
    - pyproject.toml: bump agent-framework-core minimum from 1.2.0 to 1.2.2 to align with the rest of the workspace.
    - _docker.py: validate extra_run_args at construction time and reject flags that would dismantle the isolation defaults (--privileged, --cap-add, --security-opt, --network/--net, -v/--volume/--mount, --device, --pid, --ipc, --userns, --user, --read-only, --tmpfs, --add-host, --gpus, --cgroupns, --device-cgroup-rule); also documented the warning on the docstring.
    - _docker._stop_container: retry docker rm -f once and log a warning/error when it does not succeed, so operators can audit leaked containers instead of getting a silent success.
    - _docker._run_stateless timeout path: fall back to docker rm -f when docker kill fails or times out (--rm only reaps on clean exit), and log instead of silently swallowing communicate() errors.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: alliscode <bentho@microsoft.com>
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    Co-authored-by: alliscode <25218250+alliscode@users.noreply.github.com>
  • .NET: Surface x-ms-served-model header as ChatResponse.ModelId for Foundry agents (#5979)
    * .NET: Surface x-ms-served-model header as ChatResponse.ModelId for Foundry agents
    
    Mirrors Python PR #5910. Adds an internal SCM PipelinePolicy that reads the x-ms-served-model HTTP response header on Azure OpenAI Responses calls and writes it into an AsyncLocal box. A DelegatingChatClient sits between OpenTelemetry and the MEAI OpenAIResponsesChatClient and overwrites ChatResponse.ModelId with the served snapshot so OTel spans report the actual model rather than the deployment alias. Wired through all AsAIAgent paths in Microsoft.Agents.AI.Foundry.
    
    * .NET: Fix line endings and BOM on ResponsesAgentServedModelTests
    
    * .NET: Address Copilot review on Foundry served-model PR
    
    - Restore previous ServedModelScope in finally to avoid AsyncLocal leak into caller execution context.
    - Make served-model integration test assertion robust to deployment names that already match the snapshot pattern.
    - Broaden UnitTests csproj comment to cover all conditional removals (net8.0+ requirement).
    
    * .NET: Split ServedModelTests into per-SUT files with regions
    
    Split the combined ServedModelTests.cs into one test class per SUT:
    
    - ServedModelScopeTests.cs (AsyncLocal carrier)
    - ServedModelPolicyTests.cs (SCM pipeline policy)
    - ServedModelChatClientTests.cs (delegating client, with regions for Non-streaming / Streaming / End-to-end)
    
    Shared helpers and fake clients moved into ServedModelTestHelpers.cs.
    
    Csproj net8.0+ exclusion list updated accordingly.
    
    * .NET: Consolidate served-model logic into FoundryChatClient
    
    Move x-ms-served-model header capture from the standalone ServedModelChatClient
    decorator directly into FoundryChatClient, eliminating a separate wrapper that
    had to be applied at every Foundry entry point via WireServedModel().
    
    - Register ServedModelPolicy in FoundryChatClient constructors (alongside the
      existing AgentFrameworkUserAgentPolicy registration)
    - Add StrongBox push/read logic to FoundryChatClient.GetResponseAsync and
      GetStreamingResponseAsync
    - Delete ServedModelChatClient.cs and its unit tests
    - Remove WireServedModel() from FoundryAgent and AIProjectClientExtensions
    - Update ServedModelPolicy/Scope XML docs to reference FoundryChatClient
    - Simplify ServedModelTestHelpers to use FoundryChatClient directly
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Prevent duplicate system instructions in Python telemetry (#5981)
    * Initial plan
    
    * Fix duplicated system instructions in Python telemetry
    
    * Clarify telemetry message filtering
    
    * test: cover separate and in-history system messages
    
    * Clarify observability message logging split
    
    * Simplify observability logging serialization
    
    * Harden observability regression test
    
    * Reuse observability span message serialization
    
    * Clarify observability logging loops
    
    * Polish observability message serialization
    
    * Tighten observability zip checks
    
    * Refactor observability message capture loop
    
    * Fix telemetry logging for separate system instructions
    
    * Refine observability OTEL message typing
    
    * Restore prepended-instruction logging path in _capture_messages
    
    * Revert logging change in _capture_messages; keep chat-history-only logging
    
    ---------
    
    Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
  • .NET: Add shell support to the HarnessAgent (#6005)
    * Add shell support to the HarnessAgent
    
    * Address PR comments
    
    * Address PR comments
  • Python: feat(a2a): use non-streaming transport and return_immediately for background ops (#5963)
    * feat(a2a): use non-streaming transport and return_immediately for background ops
    
    When stream=False, use a client configured with streaming=False so the
    SDK sends a single HTTP POST to message/send instead of opening an SSE
    connection via message/stream. This matches the A2A protocol's design:
    non-streaming calls use direct request/response, streaming calls use
    Server-Sent Events.
    
    Also sets return_immediately=background on SendMessageConfiguration so
    the server respects the caller's intent for background operations.
    
    Changes:
    - Create separate streaming and non-streaming internal clients (sharing
      the same httpx connection pool) to match protocol transport semantics
    - Select non-streaming client for run(stream=False) calls
    - Add SendMessageConfiguration with return_immediately=background
    - Fallback to streaming client when non-streaming unavailable (e.g. user
      provides their own client via constructor)
    - Add tests for client selection and return_immediately behavior
    
    Resolves microsoft/agent-framework#5936
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix: address PR review feedback
    
    - Initialize last_request in MockA2AClient.__init__ for explicit state
    - Use 'is not None' instead of truthiness for _non_streaming_client check
    - Assert return_immediately propagates through non-streaming client path
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix: only set configuration when background=True
    
    Only attach SendMessageConfiguration to the request when background=True,
    keeping requests minimal and preserving server-side defaults for normal
    (foreground) operations. This follows the framework pattern of only
    setting optional fields when they have meaningful values.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix: only set return_immediately for non-streaming background ops
    
    Per the A2A spec, return_immediately only applies to message/send
    (non-streaming). It has no effect on streaming operations. Only set
    the configuration field when both background=True and stream=False.
    
    Adds test verifying streaming+background does not set return_immediately.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • .NET: Add additional openai specific error observers and move them to openai project (#6004)
    * Add additional openai specific error observers and move them to openai project
    
    * Address PR comments
  • .NET: Add background agents support to HarnessAgent (#5977)
    * Add background agents support to HarnessAgent
    
    * Add unit tests
    
    * Address PR comments
  • .NET: Promote FoundryChatClient to public, add file/vector-store helpers and ToPromptAgentAsync converter (#5940)
    * Consolidate Foundry chat client decorators into FoundryChatClient
    
    - Replace AzureAIProjectChatClient and AzureAIProjectResponsesChatClient with a single internal sealed FoundryChatClient that covers three modes (pure responses, server-side agent reference, hosted agent endpoint).
    - Rename AzureAIProjectChatClientExtensions to AIProjectClientExtensions to reflect that it extends AIProjectClient.
    - All four AsAIAgent extension overloads and both FoundryAgent constructors now construct FoundryChatClient internally so the microsoft.foundry telemetry tag is uniform across paths.
    - Introduce AgentFrameworkUserAgentPolicy that stamps agent-framework-dotnet/{version} on outbound requests, mirroring the Python agent-framework-python/{version} contract.
    - Delete the Foundry-local MeaiUserAgentPolicy duplicate; rely on MEAI 10.5.1 to stamp MEAI/{version} automatically.
    - HostedAgentUserAgentPolicy keeps the combined foundry-hosting/agent-framework-dotnet/{version} segment (Python parity) and upgrades the bare segment in place to avoid duplication.
    - Tests reorganized: FoundryChatClientTests, AIProjectClientExtensionsTests, AgentFrameworkUserAgentPolicyTests, MeaiAutoUserAgentVerificationTests, plus in-place upgrade unit tests in HostedOutboundUserAgentTests.
    
    * Promote FoundryChatClient to public; add file/vector-store helpers and ToPromptAgentAsync converter
    
    - Promote FoundryChatClient from internal sealed to public sealed for Python parity, so .NET developers can hold and pass a FoundryChatClient directly the way Python developers do.
    - Mode 3 (hosted agent endpoint) now materializes an AIProjectClient from the parsed project root, making GetService<AIProjectClient>() non-null across all three construction modes. This eliminates the per-mode asymmetry that previously hid project-level helpers from agents constructed via an agent endpoint URL.
    - Add four new instance methods on FoundryChatClient mirroring Python's spec: UploadFileAsync, DeleteFileAsync, CreateVectorStoreAsync (bundles upload + create + wait), DeleteVectorStoreAsync. Single overload each, path-only inputs to start; additional overloads can be added later without breaking callers. All are Experimental, consistent with the rest of the Foundry package.
    - Add ToPromptAgentAsync extension methods on ChatClientAgent and FoundryAgent for the agent-to-prompt-agent converter described in the Foundry spec. Mode 1 (responses API) synthesizes a DeclarativeAgentDefinition from the agent's ChatOptions; mode 2 (server-side agent reference, version, or record) returns the cached or freshly fetched Definition; mode 3 throws InvalidOperationException because no local definition exists to convert.
    - Strict AITool to ResponseTool mapping for mode 1: AIFunction becomes CreateFunctionTool with the function's JSON schema; AITool instances that wrap a ResponseTool unwrap via GetService(typeof(ResponseTool)); anything else throws InvalidOperationException naming the offending tool type. Matches the Python spec's unsupported-tools-raise-ValueError contract.
    - New unit tests: FoundryChatClientVectorStoreTests (22 tests covering all four helpers across the three FoundryChatClient construction modes plus validation and cancellation), FoundryPromptAgentConverterTests (16 tests covering both extension entry points across mode 1 synthesis, mode 2 cached and fetched paths, all failure modes, and a Python-parity guard asserting both extensions produce equivalent definitions for equivalent inputs), plus four new tests in FoundryChatClientTests for the mode 3 AIProjectClient materialization.
    
    * Stop building duplicate ProjectOpenAIClient in FoundryAgent agent-endpoint ctor
    
    After Plan #2's mode-3 AIProjectClient materialization, the inner FoundryChatClient already exposes a project-level AIProjectClient (via GetService) that internally provides the project-level ProjectOpenAIClient via GetProjectOpenAIClient(). FoundryAgent's agent-endpoint constructor was still independently constructing a second project-level ProjectOpenAIClient via the now-redundant CreateProjectLevelOpenAIClientFromAgentEndpoint helper — two handles to the same logical resource.
    
    Refactor: the agent-endpoint constructor now reads the inner FoundryChatClient's materialized AIProjectClient via base.GetService(typeof(AIProjectClient)) and derives the project-level ProjectOpenAIClient from it. The dead helper on both FoundryAgent (private static wrapper) and FoundryChatClient (the actual implementation) is removed. The user-supplied per-agent ClientPipelineOptions primitives (Transport, RetryPolicy, NetworkTimeout, UserAgentApplicationId) are propagated into the materialized AIProjectClientOptions so test-injected transports and explicit retry / timeout / user-agent settings reach the project-level pipeline — preserving the behavior the dead helper used to provide.
    
    Updated AgentEndpointConstructor_GetServiceAIProjectClient_ReturnsNull to its now-correct counterpart AgentEndpointConstructor_GetServiceAIProjectClient_ReturnsNonNull, since after Plan #2 the agent-endpoint ctor surfaces a non-null AIProjectClient (per user direction in Plan #2 Q2).
    
    * Strip duplicated AIProjectClient/ProjectOpenAIClient state from FoundryAgent
    
    Both _aiProjectClient and _projectOpenAIClient fields on FoundryAgent were redundant:
    
    - _aiProjectClient: FoundryAgent's GetService<AIProjectClient> override returned this field, but DelegatingAIAgent.GetService → ChatClientAgent.GetService → FoundryChatClient.GetService<AIProjectClient> already returns the same instance through the delegating chain. Field + override are pure duplication.
    
    - _projectOpenAIClient: only used by FoundryAgent's own GetService<ProjectOpenAIClient> override and by CreateConversationSessionAsync. Per user direction, ProjectOpenAIClient is no longer exposed via GetService on either FoundryChatClient or FoundryAgent — callers retrieve it from the AIProjectClient themselves (aiProjectClient.GetProjectOpenAIClient()) the same way the framework does internally. This eliminates the mode-3 asymmetry where the chat client's stored ProjectOpenAIClient was per-agent (URL /agents/{name}/endpoint/protocols/openai) while the agent's was project-level.
    
    Refactor:
    - Delete both fields on FoundryAgent and the GetService override.
    - Delete the ProjectOpenAIClient branch from FoundryChatClient.GetService.
    - CreateConversationSessionAsync now resolves AIProjectClient at call time via this.GetService<AIProjectClient>() and derives the conversations client from it.
    - Update FoundryChatClient tests that asserted on GetService<ProjectOpenAIClient> to assert Null (deliberate removal).
    - Update FoundryAgent tests AgentEndpointConstructor_GetServiceProjectOpenAIClient_ReturnsNonNull and ProjectEndpointConstructor_GetServiceProjectOpenAIClient_ReturnsNonNull to ...ReturnsNull, and rewrite AgentEndpointConstructor_PropagatesUserAgentApplicationId_ToProjectLevelClient to look up AIProjectClient instead.
    
    No production code (only tests) referenced GetService<ProjectOpenAIClient>, so this is a safe surface reduction. Net: 30 insertions, 61 deletions; FoundryAgent shrinks to a pure delegator with only the two convenience methods (CreateSessionAsync, CreateConversationSessionAsync) on top of the delegating chain.
    
    * Rename FoundryChatClient.HostedAgentName to AgentName and populate it for mode 2
    
    The previous name implied a mode 3 only property tied to the hosted-agent endpoint URL. Today only hosted endpoints surface this name, but conceptually an agent name exists for every server-side agent the client talks to. Renaming to AgentName makes the property general-purpose and ready for future modes where the same chat client may target other server-side agent shapes that are not necessarily 'hosted'.
    
    Mode 2 (server-side agent reference) now mirrors AgentReference.Name into AgentName so callers have a uniform handle regardless of construction mode:
    
    * Mode 1 (pure responses): AgentName is null. There is no agent.
    * Mode 2 (AgentReference): AgentName == AgentReference.Name.
    * Mode 3 (agent endpoint URL): AgentName is parsed from the URL segment as before.
    
    Converter discriminator update: FoundryPromptAgentConverter previously used 'HostedAgentName is not null' to detect mode 3 and reject it. Now that mode 2 also populates AgentName, the mode 3 guard moves to the end of the resolution chain and uses the unambiguous 'AgentName is set AND no AgentReference exists' test. The user-visible error message and behavior are preserved.
    
    Dead-state cleanup spotted during format verify:
    
    * IDE0052 surfaced that FoundryChatClient._projectOpenAIClient is never read since the prior refactor stopped exposing ProjectOpenAIClient via GetService and rewired CreateConversationSessionAsync to resolve the AIProjectClient through the delegating chain. The field is deleted and its three ctor assignments removed.
    * HostedAgentEndpointInner.PerAgentClient only existed to plumb the per-agent ProjectOpenAIClient into that now-deleted field, so the property and its ctor parameter are removed. The local 'perAgentClient' variable inside BuildHostedAgentEndpointInner is still needed to derive the inner IChatClient, but no longer escapes the helper.
    
    Tests:
    
    * Mode1_PureResponses_ReturnsNullForAgentSpecificServices now also asserts AgentName is null.
    * New Mode2_AgentReference_PopulatesAgentNameFromAgentReference asserts the mode 2 mirror.
    * Mode3_HostedAgentEndpoint_ParsesAgentNameFromUrl renamed assertion target HostedAgentName to AgentName.
    
    Verification: 335/335 net10.0, 273/273 net472 Foundry unit; 229/229 Foundry.Hosting unit; format-verify (WSL2 + Docker mcr.microsoft.com/dotnet/sdk:10.0) clean on Microsoft.Agents.AI.Foundry.
    
    * Adopt canonical mode names: Responses Agent, Prompt Agent, Agent Endpoint
    
    Three FoundryChatClient construction modes now have one canonical noun used everywhere.
    
    * Responses Agent (Mode 1): inline ChatClientAgent, project-level Responses API, no server-side def.
    * Prompt Agent (Mode 2): server-side ProjectsAgentDefinition invoked by AgentReference.
    * Agent Endpoint (Mode 3): per-agent URL /agents/{name}/endpoint/protocols/openai. Hosted-or-not.
    
    'Hosted' stays the kind of agent (Microsoft.Agents.AI.Foundry.Hosting). Not synonym of Mode 3.
    
    Rings:
    1. XML docs + error messages use canonical names. en-GB to en-US: centralises, synthesise.
    2. HostedAgentEndpointInner -> AgentEndpointInner, BuildHostedAgentEndpointInner -> BuildAgentEndpointInner.
    3. Tests: Mode1_PureResponses_* -> Mode1_ResponsesAgent_*, Mode2_AgentReference_* -> Mode2_PromptAgent_*, Mode3_HostedAgentEndpoint_* -> Mode3_AgentEndpoint_*.
    
    Pure rename. No behavior change. 335/335 net10 + 273/273 net472 unit, format clean.
    
    * Address PR #5940 design feedback (Q-A through Q-F)
    
    Q-A: poll vector store til status leaves InProgress before return. Exp backoff 250ms-2s. Honor cancel.
    Q-B: try/catch upload loop. Mid-fail = best-effort DeleteFileAsync on already-uploaded ids. Swallow cleanup errors.
    Q-C: pinned AgentReference.Version uses GetAgentVersionAsync. Empty/whitespace/'latest' = GetLatest path.
    Q-D: HostedAgentUserAgentPolicy detects existing combined 'foundry-hosting/...' segment. No double prefix.
    Q-E: mode-3 vector-store test uses fake transport. No DNS to example.com.
    Q-F: no shim. Class always [Experimental] (since 8015e00f5, before dotnet-1.0.0). No compat contract. Callers rename to AIProjectClientExtensions.
    
    Rebase onto origin/main reconciliation: aad20c2b3 added public AsAIAgent(this AIProjectClient, Uri agentEndpoint, ...) extension that calls an internal FoundryAgent(AIProjectClient, Uri, ...) ctor. Reintroduced that ctor + a new FoundryChatClient(AIProjectClient, Uri, ProjectOpenAIClientOptions?) overload that reuses the supplied AIProjectClient's pipeline (via GetProjectResponsesClientForAgentEndpoint) instead of stamping a fresh credential.
    
    Verified: 346/346 net10 + 284/284 net472 Foundry unit, 230/230 Foundry.Hosting unit, format clean.
    
    * Add FoundryAgent helper extensions: UploadFile/DeleteFile/CreateVectorStore/DeleteVectorStore
    
    4 thin forwarders on FoundryAgent that route to the inner FoundryChatClient's helpers via agent.GetService<FoundryChatClient>().X(). Live in existing FoundryAgentExtensions.cs alongside ToPromptAgentAsync.
    
    Throws InvalidOperationException when agent does not expose a FoundryChatClient via GetService (same pattern as ToPromptAgentAsync).
    
    Unit tests: FoundryAgentExtensionsTests covers all 4 forwarders + null-agent ArgumentNullException for each. 8 new tests, 354/354 net10 + 292/292 net472.
    
    Integration tests: parallel FoundryAgentExtensionsTests under Foundry.IntegrationTests mirrors the existing CreateAgent_CreatesAgentWithVectorStoresAsync shape (upload -> create vector store -> FileSearch tool answers question -> cleanup), but routes every helper call through the new FoundryAgent extensions. 4 new IT tests, all verified pass live against the real Foundry project (12-30s each). Skipped by default like the existing vector-store IT.
    
    * Address Sergey's PR review comments
    
    #1 (FoundryAgent.cs:139): drop unused aiProjectClient param from internal FoundryAgent(AIProjectClient, ChatClientAgent) ctor. Was discarded after null-check. Inner FoundryChatClient already surfaces AIProjectClient via GetService. 3 call sites in AIProjectClientExtensions updated.
    
    #2 (FoundryChatClient.cs:376): add pollingTimeout param to CreateVectorStoreAsync. Defaults to 5 min, configurable, Timeout.InfiniteTimeSpan disables. Throws TimeoutException with vector store id and elapsed seconds when bound exceeded. CancellationToken still wins. New unit test PollingTimeout_ThrowsTimeoutExceptionAsync. FoundryAgentExtensions forwarder updated to plumb the new param.
    
    Verified: 355/355 net10 + 293/293 net472 Foundry unit, 230/230 Foundry.Hosting unit, format clean.
  • Python: feat(foundry): add experimental hosted tool factories on FoundryChatClient (#5958)
    * feat(foundry): add experimental hosted tool factories on FoundryChatClient
    
    Adds eight new `@experimental` static factory methods on `FoundryChatClient`
    covering Foundry-hosted tools that previously had no helper:
    
    - get_azure_ai_search_tool
    - get_sharepoint_tool
    - get_fabric_tool
    - get_memory_search_tool
    - get_computer_use_tool
    - get_browser_automation_tool
    - get_bing_custom_search_tool
    - get_a2a_tool
    
    All factories are marked with the new `ExperimentalFeature.FOUNDRY_TOOLS` tag
    and resolve the underlying `azure-ai-projects` preview classes lazily through
    a `_require_sdk_class` helper so older SDK versions still import cleanly and
    fail with a clear `ImportError` only on use.
    
    Tests cover each factory's return type and field wiring, the experimental
    metadata, and the missing-SDK-class fallback.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * test(foundry): address review comments on tool-factory tests
    
    * Skip preview-tool tests gracefully (`_skip_if_sdk_class_missing`) when
      the installed `azure-ai-projects` does not expose the required preview
      class, matching the lazy-import guard in production code so the test
      suite stays green on older SDK installs.
    * Add `filterwarnings("ignore::FutureWarning")` to each new tool-factory
      test (and the parametrized metadata test) so they remain stable under
      strict warning configurations \u2014 the global dedup in
      `_feature_stage._WARNED_FEATURES` makes `pytest.warns` brittle across
      ordered runs.
    * Use `monkeypatch.setattr(..., None, raising=False)` instead of
      `delattr` in the missing-SDK-class test so it works for modules that
      implement PEP 562 `__getattr__`.
    * Split the long `get_bing_custom_search_tool` return into two lines for
      readability.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix(foundry): harden tool-factory kwargs against silent override
    
    * Reorder the dict-literal kwargs assembly in get_azure_ai_search_tool,
      get_memory_search_tool, and get_bing_custom_search_tool so explicit
      parameters always take precedence over **kwargs (matching the safe
      pattern already used in get_a2a_tool). This prevents a caller
      passing `project_connection_id`, `index_name`, `memory_store_name`,
      `scope`, or `instance_name` through `**kwargs` from silently
      overriding the explicit security-sensitive arguments.
    * Update the README experimental note to reflect once-per-feature-id
      dedup semantics of `_feature_stage._WARNED_FEATURES` rather than
      claiming a per-factory "first use" warning.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * feat(foundry): split FOUNDRY_TOOLS / FOUNDRY_PREVIEW_TOOLS, add bing-grounding
    
    - Add ExperimentalFeature.FOUNDRY_PREVIEW_TOOLS to distinguish wrappers around
      preview Foundry SDK tool classes (Sharepoint/Fabric/Memory/ComputerUse/
      BrowserAutomation/BingCustomSearch/A2A) from FOUNDRY_TOOLS, which is for
      GA-SDK wrappers that are simply new in agent-framework-foundry
      (AzureAISearch, BingGrounding).
    - Add get_bing_grounding_tool factory and a 'Choosing a web grounding tool'
      comparison block on get_web_search_tool / get_bing_grounding_tool /
      get_bing_custom_search_tool docstrings.
    - Drop the _require_sdk_class lazy resolver: every guarded class is available
      at azure-ai-projects>=2.1.0 (the package floor), so import them eagerly.
      Concrete return types replace 'Any'.
    - README: split the experimental factories into two tables, one per feature
      flag, with a note explaining the distinction.
    - Tests: split into FOUNDRY_TOOLS / FOUNDRY_PREVIEW_TOOLS factory cases;
      drop the obsolete missing-SDK-class ImportError test.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • ci: pin third-party GitHub Actions to commit SHAs (#5972)
    Replaces every floating tag in our workflow and composite action files
    with an immutable 40-character commit SHA, keeping the original `# vX`
    comment so Dependabot can still propose version bumps. 186 occurrences
    across 25 workflows and 2 composite actions.
    
    Also widens the github-actions Dependabot entry to use the plural
    `directories` key with `/.github/actions/*` so composite actions under
    `.github/actions/<name>/action.yml` are kept up to date. Previously
    Dependabot only scanned `.github/workflows` and the repo-root
    `action.yml`, leaving our `python-setup` and `sample-validation-setup`
    composite actions unmaintained.
  • Python: Show more authentication methods in Foundry Toolbox MCP (#5719)
    * Show more authentication methods in Foundry Toolbox MCP
    
    * Remove hardcoded toolbox version num
    
    * Add Foundry MCP OAuth consent handling
    
    * Use message instead of the dedicated item type
    
    * Go back to using OAuthConsentRequestOutputItem
    
    * WIP: sample testing
    
    * Update error code
    
    * Address review on Foundry Toolbox MCP samples
    
    Reviewed feedback addressed:
    
    - Drop the branch-pinned `git+https://...@feature/...` entries from
      `04_foundry_toolbox/requirements.txt`; restore the simple comment + `mcp`
      runtime dep. The git pins were only useful while iterating on the PR and
      shouldn't ship. (eavanvalkenburg)
    
    - Fix the `/toolsets/` typo in both `04_foundry_toolbox/README.md` and
      `06_files/README.md`. Verified empirically against the
      research_toolbox in the test workspace: the toolbox MCP gateway lives at
      `/toolboxes/{name}/mcp?api-version=v1` and requires the
      `Foundry-Features: Toolboxes=V1Preview` header. `/toolsets/{name}/mcp`
      returns 403 with `preview_feature_required: Toolsets=V1Preview` (a
      different opt-in feature).
    
    - Wrap `httpx.AsyncClient(...)` in `async with ... as http_client:` in both
      samples so the connection pool is cleaned up. (Copilot reviewer)
    
    - Make the `TOOLBOX_NAME` env var consistent in both samples. Previously the
      tool name silently fell back to `"toolbox"` when `TOOLBOX_NAME` was unset,
      but `resolve_toolbox_endpoint()` still required `TOOLBOX_NAME` and would
      raise `KeyError`. The samples now resolve the endpoint once and derive the
      tool name from the resolved URL when `TOOLBOX_NAME` isn't set, so the
      local tool name always matches the upstream toolbox identity regardless
      of which env var the user set. (Copilot reviewer)
    
    - Rename `_responses.is_consent_error` to `consent_url_from_error`: the
      helper returns `str | None` (the consent URL), not a bool, so the new
      name matches behavior. Update the test class accordingly. (eavanvalkenburg)
    
    - Tighten `_handle_inner_agent`'s lazy-entry catch from `Exception` to
      `AgentFrameworkException`, the type the MCP layer actually wraps consent
      errors in via `MCPStreamableHTTPTool.__aenter__` →
      `ToolExecutionException(inner_exception=mcp_error)`. Network failures,
      cancellations, and other non-framework exceptions now propagate normally
      instead of being briefly caught and re-raised. The test helper
      `_make_consent_error` is updated to use `ToolExecutionException` so it
      matches the real-world wrapping. (eavanvalkenburg)
    
    - Clarify the `github_pat` description in `agent.manifest.yaml` to note
      it's only needed when the PAT-based connection (`github-mcp-pat-conn`)
      is chosen; users selecting the OAuth2 connection (`github-mcp-oauth-conn`)
      can leave it empty. (Copilot reviewer)
    
    Validation: ran both samples end-to-end against a real Foundry toolbox
    (`research_toolbox`) -- the samples connect successfully and the agent
    lists the toolbox's MCP tools (`api_specs___fetch_azure_rest_api_docs`,
    etc.). `uv run poe test -P foundry_hosting` passes (119 tests), pyright +
    mypy clean.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * docs: fix broken Foundry samples link in 04_foundry_toolbox README
    
    The previous URL pointed to an old location of the toolbox supported-scenarios
    doc; the doc moved to /samples/python/hosted-agents/SUPPORTED_TOOLBOX_SCENARIOS.md
    and the old /samples/python/toolbox/azd path now 404s.
    
    Caught by the markdown-link-check CI step.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>