Commit Graph

457 Commits

  • Python: Adding AgentFileStore and FileAccessProvider to support file access operations. (#6099)
    * Adding AgentFileStore and FileAccessProvider to support file ased operations for agents.
    
    * Address PR review feedback on FileAccessProvider
    
    - Probe symlinks on the unresolved candidate path so in-root symlinks
      cannot silently pass and out-of-root symlinks surface the correct
      error message.
    - Validate matching_lines elements in FileSearchResult.from_dict and
      raise a clean ValueError for non-mapping entries.
    - Cap search regex pattern length (256 chars) via a new
      _compile_search_regex helper to mitigate ReDoS, and surface the cap
      in the file_access_search_files tool description.
    - Skip non-UTF-8 files during filesystem search instead of aborting
      the entire directory walk.
    - Replace the module-scope trailing string in the data-processing
      sample with comments to avoid Ruff B018.
    - Remove the checked-in working/region_totals.md sample artifact so
      the save flow works from a clean checkout.
    - Expand the Windows stdout reconfiguration comment in task_runner.py
      for clarity.
    - Add tests for invalid/oversize regex, non-UTF-8 file search, and
      in-root symlink rejection.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fix mypy redundant-cast in FileSearchResult.from_dict
    
    Use cast(list[object], ...) instead of cast(list[Any], ...) so the
    cast represents a real type change (lists are invariant) and is no
    longer flagged by mypy as redundant, while still satisfying pyright's
    reportUnknownVariableType. Matches the existing pattern in _memory.py.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Tighten path normalization and directory resolution in FileAccess
    
    - _normalize_relative_path now strips surrounding whitespace up front
      so leading/trailing spaces never leak into file segments, and
      rejects trailing path separators for file paths so 'foo/' is no
      longer silently coerced to 'foo'.
    - FileSystemAgentFileStore._resolve_safe_directory_path normalizes
      with is_directory=True and maps an empty normalized result to the
      root. This matches InMemoryAgentFileStore so whitespace-only
      directory inputs resolve to the root instead of raising.
    - Added tests for whitespace stripping, trailing-separator rejection,
      and whitespace-only directory listing on the filesystem store.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Harden FileAccess search and atomic save in store API
    
    - Add wall-clock timeout (10s) around regex scans so a pathological pattern (e.g. `(a+)+`) below the length cap cannot stall the event loop.
    - Offload the InMemoryAgentFileStore regex scan to a worker thread, matching the filesystem store.
    - Fail closed when `Path.is_symlink` raises during the safe-path probe so a permission error cannot silently bypass the symlink/reparse-point rejection.
    - Add `overwrite: bool = True` to `AgentFileStore.write_file`; the in-memory store performs the check under the existing lock and the filesystem store uses `open(mode='x')` so concurrent callers cannot race past `overwrite=False`.
    - `file_access_save_file` now relies on the atomic store call instead of a separate `file_exists` round-trip.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fix Python 3.10 timeout handling and add directory arg to list/search tools
    
    - Catch asyncio.TimeoutError in _run_search_with_timeout. In Python 3.10
      asyncio.wait_for raises asyncio.exceptions.TimeoutError, which is
      distinct from the builtin TimeoutError (the two were unified in 3.11).
      Catching the asyncio alias works on every supported version.
    - Add an optional directory parameter to file_access_list_files and
      file_access_search_files so agents can enumerate / scope searches to
      nested folders, not just the store root.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address FileAccess review feedback: case, errors, signal, TOCTOU
    
    - InMemoryAgentFileStore now stores (display_name, content) so list_files
      and search_files return the original-case names callers wrote, matching
      the behaviour of FileSystemAgentFileStore on case-preserving filesystems
      and removing the silent in-memory vs. on-disk contract divergence.
    - FileSystemAgentFileStore.read_file raises ValueError instead of letting
      UnicodeDecodeError bubble for binary / non-UTF-8 input, restoring
      symmetry with search_files (which still skips) and giving the tool
      layer a recoverable type to translate.
    - Tool wrappers now catch ValueError and OSError around every operation
      and surface them as readable strings, so 'you used ..' and 'the file
      already exists' are both reported to the model the same way instead of
      the former crashing out as an unhandled exception.
    - _search_files_sync logs per skipped non-UTF-8 file at WARNING and an
      aggregate INFO summary so operators can distinguish 'no matches' from
      'half the corpus was unreadable'.
    - FileSystemAgentFileStore softens its docstrings to acknowledge the
      inherent probe-then-open TOCTOU window. On POSIX both read and write
      now pass O_NOFOLLOW so the kernel refuses if the leaf segment becomes
      a symlink between the probe and the open. Windows has no equivalent
      flag; the limitation is documented.
    - Tests cover: case preservation on list/search, ValueError on non-UTF-8
      read at the store and tool layer, tool-layer string responses for
      path-traversal and oversized-regex inputs, search-skip log output,
      symlink rejection on delete/search/list, and symlinked intermediate
      directory rejection.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address FileAccess nit comments: docstrings, enumerate, opt-in delete approval
    
    - Expand FileSearchMatch/FileSearchResult.to_dict docstrings to explain why
      the override is needed (__slots__ defeats the mixin's __dict__ iteration)
      and why exclude/exclude_none are accepted-but-ignored (mixin signature
      compatibility for callers like to_json).
    - Use enumerate(lines, start=1) in _search_file_content so the +1 below is
      no longer needed; rename loop variable to line_number for clarity.
    - Add opt-in require_delete_approval: bool = False on FileAccessProvider.
      When True, file_access_delete_file is registered with approval_mode
      'always_require' so the host must approve every delete. Default False
      preserves current behaviour and matches the .NET reference, but
      deployments that want a safer-by-default posture can enable it.
    - Add tests covering both delete approval modes.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * FileAccess: require delete approval by default
    
    Flip the default for FileAccessProvider(require_delete_approval=...) from
    False to True so destructive deletes are gated by host approval out of the
    box. Callers that want the previous autonomous behaviour (which matches the
    .NET reference) can pass require_delete_approval=False.
    
    Tests updated accordingly.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fixing linkinspector by installing Chrome for puppeteer first.
    
    ---------
    
    Co-authored-by: Ben Thomas <25218250+alliscode@users.noreply.github.com>
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: [Breaking] Refactor Skill API to async resource and script lookup (#6135)
    Port of .NET commit 08541ee5a9.
    
    Replace property-based Skill.content/resources/scripts with async
    by-name lookup methods:
    - content property -> async get_content() -> str
    - resources property -> async get_resource(name) -> SkillResource | None
    - scripts property -> async get_script(name) -> SkillScript | None
    
    SkillsProvider now always includes all three tools (load_skill,
    read_skill_resource, run_skill_script) and both instruction blocks
    regardless of whether any skills have resources or scripts.
    
    ClassSkill retains resources/scripts properties as overridable hooks
    for subclass reflection-based discovery.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • 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: 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>
  • 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>
  • 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: Align ModeProvider tool names and instructions (#6071)
    * Align ModeProvider tool names and instructions
    
    * Address PR comments
  • 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>
  • 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>
  • 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>
  • [BREAKING] Python: Enable instrumentation by default (#5865)
    * Enable instrumentation by default
    
    * Update samples
    
    * Optimization when span is not recording
    
    * Address Copilot comments
    
    * Revert uv.lock
    
    * Add warning
    
    * Formatting
    
    * Fix mypy
    
    * Add disable_instrumentation() with sticky user-intent semantics
    
    Add a public disable_instrumentation() entry point so users can explicitly opt
    out of Agent Framework telemetry, with a sticky-disable flag that makes the
    user's intent "leading" — no framework code path (foundry's
    configure_azure_monitor, configure_otel_providers, enable_instrumentation,
    enable_sensitive_telemetry, or direct OBSERVABILITY_SETTINGS.enable_*
    writes) can re-enable instrumentation until the user explicitly clears the
    disable with enable_instrumentation(force=True) /
    enable_sensitive_telemetry(force=True).
    
    Also addresses the two remaining unresolved review threads on the PR:
    1. test_observability_settings_defaults_instrumentation_true pins the new
       "ENABLE_INSTRUMENTATION defaults to True when env unset" behavior.
    2. test_enable_instrumentation_reads_env_sensitive_data restores coverage
       for the post-import load_dotenv() fallback path.
    
    Implementation:
    - ObservabilitySettings.enable_instrumentation / enable_sensitive_data become
      properties backed by _enable_*. While _user_disabled is True, the getters
      return False and the setters drop True writes (defense in depth so third-
      party writes can't subvert the disable).
    - Public is_user_disabled read-only property lets integrations (e.g. foundry's
      configure_azure_monitor) cheaply check the disable state without poking at
      privates.
    - enable_instrumentation() and enable_sensitive_telemetry() short-circuit with
      an info log when disabled; gain a force=True kwarg that clears the disable.
    - configure_otel_providers() still creates providers / exporters / views so a
      later force-enable can use them, but logs an info message when called while
      disabled.
    - Foundry's FoundryChatClient.configure_azure_monitor and
      FoundryAgent.configure_azure_monitor early-return when the user has
      disabled, so Azure Monitor's global providers aren't installed unnecessarily.
    
    Tests: 11 new tests covering default-on, env re-read at call time, sticky
    behavior against each re-enable surface (enable_instrumentation,
    enable_sensitive_telemetry, configure_otel_providers, direct attribute
    writes), force=True override, re-arming the disable, and the __all__ export.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * docs: document disable_instrumentation() and force=True paths
    
    Add a "Disabling instrumentation" section to the observability sample README
    that walks through:
    
    - The distinction between the ENABLE_INSTRUMENTATION env var (initial,
      non-sticky) and disable_instrumentation() (process-wide, sticky).
    - Why the sticky semantics matter: framework integrations like
      FoundryChatClient.configure_azure_monitor() can call
      enable_instrumentation() as part of their setup, and the user's opt-out
      needs to win.
    - All five surfaces guarded by the sticky disable (property reads, public
      enable functions, configure_otel_providers, direct attribute writes,
      is_user_disabled-aware integrations).
    - The force=True escape hatch on both enable_instrumentation() and
      enable_sensitive_telemetry().
    - How third-party integrations should consult OBSERVABILITY_SETTINGS.is_user_disabled.
    - The limits of the disable (does not tear down existing providers /
      in-flight spans / third-party instrumentation, does not persist across
      processes).
    
    Cross-links the new section from the ENABLE_INSTRUMENTATION row in the env
    vars table.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * docs: soften disable_instrumentation() overclaim about telemetry guarantees
    
    Replace 'no telemetry will be emitted no matter what' (which is too strong,
    since callers can still pass force=True or mutate private attributes) with
    language framing the disable as a user-intent contract that library and
    framework code is expected to honor: the framework actively short-circuits
    the public enable paths, force=True and private-attribute writes are
    acknowledged as out-of-contract escape hatches that integrations should
    not use on the user's behalf.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * docs: correct observability Dependencies section
    
    - opentelemetry-sdk is no longer a hard dependency; it is lazily imported by
      create_resource(), create_metric_views(), and configure_otel_providers()
      with a clear ImportError when missing. Day-to-day instrumentation works
      with opentelemetry-api alone provided some other component configures the
      global OpenTelemetry providers (Azure Monitor, an APM agent, application
      bootstrap, etc.).
    - opentelemetry-semantic-conventions-ai is no longer used anywhere in the
      source; remove it from the listed dependencies.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * docs: replace stale observability migration guide with current PR's only relevant migration
    
    The old guide documented the move away from setup_observability(otlp_endpoint=...)
    which was an earlier-release API change unrelated to this PR and stale enough that
    it's more confusing than helpful at this point. Replace it with a short note on the
    single migration this PR introduces: callers of
    enable_instrumentation(enable_sensitive_data=True) should switch to
    enable_sensitive_telemetry(). Cross-link to the Disabling instrumentation section
    for the rare 'force on without enabling sensitive data' use case where
    enable_instrumentation() still applies.
    
    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>
  • Python: Skip MCP prompt loading when unsupported (#5370)
    * Python: Skip MCP prompt loading when unsupported
    
    * Fix MCP pagination pyright checks
    
    * Simplify MCP support flag checks
  • Python: Bump Python package versions for a release (#5964)
    * Bump Python package versions to 1.5.0 for a release
    
    * Promote orchestrations to 1.0.0rc1
    
    * ci(python-setup): merge dynamic exclude into existing workspace exclude
    
    The python-setup action injected exclude = [...] verbatim into
    [tool.uv.workspace], producing a duplicate 'exclude' key when the
    section already had a static exclude. Scope the rewrite to the
    [tool.uv.workspace] section and append the package to the existing
    array when present; idempotent if the package is already excluded.
    
    * Address Copilot review feedback: raise inter-package floors to 1.5.0
    
    - foundry, foundry-local: agent-framework-openai >=1.4.0 -> >=1.5.0
    - azure-contentunderstanding: agent-framework-foundry >=1.4.0 -> >=1.5.0
    - azurefunctions: pin agent-framework-durabletask to >=1.0.0b260519,<2
    
    Keeps lockstep cohort consistent and avoids mixed 1.4.x / 1.5.0 installs.
    
    * Re-include azurefunctions and durabletask in the uv workspace
    
    The pinned durabletask>=1.4.0 floor is enough to make resolution succeed;
    the workspace exclude was over-correction and broke CI samples and pyright
    type-checking (re-exports in agent_framework/azure/__init__.pyi plus
    samples/04-hosting/{azure_functions,durabletask}/ could not resolve their
    imports). Dropping them from agent-framework-core[all] still stands so the
    metapackage does not pull them.
    
    * Restore azurefunctions and durabletask in agent-framework-core[all]
    
    The durabletask floor pin keeps users on the safe 1.4.0, so they are once
    again included in the metapackage. Update CHANGELOG to reflect the pin
    rather than an [all] removal.
    
    * Raise uvicorn ceiling in ag-ui and devui to allow 0.42+
    
    The root override-dependencies pins uvicorn[standard]>=0.34.0 (no upper)
    and the workspace lock resolves to 0.47.0. The package ceiling <0.42.0
    meant the workspace was no longer testing the declared supported range.
    Bump to <1 so the lock fits within the declared bounds.
    
    Also picked up by validate-dependency-bounds: refresh stale orchestrations
    RC pin in devui dev deps.
  • Python: Record actual served model from Azure OpenAI (#5910)
    * Record actual served model as response model for Azure OpenAI
    
    * Formatting
    
    * Fix tests
    
    * Fix pipeline error
    
    * Comments
    
    * Address review: surface served model via ChatResponse.model
    
    Apply blocking review feedback from PR #5910:
    
    - Use ChatResponse.model / ChatResponseUpdate.model as the source of truth
      for the Azure x-ms-served-model header value, instead of stashing it in
      additional_properties and overriding it again in observability.
      Observability already reads response.model; the chat client now overwrites
      it post-parse when the served-model header is present. Empirically the
      Azure Responses API returns the deployment alias in body.model and the
      actual snapshot (e.g. gpt-5-nano-2025-08-07) in this header.
    
    - Move the AZURE_OPENAI_SERVED_MODEL_HEADER constant out of observability.py
      and into RawOpenAIChatClient (as the SERVED_MODEL_HEADER ClassVar). The
      header is Azure-OpenAI-Responses-API-specific so observability does not
      need to know about it.
    
    - Revert the streaming text_format path to client.responses.stream(...) and
      drop the _pydantic_model_to_text_format_param helper. That helper imported
      from openai.lib._parsing._responses (a private SDK path) and the swap to
      responses.create(stream=True) dropped client-side output_parsed for
      structured-output streaming. The streaming-with-text_format path is the
      only one that does not surface the served-model header - documented inline.
    
    - Wrap the raw streaming responses in async with so the underlying socket
      closes deterministically (continuation_token retrieve + create paths).
    
    - Fix the empty-string / whitespace-only header at the source by stripping
      in _extract_served_model and returning None when nothing remains.
    
    - Revert unrelated formatting-only churn in _skills.py and test_mcp.py.
    
    - Update unit tests to assert against chat_response.model / update.model
      and add an aggregated streaming assertion plus a pin that the
      streaming-with-text_format path does not get the header.
    
    Verified end-to-end against Azure OpenAI Responses API: deployment alias
    gpt-5-nano now reports gpt-5-nano-2025-08-07 as ChatResponse.model in both
    the non-streaming and streaming paths.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix: preserve streaming structured output finalization
    
    Agent-Logs-Url: https://github.com/microsoft/agent-framework/sessions/f62076ef-558d-49e8-8fe2-f38d527c9639
    
    Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com>
    
    * refactor: name streaming response finalizer
    
    Agent-Logs-Url: https://github.com/microsoft/agent-framework/sessions/f62076ef-558d-49e8-8fe2-f38d527c9639
    
    Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com>
    
    * fix: capture streaming response format after prepare
    
    Agent-Logs-Url: https://github.com/microsoft/agent-framework/sessions/f62076ef-558d-49e8-8fe2-f38d527c9639
    
    Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com>
    
    * refactor: clarify streaming response format capture
    
    Agent-Logs-Url: https://github.com/microsoft/agent-framework/sessions/f62076ef-558d-49e8-8fe2-f38d527c9639
    
    Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com>
    
    * test: use public API for streaming structured output
    
    Agent-Logs-Url: https://github.com/microsoft/agent-framework/sessions/f62076ef-558d-49e8-8fe2-f38d527c9639
    
    Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com>
    
    * Inline the served-model header override at its two call sites
    
    The `_apply_served_model_header` helper was a 1-line wrapper around
    `_extract_served_model`. Inlining the `if served_model is not None: ...`
    matches the pattern already used in the streaming paths and folds the
    explanatory docstring onto `_extract_served_model` (which is now the
    single place that knows about the header).
    
    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>
    Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
    Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com>
  • Python: Improve the handling of intermediate outputs for workflows and orchestrations (#5623)
    * Improve the handling of intermediate outputs for workflows and orchestrations
    
    * Address PR review feedback on intermediate output forwarding
    
    - Switch workflow.as_agent() forwarding to an explicit allowlist of {output,
      intermediate, data, request_info} so orchestration-internal events
      (group_chat, handoff_sent, magentic_orchestrator) stay inside the workflow
      instead of leaking into agent responses via str(data) coercion.
    - Stop raising on intermediate AgentResponseUpdate in non-streaming run();
      surface the partial as a Message with text_reasoning content. The defensive
      raise still applies to terminal output events, where Update payloads would
      corrupt message ordering.
    - Extend the DevUI workflow-event mapper so intermediate yields wrapping
      plain strings, Messages, and list[Message] render as visible output items
      instead of generic completed-trace events.
    - Add orchestration coverage for GroupChat, Handoff, and Magentic builders
      (default vs intermediate_outputs=True; structural where end-to-end is heavy).
    
    * Lift output-designation policy into a value type
    
    Replace the ``Workflow._output_executors`` list and the
    ``RunnerContext.should_label_as_intermediate`` Protocol method with a single
    immutable ``OutputDesignation`` value type owned by ``Workflow``. Thread the
    designation as a parameter through the existing call chain (Runner ->
    EdgeRunner -> Executor -> WorkflowContext) so ``yield_output`` consults the
    threaded snapshot directly rather than calling back into the runner context.
    
    Removes the ``InProcRunnerContext._workflow`` back-reference and the
    ``WorkflowBuilder.build()`` assignment that wired it up. Adds the public
    predicate ``Workflow.is_terminal_executor(executor_id)`` for external
    observers; ``OutputDesignation`` itself stays package-internal.
    
    Key decisions
    - ``OutputDesignation.designated`` is ``frozenset[str] | None`` -- ``None``
      preserves legacy "every yield is type='output'" behavior, any frozenset
      (including empty) opts into strict mode. The ``DeprecationWarning`` for
      legacy mode at build time is unchanged.
    - ``output_designation`` is an optional parameter on ``Runner``,
      ``EdgeRunner.send_message``, ``EdgeRunner._execute_on_target``,
      ``Executor.execute``, ``Executor._create_context_for_handler``, and
      ``WorkflowContext.__init__``. Each defaults to legacy ``OutputDesignation()``
      so direct callers (Azure Functions ``CapturingRunnerContext``,
      ``test_runner`` recording fixtures) keep working without ceremony.
    - The workflow-level filter in ``_run_core`` reads ``self._output_designation``
      live, preserving today's semantics where mutating the designation after
      build still affects subsequent runs (used by two existing tests).
    - ``Workflow.to_dict()`` continues to emit ``"output_executors":
      list[str] | None`` (sorted from the frozenset). Checkpoint format unchanged.
    
    Files changed
    - _workflow.py: add ``OutputDesignation`` dataclass; replace
      ``_output_executors`` with ``_output_designation``; add
      ``is_terminal_executor``; delete ``_should_yield_output_event``.
    - _runner_context.py: drop ``should_label_as_intermediate`` Protocol method
      and ``InProcRunnerContext`` impl; drop ``_workflow`` back-reference.
    - _workflow_builder.py: remove ``context._workflow = workflow`` assignment.
    - _runner.py, _edge_runner.py, _executor.py, _workflow_context.py: thread
      ``output_designation`` parameter through the call chain.
    - tests/workflow/test_output_designation.py (new): three-state coverage of
      the value type plus the public predicate delegation.
    - tests/workflow/test_workflow_builder.py, test_validation.py,
      test_workflow.py, test_runner.py and
      orchestrations/tests/test_orchestration_intermediate_vs_terminal.py:
      switch probes from ``_output_executors`` set checks to
      ``get_output_executors`` / ``is_terminal_executor``; update two
      post-build mutation tests to set ``_output_designation`` instead.
    
    Verification
    - core/tests/workflow/, orchestrations/tests/, azurefunctions/tests/:
      1119 passed, 42 skipped, 2 xfailed.
    - ``uv run poe lint``: clean.
    - ``uv run poe typing``: only the pre-existing
      ``_AGENT_FORWARDED_EVENT_TYPES`` pyright warning from 394bcd607 remains.
    
    Notes for next iteration
    - The builder's own ``_output_executors`` attribute (``list[Executor |
      SupportsAgentRun]``) is intentionally untouched; the issue scoped the
      rename to the workflow attribute.
    - Adjacent review candidates (twin ``WorkflowAgent`` translators,
      ``_AGENT_FORWARDED_EVENT_TYPES`` kind classifier,
      ``_event_origin_context`` ContextVar removal, ``WorkflowEvent`` ADT
      split, legacy-mode removal) remain out of scope.
    
    * Add explicit workflow output designation
    
    Key decisions
    
    - Extend the internal OutputDesignation value type from terminal-only membership to output/intermediate/hidden classification. Legacy mode remains outputs=None, so workflows built without output_executors or intermediate_executors still label every yield_output as type='output'.
    
    - WorkflowBuilder now accepts intermediate_executors. Providing either designation enters explicit mode; output executors emit output, intermediate executors emit intermediate, and unlisted yield_output payloads are hidden from caller-facing events while remaining in executor_completed data.
    
    - Empty explicit designation, duplicate entries, overlaps, unknown executors, and designated executors without workflow output annotations fail build validation. Existing orchestration builders pass intermediate-capable participants through intermediate_executors to preserve current intermediate_outputs behavior until participant-oriented designation lands.
    
    Files changed
    
    - packages/core/agent_framework/_workflows/_workflow.py, _workflow_builder.py, _workflow_context.py, _validation.py, _events.py
    
    - packages/core/tests/workflow/test_output_designation.py, test_output_executors_contract.py, test_strict_mode_event_labeling.py, test_validation.py, test_workflow.py, test_workflow_agent_intermediate.py
    
    - packages/orchestrations/agent_framework_orchestrations/_sequential.py, _concurrent.py, _group_chat.py, _magentic.py
    
    - packages/core/AGENTS.md
    
    Verification
    
    - uv run pytest packages/core/tests/workflow packages/orchestrations/tests packages/devui/tests/devui/test_mapper.py -q
    
    - uv run pytest packages/azurefunctions/tests -q
    
    - uv run poe lint
    
    - uv run poe typing fails only on pre-existing packages/core/agent_framework/_workflows/_agent.py _AGENT_FORWARDED_EVENT_TYPES private-use pyright error.
    
    Notes for next iteration
    
    - issues/03-core-workflow-explicit-designation.md was moved to issues/done but issues/ remains untracked and intentionally excluded from this commit.
    
    - Slice 4 should tighten workflow.as_agent() mapping for hidden emissions and streaming-only update payloads; Slice 5 should replace orchestration intermediate_outputs with participant-oriented designation.
    
    * Tighten workflow-as-agent output mapping
    
    Key decisions
    
    - Treat AgentResponseUpdate as a streaming-only payload across the workflow.as_agent() adapter, so non-streaming agent runs now reject both terminal output and intermediate workflow events carrying updates.
    - Keep streaming classification behavior explicit: terminal update payloads remain normal text content, while intermediate update payloads are rewritten to text_reasoning content.
    - Add explicit-mode coverage proving hidden yield_output emissions do not appear in non-streaming AgentResponse messages or streaming AgentResponseUpdate chunks.
    
    Files changed
    
    - packages/core/agent_framework/_workflows/_agent.py
    - packages/core/tests/workflow/test_workflow_agent_intermediate.py
    
    Verification
    
    - uv run pytest packages/core/tests/workflow/test_workflow_agent_intermediate.py -q
    - uv run pytest packages/core/tests/workflow/test_workflow_agent.py packages/core/tests/workflow/test_workflow_agent_intermediate.py -q
    - uv run pytest packages/core/tests/workflow packages/orchestrations/tests packages/devui/tests/devui/test_mapper.py -q
    - uv run poe lint
    - uv run poe typing fails only on the pre-existing packages/core/agent_framework/_workflows/_agent.py _AGENT_FORWARDED_EVENT_TYPES private-use pyright error.
    
    Blockers or notes for next iteration
    
    - issues/04-workflow-as-agent-output-mapping.md was moved to issues/done/ but issues/ remains untracked and intentionally excluded from this commit.
    - Slice 5 should replace orchestration intermediate_outputs with participant-oriented designation.
    
    * Add orchestration participant output designation
    
    Key decisions
    
    - Replace orchestration intermediate_outputs with participant-oriented output_participants and intermediate_participants across Sequential, Concurrent, GroupChat, Magentic, and Handoff builders.
    - Keep synthetic final executors terminal by default for Concurrent, GroupChat, and Magentic; keep Sequential's final participant terminal by default; keep Handoff participants terminal by default.
    - Centralize participant designation validation for empty explicit designation, duplicates, overlaps, and unknown participants, then map validated participants to workflow output/intermediate executors.
    
    Files changed
    
    - packages/orchestrations/agent_framework_orchestrations/_participant_designation.py
    - packages/orchestrations/agent_framework_orchestrations/_sequential.py
    - packages/orchestrations/agent_framework_orchestrations/_concurrent.py
    - packages/orchestrations/agent_framework_orchestrations/_group_chat.py
    - packages/orchestrations/agent_framework_orchestrations/_magentic.py
    - packages/orchestrations/agent_framework_orchestrations/_handoff.py
    - packages/orchestrations/tests/test_orchestration_intermediate_vs_terminal.py
    - packages/orchestrations/tests/test_magentic.py
    
    Blockers or notes for next iteration
    
    - issues/05-orchestration-participant-designation.md was moved to issues/done/ but issues/ remains untracked and intentionally excluded from this commit.
    - Slice 7 should migrate samples and docs away from intermediate_outputs to the new participant designation API.
    - uv run poe typing still fails only on the pre-existing packages/core/agent_framework/_workflows/_agent.py _AGENT_FORWARDED_EVENT_TYPES private-use pyright error.
    
    * Migrate samples to explicit output designation
    
    Key decisions
    
    - Replace sample usage of the removed orchestration intermediate_outputs boolean with participant-oriented intermediate_participants designation.
    - Update raw workflow guidance to show output_executors together with intermediate_executors, and document that unlisted yields are hidden in explicit designation mode.
    - Keep orchestration final outputs terminal while streaming designated participant responses as intermediate progress, including workflow.as_agent() samples where intermediates map to text_reasoning content.
    - Refresh workflow and orchestration README guidance plus the changelog reference so public docs no longer point users at intermediate_outputs.
    
    Files changed
    
    - CHANGELOG.md
    - packages/orchestrations/README.md
    - samples/README.md
    - samples/03-workflows/README.md
    - samples/03-workflows/control-flow/intermediate_vs_terminal_outputs.py
    - samples/03-workflows/orchestrations/README.md
    - samples/03-workflows/orchestrations/group_chat_agent_manager.py
    - samples/03-workflows/orchestrations/group_chat_philosophical_debate.py
    - samples/03-workflows/orchestrations/group_chat_simple_selector.py
    - samples/03-workflows/orchestrations/magentic.py
    - samples/03-workflows/orchestrations/magentic_human_plan_review.py
    - samples/03-workflows/orchestrations/sequential_chain_only_agent_responses.py
    - samples/03-workflows/agents/group_chat_workflow_as_agent.py
    - samples/03-workflows/agents/magentic_workflow_as_agent.py
    - samples/03-workflows/agents/sequential_workflow_as_agent.py
    - samples/semantic-kernel-migration/orchestrations/group_chat.py
    - samples/semantic-kernel-migration/orchestrations/magentic.py
    
    Blockers or notes for next iteration
    
    - issues/07-samples-and-docs-explicit-output-designation.md was moved to issues/done/ but issues/ remains untracked and intentionally excluded from this commit.
    - issues/06-devui-intermediate-event-rendering.md remains present and appears already satisfied by existing DevUI mapper/tests from the prior implementation slice.
    - PRD-explicit-workflow-output-designation.md remains untracked and intentionally excluded from this commit.
    
    * Render DevUI intermediate workflow outputs
    
    Key decisions
    
    - Preserve workflow output designation metadata on visible DevUI output messages and text deltas so intermediate/data emissions remain distinguishable from terminal output.
    - Render intermediate workflow message items in the execution timeline using executor metadata, while excluding them from the final workflow result aggregation.
    - Keep terminal output message rendering unchanged and retain legacy data events on the intermediate compatibility path.
    
    Files changed
    
    - packages/devui/agent_framework_devui/_mapper.py
    - packages/devui/frontend/src/components/features/workflow/execution-timeline.tsx
    - packages/devui/frontend/src/components/features/workflow/workflow-view.tsx
    - packages/devui/frontend/src/types/openai.ts
    - packages/devui/tests/devui/test_mapper.py
    
    Blockers or notes for next iteration
    
    - issues/06-devui-intermediate-event-rendering.md was moved to issues/done/ but issues/ remains untracked and intentionally excluded from this commit.
    - PRD-explicit-workflow-output-designation.md remains untracked and intentionally excluded from this commit.
    - uv run poe typing still fails only on the pre-existing packages/core/agent_framework/_workflows/_agent.py _AGENT_FORWARDED_EVENT_TYPES private-use pyright error.
    
    * Fix mypy
    
    * Clarify orchestration participant output config
    
    * Rename participant output kwargs for clarity
    
    output_participants -> final_output_from, intermediate_participants ->
    intermediate_output_from. The old names read like categories of
    participant; the new names make it clear the kwarg designates which
    participants' outputs surface as final vs. intermediate events.
    
    * Rename core workflow output kwargs with deprecation shim
    
    Adds final_output_from / intermediate_output_from as canonical kwargs on
    Workflow and WorkflowBuilder. Old output_executors / intermediate_executors
    kwargs continue to work but emit DeprecationWarning via a shared coalesce
    helper that also rejects supplying both. Wire-format keys in to_dict()
    stay as output_executors / intermediate_executors so checkpoint
    compatibility is preserved.
    
    Internal call sites in orchestrations and samples updated to the new
    names so users following sample code learn the canonical vocabulary;
    legacy callers still work with a one-shot warning.
    
    * Suppress pyright reportPrivateUsage on cross-module sentinel import
    
    * Update docstrings
    
    * Propagate sub-workflow intermediate outputs, fix handoff/sequential intermediate-only designation, and shore up tests, sample, and docstrings around the intermediate output contract.
    
    * Add canonical workflow output_from selection
    
    Key decisions:\n- Make output_from the canonical workflow-output allow-list and keep output_executors/final_output_from as deprecated compatibility aliases.\n- Treat empty output_from/intermediate_output_from lists as explicit selections and keep validation responsible for empty, duplicate, overlap, and unknown selections.\n- Remove the branch-only public intermediate_executors WorkflowBuilder kwarg while preserving legacy wire keys in to_dict().\n\nFiles changed:\n- packages/core/agent_framework/_workflows/_workflow.py\n- packages/core/agent_framework/_workflows/_workflow_builder.py\n- packages/core/agent_framework/_workflows/_workflow_context.py\n- packages/core/agent_framework/_workflows/_agent.py\n- packages/core/agent_framework/_workflows/_agent_executor.py\n- packages/core/tests/workflow/* output-selection coverage updates\n- packages/core/AGENTS.md\n- issues/done/001-canonical-list-based-output-selection.md\n\nBlockers/notes:\n- Orchestration builders still pass final_output_from internally; follow-up issue 004 should migrate them to output_from.\n- Legacy omitted-selection behavior and explicit all/all_other literals are left for issues 002 and 003.
    
    * Add explicit all workflow output selection
    
    Key decisions:
    - Treat output_from='all' as an explicit workflow-output selection sentinel and expand it at build time to executors with declared workflow output types.
    - Keep omitted output selections in legacy all-output mode with a deprecation warning that names output_from and intermediate_output_from and points to output_from='all'.
    - Reject intermediate_output_from='all' at construction because the all-output literal is output-only for this issue.
    
    Files changed:
    - packages/core/agent_framework/_workflows/_workflow_builder.py
    - packages/core/tests/workflow/test_output_executors_contract.py
    - issues/done/002-explicit-all-output-and-legacy-migration.md
    
    Blockers/notes:
    - all_other intermediate-output selection remains for issue 003.
    - Workflow-as-agent/orchestration parity remains for issue 004.
    
    * Add all-other intermediate output selection
    
    Key decisions:
    - Treat intermediate_output_from='all_other' as an explicit intermediate-output selection sentinel and expand it at build time after the workflow graph is complete.
    - Expand all_other to output-capable executors not selected by output_from; omitted or empty output_from selects no workflow outputs, while output_from='all' leaves an empty intermediate selection.
    - Keep output_from='all_other' invalid so all_other remains intermediate-output-only and runtime classification still receives concrete executor-id sets.
    
    Files changed:
    - packages/core/agent_framework/_workflows/_workflow_builder.py
    - packages/core/tests/workflow/test_output_executors_contract.py
    - issues/done/003-all-other-intermediate-output-selection.md
    
    Blockers/notes:
    - Workflow-as-agent and orchestration parity remains for issue 004.
    - Full documentation updates remain for issue 005.
    
    * Add orchestration output selection parity
    
    Key decisions:
    - Expose output_from on sequential, concurrent, group chat, handoff, and magentic builders while keeping final_output_from as a deprecated compatibility alias.
    - Resolve orchestration participant selections through the same explicit rules as workflows: output_from='all', intermediate_output_from='all_other', hidden unselected participant payloads, and overlap/duplicate/unknown/invalid-literal validation.
    - Continue preserving documented orchestration defaults by always designating each pattern's terminal internal executor where applicable.
    
    Files changed:
    - packages/orchestrations/agent_framework_orchestrations/_participant_output_config.py
    - packages/orchestrations/agent_framework_orchestrations/_sequential.py
    - packages/orchestrations/agent_framework_orchestrations/_concurrent.py
    - packages/orchestrations/agent_framework_orchestrations/_group_chat.py
    - packages/orchestrations/agent_framework_orchestrations/_handoff.py
    - packages/orchestrations/agent_framework_orchestrations/_magentic.py
    - packages/orchestrations/agent_framework_orchestrations/_orchestration_request_info.py
    - packages/orchestrations/tests/test_orchestration_intermediate_vs_terminal.py
    - issues/done/004-workflow-as-agent-and-orchestration-parity.md
    
    Blockers/notes:
    - Full documentation and sample migration wording remains for issue 005.
    - Existing tests that intentionally use final_output_from now emit the new deprecation warning.
    
    * Document workflow output selection contract
    
    Key decisions:
    - Use Workflow Output and Intermediate Output as the developer-facing terms for selected caller-facing emissions.
    - Document output_from and intermediate_output_from as the canonical API, with output_from as an allow-list and unselected payloads hidden unless explicitly selected as intermediate.
    - Add scenario and invalid-selection tables for workflow and orchestration docs, including legacy omission warnings, output_from='all', intermediate_output_from='all_other', list selections, invalid literals, overlap, duplicates, unknown selections, and empty explicit selections.
    - Migrate samples away from final_output_from and output_executors except where compatibility aliases are explicitly documented.
    
    Files changed:
    - packages/core/AGENTS.md
    - packages/orchestrations/README.md
    - packages/orchestrations/agent_framework_orchestrations/_handoff.py
    - packages/orchestrations/agent_framework_orchestrations/_sequential.py
    - samples/03-workflows/README.md
    - samples/03-workflows/control-flow/intermediate_vs_terminal_outputs.py
    - samples/03-workflows/human-in-the-loop/agents_with_approval_requests.py
    - samples/03-workflows/orchestrations/README.md
    - samples/04-hosting/foundry-hosted-agents/responses/05_workflows/main.py
    - scripts/sample_validation/create_dynamic_workflow_executor.py
    - issues/done/005-document-output-selection-contract.md
    
    Blockers/notes:
    - Direct full Ruff on scripts/sample_validation/create_dynamic_workflow_executor.py still reports pre-existing docstring/print/line-length issues outside this docs migration; syntax-focused checks for changed files pass.
    - No remaining AFK issue files are present under issues/.
    
    * Latest updates
    
    * Typing fixes
    
    * Cleanup
  • Python: Parse YAML block scalars in SKILL.md frontmatter (#5863)
    The frontmatter parser previously matched only single-line `key: value` pairs, so block scalar indicators (`|` literal, `>` folded, with chomping `-`/`+`) were silently truncated to the indicator character. Multi-line descriptions like `description: >\n  ...` lost their content.
    
    Add `_parse_yaml_scalar_value()` which detects block scalar indicators, collects indented continuation lines, strips the common leading indentation, joins per scalar style (newlines for `|`, spaces for `>`), and applies chomping per the YAML 1.2 spec. Update `_extract_frontmatter()` to use the helper for unquoted values.
    
    Adds 15 unit tests covering literal/folded styles, all chomping variants, indentation handling, content containing colons, non-description fields, tab indentation, blank-line preservation, and a regression test for plain values.
    
    Fixes #5713.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: bump package versions for 1.4.0 release (#5872)
    * fixes
    
    * fixes
    
    * Python: bump package versions for 1.4.0 release
    
    Cuts the python-1.4.0 release. MINOR bump on the released cohort
    (agent-framework, agent-framework-core, agent-framework-openai,
    agent-framework-foundry: 1.3.0 -> 1.4.0), driven by breaking changes
    in experimental skills API and new features. All 21 beta packages
    stamp 1.0.0b260514, all 3 alpha packages stamp 1.0.0a260514, and
    ag-ui remains at 1.0.0rc1 (freshly promoted). Date stamp reflects
    2026-05-14 Pacific.
    
    - Released cohort: 1.3.0 -> 1.4.0
    - Beta packages (21): 1.0.0b260507 -> 1.0.0b260514
    - Alpha packages (3): 1.0.0a260507 -> 1.0.0a260514
    - ag-ui: stays at 1.0.0rc1 (dep bound updated only)
    - Inter-package dependency lower bounds updated (>=1.3.0 -> >=1.4.0)
    - Fix chatkit StructuredInputItem exhaustiveness for openai-chatkit 1.6.4
    - Update CHANGELOG compare links
    - uv.lock refreshed
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: forward MCP tool call metadata (#5815)
    * Python: forward MCP tool call metadata
    
    * fix: preserve MCP tool meta after prompt reload
  • Python: Support list[str] arguments for file-based skill scripts (#5850)
    Port of .NET PR #5475. Broadens the args type from dict[str, Any] | None
    to dict[str, Any] | list[str] | None across the skill script API surface,
    enabling CLI-style argv forwarding to subprocess scripts.
    
    Changes:
    - SkillScript.run(), InlineSkillScript.run(), FileSkillScript.run(): widen
      args type; InlineSkillScript rejects list with TypeError
    - FileSkillScript.parameters_schema: returns array-of-strings schema
    - FileSkill.content: appends <scripts> block with parameters_schema
    - SkillScriptRunner protocol: widen args type
    - SkillsProvider._run_skill_script: widen args type
    - run_skill_script tool schema: accept object, array, or null
    - subprocess_script_runner sample: accept list[str], reject dict
    - class_based_skill sample: fix missing SkillFrontmatter wrapper
    - Standardize 'folder' to 'directory' in docstrings (#5712)
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • [BREAKING] Python: Align file skill folder discovery with agentskills.io spec (#5807)
    * Align Python FileSkillsSource with agentskills.io spec
    
    Update FileSkillsSource to scan spec-defined subdirectories instead of
    recursive rglob for resource and script discovery:
    
    - Resources: scan 'references/' and 'assets/' (was: entire skill tree)
    - Scripts: scan 'scripts/' (was: entire skill tree)
    - Add resource_directories and script_directories parameters for
      customization, with '.' root indicator for skill root files
    - Add directory validation: reject '..' traversal, absolute paths, empty
      names; normalize separators and deduplicate directories
    - Non-recursive scanning within each configured directory (top-level only)
    - Containment check validates files against target directory, not just
      skill root, for stronger path-traversal defense
    - Case-insensitive directory deduplication via os.path.normcase()
    - Cross-platform absolute path rejection in directory validation
    - Sort discovery results for stable ordering
    - Update SkillsProvider.from_paths() to pass new parameters through
    - Update all tests for new subdirectory-scoped discovery behavior
    
    Resolves #5711.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address PR review: tighten path validation and add containment guard
    
    - Narrow Windows absolute path check to proper drive-root pattern
      (re.match r'^[A-Za-z]:[/\\]') to avoid rejecting valid POSIX names
    - Add _is_path_within_directory guard before _has_symlink_in_path in
      both discovery methods to prevent ValueError on escaped paths
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Log warning on OSError during directory listing in skill discovery
    
    Address review comment: _discover_resource_files and _discover_script_files
    previously swallowed OSError silently when iterdir() failed. Now log a
    warning so permission errors and transient FS failures are visible
    instead of making resource/script directories silently disappear.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Strip server-issued response item IDs under storage (#3295) (#5690)
    Fixes microsoft/agent-framework#3295. When the OpenAI Responses chat
    client sends a request that carries previous_response_id / conversation_id
    / conversation, the server already has the prior turn's response items
    and rejects duplicates with "Duplicate item found with id fc_xxx". The
    chat client was re-sending them inline whenever the input messages still
    carried the items in additional_properties (workflow replay, history
    providers, etc.), which broke any tool-using agent with persistent
    history.
    
    Decisions:
    - Single chokepoint: _prepare_message_for_openai. When the resulting
      request uses service-side storage, drop function_call, reasoning,
      approval-request/response, and local-shell-call items from the wire
      input. Keep function_result with its call_id; the server pairs it to
      the prior function_call via that key.
    - function_result is preserved unconditionally except for the local-shell
      variant, which carries its own server-issued item id.
    - No public API change. Wire format change is subtractive and only on
      requests that would otherwise 400.
    - Re-pointed the strict-xfail in test_full_conversation.py from #4047 to
      #3295. Kept xfail because the test asserts executor-level session-id
      clearing, which is the defense-in-depth half tracked by 3295-03; this
      slice closes the wire-level half.
    
    Files:
    - python/packages/openai/agent_framework_openai/_chat_client.py: strip
      rule applied alongside the existing reasoning-item branch.
    - python/packages/openai/tests/openai/test_openai_chat_client.py: four
      new tests pin the contract (function_call, approval, local-shell-call
      stripped under storage; everything kept without storage). Updated
      pre-existing tests that exercised the storage-on path to either pass
      request_uses_service_side_storage=False explicitly or assert the new
      strip behavior.
    - python/packages/foundry/tests/foundry/test_foundry_chat_client.py:
      same explicit storage-off opt-in for the inherited test.
    - python/packages/core/tests/workflow/test_full_conversation.py:
      re-pointed xfail reason to #3295 and the executor-level follow-up.
    
    Notes for next iteration:
    - 3295-01 (HITL wire-format validation against live OpenAI/Foundry) was
      not run; it requires the user's API credentials. The PRD design is
      locked but the empirical confirmation is still pending. If script 3
      fails on either provider, this slice may need to be revisited.
    - 3295-03 (clear service_session_id in AgentExecutor on full-history
      replay) remains open. After it lands the xfail in
      test_full_conversation.py can be removed.
    - pytest was not run in this iteration because uv-based pytest commands
      required interactive approval. Validation rests on careful reading;
      next iteration should run the openai + core test suites.
  • [Python] [Breaking] Extract skill spec metadata into SkillFrontmatter (#5775)
    * Fix Skill docstring consistency and spelling
    
    - Add ClassSkill to Skill class docstring concrete implementations list
    - Normalize 'defence' to 'defense' for American English consistency
    - Remove extra blank line in InlineSkill docstring example
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fix E501 line-too-long lint error in test_skills.py
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fix stale test section header to reflect SkillFrontmatter API
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fix metadata children overriding top-level frontmatter fields
    
    Scope YAML_KV_RE to column-0 keys only so indented children
    under metadata: are not mistakenly parsed as top-level fields.
    Add regression test and spec fields to sample SKILL.md files.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: fix: prevent MCP message_handler deadlock on notification reload (#4866)
    * fix(python): prevent MCP message_handler deadlock on notification reload
    
    When an MCP server sends a notifications/tools/list_changed or
    notifications/prompts/list_changed notification, the message_handler
    previously awaited load_tools()/load_prompts() directly. Since the
    handler runs on the MCP SDK's single-threaded receive loop, this
    caused a deadlock: load_tools() sends a list_tools request and waits
    for its response, but the receive loop cannot deliver that response
    while blocked in the handler.
    
    This manifested as a timeout in call_tool(), which then surfaced as
    "Error: Function failed." to the model instead of the real tool
    output. The MATLAB MCP server reliably triggers this because it sends
    a tools/list_changed notification during tool execution.
    
    Fix: schedule reloads as background asyncio.Tasks via a new
    _schedule_reload() helper, freeing the receive loop immediately.
    
    Fixes #4828
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address PR review feedback: fix exc_info, coalesce reloads, shutdown cleanup, tests
    
    - Fix exc_info=exc -> exc_info=True in _schedule_reload and message_handler
    - Tighten _schedule_reload param type from Any to Coroutine[Any, Any, None]
    - Coalesce reloads: cancel-and-replace per reload kind to prevent unbounded growth
    - Cancel pending reload tasks in _close_on_owner before tearing down session
    - Re-raise CancelledError in _safe_reload to respect task cancellation
    - Replace flaky asyncio.sleep(0) with asyncio.wait_for/gather in tests
    - Add caplog assertions to verify reload failure is actually logged
    - Assert _pending_reload_tasks cleanup on error path
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix: address review comments on MCP reload handling
    
    - Fix exc_info=True -> exc_info=message in message_handler error logging,
      since the handler is not called from an except block
    - Await cancelled reload tasks in _close_on_owner before tearing down
      the session to avoid 'Task was destroyed but pending' warnings
    - Add cancel-and-replace test verifying duplicate notifications cancel
      the first reload task and only keep one in flight
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix: remove Task.cancelling() call for Python 3.10 compat
    
    Task.cancelling() was added in Python 3.11. Replace with awaiting
    the task and checking cancelled() instead.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Add debug log when cancelling superseded reload task
    
    Log at DEBUG level when a new notification cancels an in-flight reload
    task, improving observability of the cancel-and-replace behavior.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <copilot@github.com>
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: bump package versions for 1.3.0 release (#5706)
    * Python: bump package versions for 1.3.0 release
    
    MINOR bump on the released cohort (agent-framework, agent-framework-core,
    agent-framework-openai, agent-framework-foundry: 1.2.2 -> 1.3.0). All 22
    beta packages stamp 1.0.0b260507 and all 3 alpha packages stamp
    1.0.0a260507 per the lockstep convention. Date stamp reflects 2026-05-07
    Pacific.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address review: bump foundry_local openai floor, fix devui orchestrations pin, clarify breaking scope
    
    - foundry_local: bump agent-framework-openai lower bound from >=1.1.0 to >=1.3.0
    - devui: update stale agent-framework-orchestrations dev pin from 1.0.0b260402 to 1.0.0b260507
    - CHANGELOG: clarify [BREAKING] applies to experimental skills API only
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Revert devui orchestrations pin to 1.0.0b260402 to avoid breaking DevUI
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Add ClassSkill for class-based skill definitions (#5678)
    * Python: Add ClassSkill for class-based skill definitions
    
    Add ClassSkill abstract base class with decorator-based resource and script
    discovery, porting .NET's AgentClassSkill (PRs #5027 and #5183) to Python.
    
    - Add ClassSkill(Skill, ABC) with instructions abstract property, cached
      content/resources/scripts properties
    - Add @ClassSkill.resource and @ClassSkill.script static method decorators
      for auto-discovery of methods and properties
    - Extract _build_skill_content() and _create_resource_element() shared
      helpers from InlineSkill for reuse
    - Add _discover_marked_members() for scanning class hierarchies
    - Add _make_method_name() for Python-to-skill name conversion
    - Add class_based_skill sample (UnitConverterSkill)
    - Update mixed_skills sample with TemperatureConverterSkill
    - Add 58 new tests covering ClassSkill, decorator discovery, property
      resources, inheritance, kwargs forwarding, and duplicate detection
    - Export ClassSkill from agent_framework public API
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix: replace try/except/continue with assignment to satisfy bandit B112
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * address PR review feedback
    
    - Walk cls.__mro__ in _discover_marked_members for inherited property resources
    - Use inspect.getattr_static for MRO-aware is_property check
    - Return defensive copies from resources/scripts properties
    - Raise TypeError on wrong decorator stacking order (@resource above @property)
    - Log warning instead of silently swallowing descriptor errors during discovery
    - Validate explicit name= at decoration time via _validate_member_name
    - Add tests for all of the above
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fix temperature converter skill: make resource necessary for script
    
    Refactor TemperatureConverterSkill so the agent must read the
    formulas resource (factor/offset) before calling the script,
    aligning with the volume-converter pattern.
    
    - Resource: numeric factor/offset table instead of symbolic formulas
    - Script: generic linear transform (value * factor + offset)
    - Instructions: updated to reflect new workflow
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Fix MCPStreamableHTTPTool leaking asyncio.CancelledError when MCP server is unreachable (#5687)
    * fix: wrap asyncio.CancelledError in ToolException in _connect_on_owner (#5667)
    
    asyncio.CancelledError is a BaseException (not Exception) in Python 3.8+.
    When an MCP server is unreachable, the MCP library's internal anyio task
    group raises CancelledError, which escaped all three 'except Exception'
    handlers in _connect_on_owner(). This propagated through
    _run_lifecycle_owner -> _run_on_lifecycle_owner -> connect -> __aenter__,
    bypassing user except Exception blocks entirely.
    
    Fix: change the three except-Exception clauses in _connect_on_owner to
    'except (Exception, asyncio.CancelledError)' so spurious CancelledErrors
    from the MCP transport layer are caught and wrapped in ToolException,
    consistent with the method's documented contract.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix(mcp): propagate genuine task CancelledError in connect() (#5667)
    
    On Python >= 3.11, check task.cancelling() > 0 before wrapping
    CancelledError as ToolException in the three except blocks inside
    _connect_on_owner(). When the current task is being cancelled by its
    caller, the CancelledError now propagates after cleanup, consistent
    with the existing pattern at _mcp.py:560-564 and _runner.py:115-120.
    
    On Python < 3.11 task.cancelling() is unavailable, so MCP-internal
    CancelledErrors still cannot be reliably distinguished from
    caller-driven cancellation; they continue to be wrapped as
    ToolException with a comment documenting the trade-off.
    
    Tests:
    - Add cleanup assertion to transport-creation CancelledError test
    - Add MCPStdioTool variants exercising the 'command' message branches
      for both transport-creation and initialize CancelledError paths
    - Add Python 3.11+-gated tests verifying genuine task cancellation
      propagates (and still cleans up) for transport and initialize stages
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix(mcp): log CancelledError with exc_info before wrapping in ToolException (#5667)
    
    CancelledError inherits from BaseException (not Exception) on Python >= 3.8,
    so the 'inner_exception=ex if isinstance(ex, Exception) else None' guard
    always yields None for CancelledError. This means ToolException.__init__
    calls logger.log(level, message, exc_info=None), dropping the traceback.
    
    Add an explicit logger.debug(error_msg, exc_info=ex) before each
    raise ToolException(...) in the three CancelledError handlers so the
    full traceback is preserved in debug logs when MCP-internal cancellation
    is wrapped rather than propagated.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address review feedback for #5667: Python: [Bug]: Error Handling Issue regarding Python MCPStreamableHTTPTool Class
    
    * refactor(_mcp): extract cancellation helper, fix session error msg and exc_info
    
    - Extract _should_propagate_cancelled_error() helper to eliminate duplicated
      genuine-cancellation detection logic across the three connect() except blocks
    - Fix session-creation ToolException message to include exception details
      (e.g. 'Failed to create MCP session: <ex>') matching the transport and
      initialize failure paths
    - Change exc_info=ex to exc_info=True in all three logger.debug() calls
      for idiomatic logging
    - Add tests for _should_propagate_cancelled_error helper
    - Add regression test asserting session error message includes exception text
    - Add test verifying logger.debug is called with exc_info=True
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * refactor: factor out _close_and_check_cancelled helper in _connect_on_owner
    
    Addresses review comment on PR #5687:
    
    1. Add _close_and_check_cancelled() helper method that combines
       _safe_close_exit_stack() + _should_propagate_cancelled_error() into a
       single await-able call. This eliminates the duplicated close-then-check
       pattern that appeared identically in all three connect phases (transport,
       session, initialize), reducing future drift risk.
    
    2. Comments 2 and 3 (missing {ex} in session error message and non-idiomatic
       exc_info=ex) were already addressed in the current code: all error messages
       include {ex} and all logger.debug calls use exc_info=True.
    
    3. Add test_connect_genuine_cancellation_during_session_creation_propagates
       to cover the previously untested genuine-cancellation path in the
       session-creation phase (transport and initialize phases already had tests).
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address review feedback for #5667: review comment fixes
    
    ---------
    
    Co-authored-by: Copilot <copilot@github.com>
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Add base_url parameter to AnthropicClient and RawAnthropicClient (#5685)
    * feat(anthropic): add base_url parameter to AnthropicClient and RawAnthropicClient
    
    Add base_url support to AnthropicSettings TypedDict, RawAnthropicClient,
    and AnthropicClient so users can point the client at Foundry or other
    Anthropic-compatible endpoints without having to construct AsyncAnthropic
    manually.
    
    - Add base_url field to AnthropicSettings (resolved from ANTHROPIC_BASE_URL env var)
    - Add base_url parameter to RawAnthropicClient.__init__ and pass it to AsyncAnthropic
    - Add base_url parameter to AnthropicClient.__init__ and forward to super
    - Add unit tests for base_url on both client classes
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: Add `base_url` parameter to `AnthropicClient` and `RawAnthropicClient`
    
    Fixes #5683
    
    * test: add ANTHROPIC_BASE_URL env fallback tests for issue #5683
    
    Add unit tests verifying that both AnthropicClient and RawAnthropicClient
    pick up base_url from the ANTHROPIC_BASE_URL environment variable via
    load_settings when base_url is not passed explicitly as a constructor arg.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * test(anthropic): explicit base_url kwarg beats ANTHROPIC_BASE_URL env var (#5683)
    
    Add regression tests asserting that when both ANTHROPIC_BASE_URL is set
    in the environment *and* an explicit base_url kwarg is passed to
    AnthropicClient / RawAnthropicClient, the explicit kwarg wins.
    
    This closes the priority-ordering contract (explicit arg > env var) that
    the existing tests left implicit.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <copilot@github.com>
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Core: notify agent of external AgentModeProvider mode changes (#5650)
    When the operating mode is changed externally (e.g. via a slash-command handler
    calling set_agent_mode), the agent's chat history still shows the prior set_mode
    tool call near the end. Updating only the system instructions is insufficient —
    models tend to anchor on the recent tool call and ignore the new mode.
    
    Mirror the .NET AgentModeProvider behavior: when set_agent_mode detects an actual
    mode change, record the previous mode in provider state. On the next before_run,
    the provider pops that flag and injects a user-role notification message
    announcing the switch, so the most recent context unambiguously reflects the
    current mode. The agent-driven set_mode tool path bypasses this so it does not
    trigger a redundant notification on its own change.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Remove bespoke Foundry toolbox helpers; standardize on MCP for toolbox consumption (#5671)
    * Remove Foundry toolbox helpers; standardize on MCP for toolbox consumption
    
    - Remove RawFoundryChatClient.get_toolbox() and its fetch_toolbox import
    - Remove fetch_toolbox, select_toolbox_tools, get_toolbox_tool_name,
      get_toolbox_tool_type, FoundryHostedToolType, ToolboxToolSelectionInput
      from agent_framework_foundry._tools
    - Remove ExperimentalFeature.TOOLBOXES from _feature_stage.py (no consumers)
    - Drop toolbox re-exports from agent_framework_foundry/__init__.py and
      agent_framework.foundry namespace
    - Update _sanitize_foundry_response_tool docstring to remove toolbox framing;
      sanitization logic itself is unchanged
    - Update _agent.py docstring: 'toolbox-fetched MCP' → 'hosted MCP'
    - Delete tests/test_toolbox.py (all tests covered removed helpers)
    - Update test_foundry_chat_client.py: rename/redoc tests that mentioned
      toolbox but test sanitization that remains
    - Delete foundry_chat_client_with_toolbox.py (bespoke toolbox API sample)
    - Delete foundry_toolbox_context_provider.py (relied on select_toolbox_tools)
    - Rename foundry_chat_client_with_toolbox_mcp.py →
      foundry_chat_client_with_toolbox.py (canonical MCP pattern)
    - Rewrite 04_foundry_toolbox/main.py to use MCPStreamableHTTPTool
    - Update provider/README, context_providers/README, 04_foundry_toolbox/README
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix(samples): update 06_files sample to consume toolbox via MCP (#5670)
    
    Replace removed get_toolbox/select_toolbox_tools APIs with
    MCPStreamableHTTPTool, using allowed_tools=["code_interpreter"] to
    select only the code interpreter from the toolbox endpoint.
    
    Update .env.example and README to use FOUNDRY_TOOLBOX_ENDPOINT
    instead of TOOLBOX_NAME.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix(foundry): remove non-existent toolbox helper APIs from README (#5670)
    
    Remove the 'fetch, optionally filter, and pass tools directly' pattern
    from the FoundryChatClient toolbox documentation, as select_toolbox_tools
    and get_toolbox were removed. Only the MCP endpoint pattern is documented.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix(foundry): remove residual toolbox docstring references and reproduction report
    
    Remove REPRODUCTION_REPORT.md (workflow artifact that should not be committed),
    and update two remaining docstring references that still said 'toolbox reads'
    /'toolbox definition' after the toolbox helpers were removed.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: Remove bespoke Foundry toolbox helpers; standardize on MCP for toolbox consumption
    
    Fixes #5670
    
    * fix(#5670): resolve toolbox endpoint from TOOLBOX_NAME fallback; add namespace regression tests
    
    - Add _resolve_toolbox_endpoint() helper in 04_foundry_toolbox/main.py and
      06_files/main.py that prefers FOUNDRY_TOOLBOX_ENDPOINT but falls back to
      deriving the MCP URL from FOUNDRY_PROJECT_ENDPOINT + TOOLBOX_NAME — fixing
      the startup KeyError when agents are deployed via azd provision (which injects
      TOOLBOX_NAME, not FOUNDRY_TOOLBOX_ENDPOINT).
    - Update 04_foundry_toolbox/.env.example to use FOUNDRY_TOOLBOX_ENDPOINT
      (consistent with 06_files).
    - Add TOOLBOX_NAME env var to 06_files/agent.yaml so deployed agents have it
      available for the fallback derivation.
    - Update both READMEs to document the two ways to supply the toolbox endpoint.
    - Add test_foundry_namespace_no_longer_exposes_toolbox_helpers() with negative
      assertions for FoundryHostedToolType, get_toolbox_tool_name,
      get_toolbox_tool_type, and select_toolbox_tools — guarding against accidental
      re-introduction of removed symbols.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix(samples): fail fast on empty FOUNDRY_TOOLBOX_ENDPOINT; add unit tests
    
    Addresses review feedback for #5670:
    
    - In _resolve_toolbox_endpoint() (04_foundry_toolbox/main.py and
      06_files/main.py) change the walrus-operator check from a truthy
      test to an explicit 'is not None' guard.  An explicitly set empty
      string now raises ValueError immediately with a clear message
      instead of silently falling through to the fallback URL
      construction.
    
    - Add tests/samples/hosting/test_toolbox_endpoint.py covering both
      sample modules:
        (a) FOUNDRY_TOOLBOX_ENDPOINT set → returned as-is
        (b) FOUNDRY_TOOLBOX_ENDPOINT set to empty string → ValueError
        (c) fallback constructs URL from FOUNDRY_PROJECT_ENDPOINT + TOOLBOX_NAME,
            stripping trailing slashes
        (d) neither variable group set → KeyError
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address review feedback: remove extraneous test and docstring content
    
    - Remove test_foundry_namespace_no_longer_exposes_toolbox_helpers (no longer warranted)
    - Remove docstring from _agent.py _prepare_tools_for_openai (extraneous)
    - Trim _chat_client.py _prepare_tools_for_openai docstring to one-liner (toolbox references no longer relevant)
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix: remove remaining extraneous docstring from RawFoundryChatClient._prepare_tools_for_openai
    
    Address review comment on PR #5671: reviewer noted the description
    isn't warranted now that toolbox helpers have been removed. Matches
    the pattern in RawFoundryAgentChatClient which has no docstring.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <copilot@github.com>
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: [Breaking] Restructure agent skills to use multi-source architecture (#5584)
    * migrate skills to multi source architecture
    
    * Fix ruff lint errors in skills module (ASYNC240, SIM108, E501)
    
    - Use anyio.Path for async file I/O in _FileSkillResource.read()
    - Use noqa: ASYNC240 for pure string os.path calls in async context
    - Restore pre-commit if/else pattern in InlineSkillScript.run()
    - Break long lines to fit 120-char limit in _skills.py and test_skills.py
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix: collapse multi-line lambdas to single lines to fix pyright errors
    
    The pyright ignore comments only suppress errors on the same line, so
    multi-line lambdas left arguments on continuation lines uncovered.
    Collapse both lambdas to single lines matching the existing load_skill
    lambda pattern.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix: replace untyped lambdas with typed inner functions to fix pyright errors
    
    Python lambdas cannot have type annotations, so pyright reports
    reportUnknownLambdaType and reportUnknownArgumentType errors that
    cannot be suppressed with inline ignore comments. Replace the
    lambdas for read_skill_resource and run_skill_script with typed
    inner async functions.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix: address PR review feedback on docs and prompt template
    
    - Update with_prompt_template() docstring to document the
      {resource_instructions} placeholder requirement
    - Remove stray backslashes after {resource_instructions} and
      {runner_instructions} in DEFAULT_SKILLS_INSTRUCTION_PROMPT
    - Update subprocess_script_runner docstring to reflect
      FileSkillScript.full_path usage
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * refactor: replace dict[str, Skill] with Sequence[Skill] in SkillsProvider
    
    Replace internal dict-based skills storage with Sequence[Skill] to
    eliminate silent duplicate overwrites and simplify the code. Add
    _find_skill helper for case-insensitive linear lookup.
    
    Also fix pyright errors in tests by adding isinstance assertions
    before accessing .function on SkillResource/SkillScript base types.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * refactor: add read-time resource path validation in _FileSkillsSource
    
    Move security validation (path-traversal and symlink guards) for
    file-based skill resources into _FileSkillsSource, restoring the
    read-time checks that existed in main via _read_file_skill_resource.
    
    - Add _get_validated_resource_path static method on _FileSkillsSource
      that validates containment, existence, and symlink safety
    - _FileSkillsSource.get_skills() validates resource paths at discovery
      time via _get_validated_resource_path before passing to _FileSkillResource
    - Move _normalize_resource_path, _is_path_within_directory, and
      _has_symlink_in_path from module-level into _FileSkillsSource as
      static methods (only used there)
    - _FileSkillResource remains a simple path-to-content reader
    - Add tests for _get_validated_resource_path security checks
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix: reject str/Path in SkillsProvider constructor to prevent str-as-Sequence ambiguity
    
    Since str is a Sequence, passing a path string to the source parameter
    would silently be treated as a sequence of characters instead of a
    file source. Add an explicit TypeError with a helpful message pointing
    callers to SkillsProvider.from_paths().
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address PR #5584 review feedback
    
    - Remove .NET reference from _FileSkillResource docstring
    - Fix inconsistent resource name example (references/FAQ.md -> references/FAQ)
    - Simplify SkillsProvider usage in code_defined_skill sample (pass single skill directly)
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * remove skillsproviderbuilder
    
    * Update python/packages/core/agent_framework/_skills.py
    
    Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
    
    * fix: remove dead code and fix sync function call in InlineSkillResource.read()
    
    - Change await self.function() to self.function() for sync functions
      without **kwargs; async results are handled by inspect.isawaitable()
    - Remove unreachable raise ValueError since __init__ already validates
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * remove full_path unnecessary property
    
    * replace anyio with asyncio.to_thread for file I/O in _FileSkillResource
    
    Replace anyio.Path usage with asyncio.to_thread + pathlib.Path since
    anyio is not a direct dependency of core (transitive via mcp).
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * simplify awaitable check to return directly
    
    Use 'return await result' instead of assigning then returning.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * address PR review feedback for skills refactoring
    
    - Replace anyio with asyncio.to_thread + pathlib.Path for file I/O
    - Simplify awaitable check to return directly
    - Remove unnecessary function None guard in InlineSkillResource.read()
    - Add assert for type narrowing on self.function
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * address PR review feedback for skills refactoring
    
    - Replace anyio with asyncio.to_thread + pathlib.Path for file I/O
    - Simplify awaitable checks to return directly
    - Remove unnecessary function None guard in InlineSkillResource.read()
    - Use typing.cast instead of assert for type narrowing
    - Add caching behavior note to SkillsProvider docstring
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * refactor: move name/description from abstract properties to Skill.__init__
    
    Replace abstract properties for name and description on the Skill ABC
    with a base __init__ that validates and stores them as regular
    attributes. This simplifies custom Skill subclasses (only content
    remains abstract) and centralizes validation in the base class,
    consistent with SkillResource and SkillScript base classes.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
  • Python: Add Python parity for InvokeMcpTool in declarative workflow (#5630)
    * Add Python parity for HttpRequestAction in declarative workflow
    
    * Ran pyupgrade and pright to fix CI issues
    
    * Fix conversation ID dot parsing for http executor
    
    * Removed unnecessary export command
    
    * Initial implementation of invoke mcp tool in python
    
    * Update sample to support require approval to be toggled by environment variable.
    
    * Fix cache and PR comments
    
    * Update python/samples/03-workflows/declarative/invoke_mcp_tool/main.py
    
    Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
  • Python: information-flow control prompt injection defense (#5331)
    * Python: Information-flow control based prompt injection defense (#5024)
    
    * fides integration
    
    * documentation
    
    * documentation
    
    * documentation
    
    * human-approval on policy violation
    
    * numenous hyena 'works'
    
    * IFC based implementation
    
    * minor edits in documentation
    
    * rebasing the branch and running the email example
    
    * Add security tests for IFC middleware
    
    * Fix Role.TOOL NameError in approval handling
    
    * tiered labelling scheme
    
    * 3 tier labelling scheme in middleware
    
    * Adapt security middleware to list[Content] tool results
    
    * Refactor SecureAgentConfig as context provider and address Copilot review comments
    
    * Update FIDES docs to reflect context provider pattern and update code for ContextProvider rename
    
    * Fix security examples: use OpenAIChatClient instead of non-existent AzureOpenAIChatClient
    
    * Address PR review: consolidate security modules, remove ContentLineage, update docs
    
    * remove unrelated files
    
    * remove comment from _tools.py and rename decision file
    
    * Fix CI failures: Bandit B110, broken md links, hosted approval passthrough
    
    * apply template to decision doc 0024
    
    * minor fixes to decision doc 0024
    
    ---------
    
    Co-authored-by: Aashish <t-akolluri@microsoft.com>
    
    * Python: follow up FIDES security flow (#5330)
    
    * Python: follow up FIDES security flow
    
    Refine the secure approval path, mark the security classes with the FIDES experimental feature label, and clean up the related docs/tests. Also fix workspace-level validation regressions uncovered while running the full Python check suite.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: remove FIDES GitHub MCP sample
    
    Drop the GitHub MCP security sample from the FIDES follow-up branch while keeping the remaining security docs and samples intact.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address PR review: fix paths and update FIDES implementation (#5352)
    
    * Python: updated import naming and comment from review (#5421)
    
    * updated import naming and comment from review
    
    * Add approval replay None call-id test
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: Address PR 5331 comments and track sesssion while calling Agent in email_security_example (#5446)
    
    * Address PR review: fix paths and update FIDES implementation
    
    * Address PR comments and add session tracking in email example in samples
    
    * Fix session creation and resolve merge conflict in docstring example
    
    * Resolve merge conflict in docstring example
    
    * Python: add test for empty-message pruning in approval result replacement (#5617)
    
    Adds test coverage for the second-pass logic in
    `_replace_approval_contents_with_results` that removes messages whose
    `contents` list becomes empty after first-pass content removal.
    
    Addresses review comment on PR #5331:
    https://github.com/microsoft/agent-framework/pull/5331#discussion_r3129039445
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: shrutitople <shruti.tople@gmail.com>
    Co-authored-by: Aashish <t-akolluri@microsoft.com>
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Core: add experimental session-mode harness context provider (#5611)
    * Python: Core: add experimental session-mode harness context provider
    
    Introduces the _harness namespace and the first context provider:
    SessionModeContextProvider, with get_session_mode / set_session_mode
    helpers and a DEFAULT_MODE_SOURCE_ID constant. Behind
    @experimental(ExperimentalFeature.HARNESS).
    
    Also folds in a small _sessions.py cleanup (try/except ImportError
    -> contextlib.suppress) touched while developing the harness.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: Core: align session-mode harness with .NET AgentModeProvider
    
    Mirror the default mode descriptions and instruction template used
    by the .NET AgentModeProvider so the cross-language harness UX is
    consistent.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: Core: address review feedback on session-mode harness
    
    - json.dumps tool outputs to stay valid for arbitrary mode names
    - normalize configured mode keys (lower+strip) so custom-cased configs work
    - raise TypeError instead of silently replacing non-dict session state
    - mark get_session_mode/set_session_mode as @experimental(HARNESS)
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: Core: rename SessionModeContextProvider to AgentModeProvider
    
    Match the .NET AgentModeProvider class name for cross-language
    consistency. Helpers renamed accordingly: get_session_mode ->
    get_agent_mode, set_session_mode -> set_agent_mode. The default
    source_id is now "agent_mode". Construction pattern stays Pythonic
    (kwargs, not an options object).
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: Core: address AgentModeProvider review feedback
    
    - default_mode now defaults to None and falls back to the first configured
      mode, decoupling the kwarg from the built-in 'plan'/'execute' set.
    - get_agent_mode catches ValueError when a previously persisted mode is no
      longer in available_modes and resets to the default mode (matching the
      non-string recovery branch). Added regression coverage for both behaviors.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Fix hyperlight WasmSandbox cross-thread Drop and harden hosted-agent sample (#5603)
    * update hyperlight to beta and move samples, add hosted agent sample
    
    * Python: Fix hyperlight WasmSandbox cross-thread Drop and harden sample
    
    Root cause: when a worker-side closure raised, the exception's __traceback__
    retained frame locals that included the partially constructed PyO3 sandbox.
    Future.result() re-raised that exception on the caller thread, and when the
    caller's exception was eventually GC'd the frame locals were released
    off-thread, dec_ref'ing the unsendable sandbox from the wrong thread and
    tripping the PyO3 panic
    '_native_wasm::WasmSandbox is unsendable, but is being dropped on another thread'.
    
    Fix:
    * Add _SandboxWorker._run_on_worker which catches every exception on the
      worker, drops __traceback__ there, deletes the original exception, and
      re-raises a fresh instance on the caller thread. initialize and execute
      route through it; dispose keeps its bare-submit semantics.
    * Add an opt-in diagnostic module _drop_diagnostic (no-op unless
      HYPERLIGHT_TRACE_DROPS=1) that installs a sys.unraisablehook and dumps
      owner-thread + per-thread stacks on any future cross-thread unsendable
      Drop. Useful for triaging similar PyO3 regressions.
    * Tests: cross-thread invocation, traceback-leak isolation, _SandboxEntry
      attribute-shape check, and a stale-reference stress test driven through
      asyncio.to_thread.
    
    Sample (samples/04-hosting/foundry-hosted-agents/responses/06_hyperlight_codeact):
    * Dockerfile installs agent-framework-* from in-tree source with python/ as
      build context so unreleased fixes can be validated end-to-end.
    * call_server.py pins the Responses API version.
    * main.py enables include_detailed_errors=True so future tool failures
      surface the actual exception text instead of a bare 'Error: Function
      failed.' string.
    * README.md documents the in-tree-package build and the Hyperlight
      hypervisor requirement (/dev/kvm on Linux, MSHV on Windows). Hosted
      environments without hypervisor passthrough surface 'No Hypervisor was
      found for Sandbox'; this is a hosting constraint, not a hyperlight bug.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: remove _drop_diagnostic from hyperlight package
    
    The diagnostic module was useful while bisecting the cross-thread Drop bug,
    but it is no longer needed now that _SandboxWorker._run_on_worker prevents
    the panic at the source.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: address PR review feedback on hyperlight
    
    - Use lazy agent_framework.hyperlight import in sample main.py.
    - Env-driven endpoint (FOUNDRY_AGENT_ENDPOINT) in call_server.py; remove personal URLs.
    - Align agent.yaml model deployment with manifest (gpt-4.1-mini).
    - Tighten Dockerfile requirements guard; drop dangling deploy.ps1 reference.
    - Preserve exception args when sanitizing tracebacks in _run_on_worker.
    - Add public _SandboxWorker.is_alive(); update test to avoid private attr.
    - Add namespace coverage tests for agent_framework.hyperlight lazy loader.
    - Add prominent note: Foundry hosted-agent runtime does not yet support
      Hyperlight (no hypervisor exposed); container works locally with /dev/kvm.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: bump hyperlight-sandbox dependencies to 0.4.x
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: renumber hyperlight codeact sample to 08
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Coerce worker exception args to strings for cross-thread safety
    
    Stringify exc.args on the worker thread before propagating, so any
    PyO3 unsendable object captured in args (e.g. via a caller-supplied
    callback or underlying SDK) cannot be Dropped on the calling thread.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * moved sample
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Core: add experimental todo-list harness context provider (#5612)
    * Python: Core: add experimental todo-list harness context provider
    
    Adds TodoListContextProvider with pluggable TodoStore backends:
    TodoSessionStore (in-session) and TodoFileStore (JSONL on disk).
    Public types: TodoItem, TodoInput. Behind
    @experimental(ExperimentalFeature.HARNESS).
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: Core: align todo harness instructions with .NET TodoProvider
    
    Reformat DEFAULT_TODO_INSTRUCTIONS to mirror the .NET TodoProvider
    DefaultInstructions wording and structure, and bring the class
    docstring closer to the .NET XML <remarks> block. Keeps Python tool
    names in snake_case.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: Core: address review feedback on todo harness
    
    - mark TodoStore as @experimental(HARNESS) for surface consistency
    - TodoSessionStore.load_state now raises ValueError on malformed items
    - TodoFileStore now namespaces persisted state by source_id
    - TodoFileStore now safely encodes session_id/owner and verifies path containment (matches FileHistoryProvider pattern)
    - per-(session, source_id) asyncio.Lock around read-modify-write to avoid races
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: Core: rename TodoListContextProvider to TodoProvider
    
    Match the .NET TodoProvider class name for cross-language consistency.
    Other public types (TodoStore, TodoSessionStore, TodoFileStore,
    TodoItem, TodoInput) are unchanged. Construction stays Pythonic
    (kwargs, not an options object).
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: Core: address TodoProvider review feedback
    
    - TodoStore.load_state/save_state are now async; TodoFileStore performs
      disk I/O via asyncio.to_thread so the event loop is no longer blocked
      while the per-session mutation lock is held.
    - TodoSessionStore now raises ValueError for malformed top-level state
      (non-dict / non-list 'items' / non-int 'next_id') to match the
      TodoFileStore contract instead of silently re-defaulting.
    - Both stores now clamp next_id to max(item.id) + 1 after load to make
      ID collisions impossible after recovery or reconfiguration.
    - TodoFileStore writes atomically by writing a sibling temp file and
      os.replace-ing it so a crash mid-write cannot truncate the state file.
    - TodoFileStore.load_state no longer creates parent directories for
      sessions that never write; mkdir is deferred to save_state.
    - TodoProvider mutation locks now live in a weakref.WeakKeyDictionary
      keyed by AgentSession, so locks for GC'd sessions are evicted instead
      of leaking in long-running services.
    
    Tests cover each change including a TodoFileStore-backed end-to-end
    provider flow, atomic-write recovery, and lock GC eviction.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Core: add experimental memory harness context provider (#5613)
    * Python: Core: add experimental memory harness context provider
    
    Adds MemoryContextProvider with topic-indexed long-term memory and
    chat-driven compaction. Pluggable MemoryStore backends include
    MemoryFileStore. Public types: MemoryIndexEntry, MemoryTopicRecord.
    Behind @experimental(ExperimentalFeature.HARNESS).
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: Core: address review feedback on memory harness
    
    - mark MemoryStore as @experimental(HARNESS) for surface consistency
    - safely encode owner id and verify path containment (matches FileHistoryProvider pattern)
    - namespace MemoryFileStore on-disk layout by source_id to avoid cross-provider collisions
    - before_run computes index_entries once and only rewrites MEMORY.md when content changes
    - asyncio locks around topic/state read-modify-write to avoid concurrent-write races
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address PR feedback: harden memory store IO + consolidation behavior
    
    - Atomic writes via os.replace + temp sibling for topic, state, and index files so
      crashes/disk-full failures cannot leave a truncated half-written file.
    - Stop creating directories on read paths: list_topics/read_state/search_transcripts
      and get_messages return empty when nothing has been written. mkdir is deferred to
      the actual save path (write_topic/write_state/save_messages).
    - Escape lines that look like markdown headings on render and unescape them on parse,
      so a memory or summary containing '## Summary'/'## Memories' cannot tamper with the
      topic file structure.
    - Narrow extraction/consolidation chat-client failure handling to ChatClientException,
      asyncio.TimeoutError, and OSError. Programmer errors (AttributeError, TypeError, ...)
      now propagate so misconfigured clients fail loudly.
    - Log a payload-prefix preview for every silent shape branch in _extract_memories and
      _consolidate_topic so unparsable extractor output is debuggable instead of invisible.
    - Restructure _run_consolidation: read maintenance state and topic snapshot under the
      state lock, run the LLM consolidation loop without holding the state lock, and only
      advance last_consolidated_at/sessions_since_consolidation if at least one topic
      succeeded. Transient consolidation failures now leave the maintenance window in
      place so the next after_run retries instead of silently sliding forward.
    - Add regression tests for: markdown-marker round-trip, atomic-write recovery on
      os.replace failure, no-mkdir on pure read paths, transient consolidation failure
      preserves state, and propagation of programmer errors.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Add Python parity for HttpRequestAction in declarative workflow (#5599)
    * Add Python parity for HttpRequestAction in declarative workflow
    
    * Ran pyupgrade and pright to fix CI issues
    
    * Fix conversation ID dot parsing for http executor
    
    * Removed unnecessary export command
  • Python: Support OpenAI and Gemini allowed_tools tool choice (#5322)
    * Support OpenAI allowed_tools in ToolMode (#5309)
    
    Add allowed_tools field to ToolMode TypedDict, enabling users to restrict
    which tools the model may call via the OpenAI allowed_tools tool_choice
    type. This preserves prompt caching by keeping all tools in the tools list
    while limiting which ones the model can invoke.
    
    - Add allowed_tools: list[str] to ToolMode TypedDict
    - Add validation in validate_tool_mode() (only valid when mode == "auto")
    - Convert to OpenAI API format in _prepare_options()
    - Add tests for validation and API payload generation
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: Support OpenAI `allowed_tools` tool choice in Python SDK
    
    Fixes #5309
    
    * Fix #5309: Validate allowed_tools shape and add Chat Completions client support
    
    - validate_tool_mode now checks allowed_tools is a non-string sequence of
      strings and normalizes to list[str], raising ContentError for invalid types
    - Add missing allowed_tools branch in _chat_completion_client._prepare_options
      so allowed_tools is emitted as the OpenAI allowed_tools wire format instead
      of being silently dropped
    - Add tests for invalid allowed_tools types (string, int, mixed), empty list,
      tuple normalization, and Chat Completions client payload generation
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix: support allowed_tools with mode 'required' in addition to 'auto'
    
    OpenAI's allowed_tools tool_choice type supports both mode 'auto' and
    'required'. Update validation, client conversion, and tests to allow
    both modes instead of restricting to 'auto' only.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix: use Gemini VALIDATED mode for allowed_tools, warn in unsupported providers
    
    - Use FunctionCallingConfigMode.VALIDATED instead of ANY when allowed_tools
      is set with auto mode in Gemini, preserving optional tool-call semantics.
    - Handle allowed_tools in required mode with required_function_name precedence.
    - Fix allowed_names guard to use identity check (is not None) so empty lists
      are preserved.
    - Bump google-genai minimum to >=1.32.0 (VALIDATED added in that version).
    - Add warnings in Anthropic and Bedrock when allowed_tools is set but not
      supported.
    - Add Gemini unit tests for allowed_tools with auto, required, empty list,
      and required_function_name precedence scenarios.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix: Chat Completions API does not support allowed_tools, add integration tests
    
    - Chat Completions API (_chat_completion_client.py) now warns and falls
      back to plain mode when allowed_tools is set, since the /chat/completions
      endpoint does not support the allowed_tools type.
    - Add allowed_tools integration test param to both OpenAIChatClient
      (Responses API) and OpenAIChatCompletionClient parametrized option tests.
    - Update Chat Completions unit tests to reflect the warn-and-fallback
      behavior.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix: remove unused walrus operator variable in chat completion client
    
    Remove assigned-but-never-used variable 'allowed' flagged by ruff F841.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <copilot@github.com>
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: bump package versions for 1.2.2 release (#5561)
    * Python: bump package versions for 1.2.2 release
    
    PATCH bump (1.2.1 -> 1.2.2) for the released cohort. Five PRs land in this
    window:
    
    - agent-framework-openai: fix file_search citations breaking the assistant-
      message history roundtrip (#5557) — drives the released-tier PATCH
    - agent-framework-orchestrations: [BREAKING] standardize orchestration
      terminal outputs as AgentResponse (#5301)
    - agent-framework-core, agent-framework-declarative: preserve Workflow.run()
      shared state across calls, accept list[Message] in declarative start
      executor, and coerce Enum values when serializing PowerFx symbols (#5531)
    - agent-framework-foundry-hosting: add hosted Durable Workflow support
      (#5531)
    - agent-framework-azure-contentunderstanding: new alpha package — Azure AI
      Content Understanding context provider (#4829)
    - dependencies: workspace package dependency refresh (#5555)
    
    Per lockstep convention, all 21 beta packages stamp 1.0.0b260429 and all 4
    alpha packages (now including the new contentunderstanding) stamp
    1.0.0a260429. Date stamp reflects 2026-04-29 Pacific. Every non-core package
    floor on agent-framework-core is raised to >=1.2.2; the new
    contentunderstanding package's stale >=1.0.0 floor is brought into line.
    
    Two follow-on fixes bundled to keep validate-dependency-bounds-test green
    at lowest-direct resolution:
    - Bump agent-framework-azure-contentunderstanding's azure-ai-content
      understanding lower bound from >=1.0.0 to >=1.0.1 (1.0.0 ships without
      proper typing — pyright reports 65 unknown-type errors)
    - Add pyright ignore comments to core/foundry/__init__.pyi for the new
      alpha package's type-stub imports, since alpha packages are not in
      core's [all] extra and therefore aren't installed at lowest-direct
    
    * Python: add #5552 to 1.2.2 CHANGELOG
    
    Add the streaming-span observability fix to the Fixed section. PR is on
    upstream/main but not yet pulled into origin/main; the code itself will
    land via the PR merge.
    
    * Python: address PR #5561 review feedback on dependency bounds
    
    Two packaging fixes flagged in review:
    
    1. agent-framework-azure-contentunderstanding: add agent-framework-foundry
       as a runtime dependency. The package's README directs users to
       `pip install agent-framework-azure-contentunderstanding --pre` and the
       basic example imports `FoundryChatClient` from `agent_framework.foundry`,
       so the documented install path was failing with ImportError. Pulling
       agent-framework-foundry into deps makes the advertised entry path
       self-contained.
    
    2. agent-framework-foundry: bump agent-framework-openai lower bound from
       >=1.1.0 to >=1.2.2,<2. Foundry imports private modules from
       agent_framework_openai (`_chat_client.py:22`, `_agent.py:34`), so
       resolvers were free to pair foundry==1.2.2 with older OpenAI versions
       that lack this release's coordinated Responses/history fix. Lockstep the
       floor with the released cohort to prevent mismatched installs.
    
    Both changes pass `validate-dependency-bounds-test` lower + upper at
    their respective packages.
  • Python: Fix spans not correctly nested when using streaming (#5552)
    * Fix spans not correctly nested when using streaming
    
    * fix pre commit
    
    * Address comments
  • Python: Feature/hosted dwf (#5531)
    * Fix declarative Workflow.as_agent() by accepting list[Message] in start executor
    
    The declarative start executor (JoinExecutor) only advertised dict and str
    in its input_types, so WorkflowAgent.__init__ rejected it with
    'Workflow's start executor cannot handle list[Message]'.
    
    Add list[Message] to the JoinExecutor handler annotation and add a
    matching branch in DeclarativeActionExecutor._ensure_state_initialized
    that extracts the last user-message text and falls through to the
    string-input initialization path, so =System.LastMessageText works
    end-to-end via as_agent().
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Populate Conversation.messages from list[Message] trigger
    
    When Workflow.as_agent() is invoked with a list[Message], the start executor now populates Conversation.messages / Conversation.history / System.conversations.{id}.messages with prior turns only (excluding the latest user message), and surfaces the latest user message via Inputs.input and System.LastMessage*. This matches InvokeAzureAgent's contract that the messages binding holds prior turns and the executor itself appends the new user input before invoking, avoiding double-append of the trailing user turn while preserving full history (incl. assistant/system/tool roles and multi-modal content) for downstream actions.
    
    * Coerce Enum values when serializing PowerFx symbols
    
    MessageRole and other str-subclass Enums passed isinstance(v, str) and were forwarded to pythonnet unchanged. pythonnet then raised 'MessageRole value cannot be converted to System.String' for every PowerFx primitive when ConditionGroup/Expr eval walked the symbol table containing Conversation.messages. Reduce Enum members to their underlying value before the primitive check so eval sees plain strings/ints.
    
    * Foundry hosting: pass full conversation history to workflow agents
    
    _handle_inner_workflow only forwarded the latest user turn to WorkflowAgent.run, even though _handle_inner_agent already prepends history fetched from Foundry storage to the messages it sends a regular agent. Declarative workflows reset Conversation.messages on every run (state.initialize), so checkpoint replay alone does not give them prior turns - the host has to pass them in, the same way it does for non-workflow agents. Mirror that contract: fetch context.get_history() and pass [*history, *input_messages] to the workflow agent.
    
    * feat(workflows): support combined message + checkpoint_id for multi-turn continuation
    
    Allow Workflow.run(message=..., checkpoint_id=...) so callers can restore
    prior workflow state from a checkpoint AND deliver a new message to the
    start executor in a single call. The existing reset_context logic
    already preserves shared state when checkpoint_id is set, so this gives
    us 'fresh start executor invocation with prior state intact' - exactly
    what hosted multi-turn declarative workflows need.
    
    - _workflow.py: drop the message+checkpoint_id mutual exclusion and
      update _execute_with_message_or_checkpoint to do both (restore then
      execute) when both are provided.
    - _agent.py: in _run_core's checkpoint branch, also forward
      input_messages so WorkflowAgent.run(messages, checkpoint_id=...) works
      end-to-end. Falls back to the legacy 'restore only' behavior when
      messages are absent.
    - _declarative_base.py: detect continuation in _ensure_state_initialized
      by checking whether DECLARATIVE_STATE_KEY already exists in shared
      state; if so, refresh inputs/LastMessage* and append non-user trigger
      messages instead of calling state.initialize() (which would wipe
      Conversation/Local/System).
    - foundry_hosting/_responses.py: collapse the host's two-call pattern
      (restore-only, then fresh run) into a single combined call now that
      the underlying APIs support it.
    - tests: drop the assertion that combined message+checkpoint_id raises.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Pivot: preserve workflow state across run() calls
    
    Replace the prior 'combined message + checkpoint_id in one run()' approach
    with a cleaner default: Workflow.run no longer wipes shared state or runner-
    context messages between calls. Iteration counting and per-run kwargs still
    reset on a fresh-message run; checkpoint and responses runs are continuations
    that preserve everything.
    
    This lets a WorkflowAgent be invoked repeatedly on the same instance and
    maintain multi-turn context (e.g. accumulated Conversation.messages) without
    asking developers to opt in. Hosted-agent multi-turn pattern becomes two
    explicit calls: restore-from-checkpoint (drive to idle), then run-with-message.
    
    Key changes:
    - _workflow.py: drop _state.clear() and reset_for_new_run() from run().
      Reset iteration count and run kwargs on fresh-message runs only.
      Restore 'Cannot provide both message and checkpoint_id' validation.
      Add async guard: fresh-message run with un-drained pending executor
      messages from a prior run is invalid.
    - _runner.py: clear _state before import_state in restore_from_checkpoint
      so restore is authoritative (import_state merges, not replaces).
    - _agent.py: revert checkpoint branch to restore-only (no message forward).
    - _responses.py (foundry_hosting): two-call host pattern - restore checkpoint
      silently, then run with new user input.
    - tests: state-preservation is the new default; rebuild Workflow for clean slate.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fix CI lint and mypy issues from prior pivot commit
    
    - _workflow.py: collapse nested if (SIM102), drop redundant assignment (RET504)
    - _declarative_base.py: remove unused last_user_msg = tail assignment
      whose Message | None type clashed with the prior Message-typed branch
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address PR review: fix Inputs.input update and checkpoint storage path
    
    - _declarative_base.py: continuation branch was writing 'Inputs.input' via
      state.set, which routes to the Custom namespace and never updates the
      PowerFx-visible Workflow.Inputs.input. Update state_data['Inputs'] in
      place via get_state_data / set_state_data so =Workflow.Inputs.input and
      =inputs.input see the new turn's user text on continuation.
    - _declarative_base.py: refresh docstring to clarify that on a list[Message]
      trigger, Conversation.messages excludes the current user message at the
      start of the turn (agent executors append it before invoking the inner
      agent).
    - _responses.py: when previous_response_id is supplied (no conversation_id),
      the prior checkpoint lives under <storage>/<previous_response_id> but new
      checkpoints must land under <storage>/<current_response_id> for the next
      turn to find them. Hold onto restore_storage from the get_latest lookup
      and pass it to the restore-only run; pass write_storage (current id) to
      the message-delivery run and to checkpoint cleanup.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fix pyright errors in _declarative_base.py for CI
    
    - Replace state._state.get(...) protected access with new public
      is_initialized() method on DeclarativeWorkflowState (also clearer intent
      for the continuation detection use case).
    - Add narrow pyright ignores for the Any-typed trigger paths that pyright
      cannot fully narrow (the list[Message] isinstance loop and the
      fallback-DefaultTransform branch).
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address Copilot review batch: tests + Workflow.reset escape hatch
    
    * Add Workflow.reset() public method as recovery escape hatch when an
      in-flight run aborted (e.g. WorkflowConvergenceException) and the
      workflow is not checkpointed. Update the in-flight messages guard's
      error message to point callers at it.
    
    * Add test_workflow_run_inflight_messages_guard exercising both the
      guard (sync + streaming) and the reset() recovery path.
    * Add test_workflow_reset_rejects_concurrent_runs to lock down the
      in-progress guard on reset.
    
    * Add test_as_agent_continuation_preserves_prior_state covering the
      is_continuation branch in _ensure_state_initialized: stamps a marker
      between calls and asserts it survives, while Inputs.input and
      System.LastMessageText refresh to the new turn.
    
    * Add test_powerfx_safe.py regression tests for the Enum branch in
      _make_powerfx_safe (str-subclass, int-subclass, plain Enum, and
      Enums nested in dict/list).
    
    * Drop redundant @pytest.mark.asyncio on
      test_as_agent_round_trip_with_last_message_text (asyncio_mode='auto').
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Skip restore-only pre-pass when checkpoint has pending request_info
    
    Address Copilot review on _responses.py: the restore-only checkpoint
    replay populates self._agent.pending_requests for any request_info
    events captured in the checkpoint. The follow-up run(input_messages)
    call would then route through WorkflowAgent._process_pending_requests,
    which expects function-response content and rejects plain text input
    as 'unexpected content while awaiting request info responses'.
    
    Workflows resumed from a checkpoint that was idle-with-pending-requests
    would therefore fail every subsequent plain-text user turn. Inspect the
    loaded checkpoint and skip the pre-pass when its
    pending_request_info_events dict is non-empty. Workflows that don't use
    request_info (the current sample set) are unaffected; workflows that do
    will fall through to a fresh-message run rather than silently corrupting
    the routing state.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Loosen azure-ai-agentserver-* pins to major version
    
    The exact-version pins on azure-ai-agentserver-{core,responses,invocations}
    forced foundry-hosting consumers to upgrade in lockstep with every beta
    bump from upstream. Switch to '>=current,<next-major' so we pick up patch
    and feature updates within the same major series without a coordinated
    release.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Drop Workflow.reset(); checkpointing is the recovery path
    
    The in-flight-messages guard prevented silent misbehavior, but the
    companion Workflow.reset() escape hatch only cleared _messages while
    leaving iteration count, executor-local state, and shared State
    mutations in an indeterminate condition after a mid-run failure. That
    gave a false sense of recovery.
    
    Recovery from a mid-run failure is supported only via checkpoint
    restoration. Keep the guard and reframe its error message accordingly;
    remove reset() and its tests.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address Tao's review on PR 5531
    
    - Rename Workflow._run_workflow_with_tracing parameter
      is_fresh_message_run -> is_continuation (default False, inverted).
      Fresh-message turns reset per-run accounting; continuations
      (checkpoint restores, responses replays) preserve it.
    - Simplify the in-flight-messages guard: _validate_run_params already
      enforces that 'message' is mutually exclusive with 'checkpoint_id'
      and 'responses', so the additional checks were dead code.
    - foundry_hosting _responses: move the restore-only pre-pass above
      emit_created/emit_in_progress; restore is preparation, not run
      progress. Drop the skip-restore gate (state preservation requires
      unconditional restore) and instead clear agent.pending_requests
      after the restore-only call. Collapse over-conditioned check.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Don't clear pending_requests after restore-only pre-pass
    
    Pending requests in the restored checkpoint represent genuinely
    outstanding HITL requests. The next user input may carry function
    responses (Responses API `function_call_output` items become
    FunctionResultContent / FunctionApprovalResponseContent), which
    `WorkflowAgent._process_pending_requests` correctly extracts and
    matches against the populated `pending_requests`. Clearing them
    after restore would silently drop that state and force the next turn
    to be treated as a fresh input even when the caller is responding to
    the outstanding requests.
    
    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: Evan Mattson <35585003+moonbox3@users.noreply.github.com>
  • Python: [BREAKING] Standardize orchestration terminal outputs as AgentResponse (#5301)
    * Fix orchestration outputs so as_agent() returns the final answer only. Align other orchestration outputs
    
    * Fix orchestration output issues from review comments
    
    1. Sample cleanup: Remove commented-out FoundryChatClient block and update
       prerequisites to reference OPENAI_CHAT_MODEL_ID instead of FOUNDRY_* vars.
    
    2. Sequential approval output: Change _EndWithConversation.end_with_agent_executor_response
       from a no-op sink to yield response.agent_response. When the last participant is
       AgentApprovalExecutor (via with_request_info), _EndWithConversation is the output
       executor so the yield produces the terminal answer. When the last participant is a
       regular AgentExecutor, _EndWithConversation is not in output_executors so the yield
       is silently filtered out.
    
    3. Forward data events through WorkflowExecutor: _process_workflow_result now also
       forwards 'data' events from sub-workflows so that emit_intermediate_data=True on
       AgentExecutor works correctly when wrapped in AgentApprovalExecutor.
    
    4. Concurrent docstring: Update _AggregateAgentConversations docstring to say
       'deterministic participant order' instead of 'completion order'.
    
    5. Add test_concurrent_intermediate_outputs_emits_data_events verifying that
       ConcurrentBuilder(intermediate_outputs=True) emits per-participant data events
       alongside the single aggregated output event.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Add tests for sequential workflow with_request_info and intermediate_outputs (#5301)
    
    Address PR review comments 2, 3, and 5:
    
    - Add test_sequential_request_info_last_participant_emits_output:
      Verifies that when the last participant is wrapped via with_request_info()
      (AgentApprovalExecutor), the workflow still emits a terminal output after
      approval, exercising the _EndWithConversation.end_with_agent_executor_response
      fallback path.
    
    - Add test_sequential_request_info_with_intermediate_outputs_emits_data_events:
      Verifies that emit_intermediate_data=True works correctly through
      AgentApprovalExecutor wrapping—WorkflowExecutor._process_result already
      forwards data events from sub-workflows, so intermediate agent responses
      surface as data events in the parent workflow.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fix pyright type errors from AgentResponse output refactor (#5301)
    
    Update cast() calls in _group_chat.py and _magentic.py to use
    WorkflowContext[Never, AgentResponse] instead of the old
    WorkflowContext[Never, list[Message]], matching the updated method
    signatures in _base_group_chat_orchestrator.py.
    
    Fix _sequential.py _EndWithConversation.end_with_agent_executor_response
    to declare WorkflowContext[Any, AgentResponse] so yield_output accepts
    AgentResponse[None].
    
    Fix _workflow_executor.py data event forwarding to handle nullable
    executor_id.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fix pyright reportUnknownVariableType in _agent.py (#5301)
    
    Extract event.data into a typed local variable before the isinstance
    check to avoid pyright narrowing it to AgentResponse[Unknown].
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fix pyright reportMissingImports for orjson in file history samples (#5301)
    
    Add pyright: ignore[reportMissingImports] to orjson imports that are
    already guarded by try/except ImportError, matching the existing pattern
    used elsewhere in the samples.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address review feedback for #5301: review comment fixes
    
    * Address review feedback for #5301: review comment fixes
    
    * Revert sequential_workflow_as_agent sample to FoundryChatClient
    
    Reverts the mistaken switch from FoundryChatClient to OpenAIChatClient
    in the sequential workflow as agent sample.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address ultrareview feedback: emit_data_events rename + WorkflowAgent reasoning conversion
    
    Layered on top of the prior review-feedback work in this branch.
    
    Renames:
    - AgentExecutor.emit_intermediate_data -> emit_data_events (mechanical
      rename; orchestration semantics live at the orchestration layer, not
      the general-purpose executor). Forwarded through MagenticAgentExecutor,
      AgentApprovalExecutor, and all orchestration call sites.
    - HandoffAgentExecutor._check_terminate_and_yield -> _should_terminate
      (pure predicate; no longer yields anything). HandoffBuilder docstring
      rewritten to describe the new per-agent AgentResponse output contract.
    
    WorkflowAgent reasoning-content conversion:
    - Add _rewrite_text_to_reasoning(contents) and _msg_as_reasoning(msg)
      helpers; the as_agent() path now reframes text content from data events
      as text_reasoning Content blocks before merging into the AgentResponse.
    - Consumers iterate msg.contents and branch on content.type — same path
      they already use for Claude thinking and OpenAI reasoning. No new
      field on Message/AgentResponse/WorkflowEvent.
    - Streaming branch constructs fresh AgentResponseUpdate instances instead
      of mutating shared payloads (regression test added).
    - Helper _msg_maybe_reasoning consolidates the conditional rewrite at
      three call sites in the non-streaming conversion.
    
    Tests:
    - TestWorkflowAgentReasoningHelpers + TestWorkflowAgentDataEventReasoningConversion
      add 9 new tests covering helpers, non-streaming, streaming, mixed content,
      already-reasoning passthrough, and mutation-safety regression.
    - Updated test_sequential_as_agent_with_intermediate_outputs_includes_chain
      to assert text_reasoning content for intermediate agents.
    
    * Fix pyright: widen event.data to Any to avoid partial-unknown narrowing
    
    The streaming conversion path narrowed event.data via isinstance against
    generic AgentResponse, producing AgentResponse[Unknown] and tripping
    reportUnknownVariableType/reportUnknownMemberType. Binding data: Any
    before the check keeps runtime behavior identical while restoring a fully
    known type for downstream access.
    
    * Clean up design
    
    * Scope to agent output semantics only
    
    * yield AgentResponseUpdate streaming, AgentResponse non-streaming
    
    * Fix mypy/pyright: widen cast types at GroupChat callsites
    
    Eight callsites in _group_chat.py still cast to WorkflowContext[Never,
    AgentResponse] but the base orchestrator methods now accept the wider
    WorkflowContext[Never, AgentResponse | AgentResponseUpdate] (mode-aware
    yields). W_OutT is invariant, so the narrower cast is not assignable.
    Magentic was widened in the same commit; this catches the GroupChat
    callsites that were missed.
    
    * Python: skip flaky Foundry / Foundry Hosting integration tests (#5553)
    
    These two integration tests have been failing in the merge queue across
    multiple unrelated PRs (5301, 5531). Both are marked `@pytest.mark.flaky`
    with 3 retries, but all attempts fail back-to-back. Skipping both with a
    reason pointing to #5553 so they can be fixed properly without continuing
    to block unrelated merges.
    
    - packages/foundry_hosting/tests/test_responses_int.py::TestOptions::test_temperature_and_max_tokens
    - packages/foundry/tests/foundry/test_foundry_embedding_client.py::TestFoundryEmbeddingIntegration::test_text_embedding_live
    
    Also includes a one-line uv.lock specifier-ordering normalization
    auto-applied by the poe-check pre-commit hook.
    
    ---------
    
    Co-authored-by: Copilot <copilot@github.com>
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • [Python] Add agent-framework-azure-ai-contentunderstanding package (#4829)
    * feat: add agent-framework-azure-contentunderstanding package
    
    Add Azure Content Understanding integration as a context provider for the
    Agent Framework. The package automatically analyzes file attachments
    (documents, images, audio, video) using Azure CU and injects structured
    results (markdown, fields) into the LLM context.
    
    Key features:
    - Multi-document session state with status tracking (pending/ready/failed)
    - Configurable timeout with async background fallback for large files
    - Output filtering via AnalysisSection enum
    - Auto-registered list_documents() and get_analyzed_document() tools
    - Supports all CU modalities: documents, images, audio, video
    - Content limits enforcement (pages, file size, duration)
    - Binary stripping of supported files from input messages
    
    Public API:
    - ContentUnderstandingContextProvider (main class)
    - AnalysisSection (output section selector enum)
    - ContentLimits (configurable limits dataclass)
    
    Tests: 46 unit tests, 91% coverage, all linting and type checks pass.
    
    * fix: update CU fixtures with real API data, fix test assertions
    
    - Replace synthetic fixtures with real CU API responses (sanitized)
    - Update test assertions to match real data (Contoso vs CONTOSO,
      TotalAmount vs InvoiceTotal, field values from real analysis)
    - Add --pre install note in README (preview package)
    - Document unenforced ContentLimits fields (max_pages, duration)
    
    * chore: add connector .gitignore, update uv.lock
    
    * refactor: rename to azure-ai-contentunderstanding, fix CI issues
    
    Align naming with Azure SDK convention and AF pattern:
    - Directory: azure-contentunderstanding -> azure-ai-contentunderstanding
    - PyPI: agent-framework-azure-contentunderstanding -> agent-framework-azure-ai-contentunderstanding
    - Module: agent_framework_azure_contentunderstanding -> agent_framework_azure_ai_contentunderstanding
    
    CI fixes:
    - Inline conftest helpers to avoid cross-package import collision in xdist
    - Remove PyPI badge and dead API reference link from README (package not published yet)
    
    * feat: add samples (document_qa, invoice_processing, multimodal_chat)
    
    - document_qa.py: Single PDF upload, CU context provider, follow-up Q&A
    - invoice_processing.py: Structured field extraction with prebuilt-invoice
    - multimodal_chat.py: Multi-file session with status tracking
    - Add ruff per-file-ignores for samples/ directory
    - Update README with samples section, env vars, and run instructions
    
    * feat: add remaining samples (devui_multimodal_agent, large_doc_file_search)
    
    - S3: devui_multimodal_agent/ — DevUI web UI with CU-powered file analysis
    - S4: large_doc_file_search.py — CU extraction + OpenAI vector store RAG
    - Update README and samples/README.md with all 5 samples
    
    * feat: add file_search integration for large document RAG
    
    Add FileSearchConfig — when provided, CU-extracted markdown is automatically
    uploaded to an OpenAI vector store and a file_search tool is registered on
    the context. This enables token-efficient RAG retrieval for large documents
    without users needing to manage vector stores manually.
    
    - FileSearchConfig dataclass (openai_client, vector_store_name)
    - Auto-create vector store, upload markdown, register file_search tool
    - Auto-cleanup on close()
    - When file_search is enabled, skip full content injection (use RAG instead)
    - Update large_doc_file_search sample to use the integration
    - 4 new tests (50 total, 90% coverage)
    
    * fix: add key-based auth support to all samples
    
    Follow established AF pattern: check for API key env var first,
    fall back to AzureCliCredential. Supports AZURE_OPENAI_API_KEY and
    AZURE_CONTENTUNDERSTANDING_API_KEY environment variables.
    
    * FEATURE(python): add analyzer auto-detection, file_search RAG, and lazy init
    
    _context_provider.py:
    - Make analyzer_id optional (default None) with auto-detection by media
      type prefix: audio->audioSearch, video->videoSearch, else documentSearch
    - Add _ensure_initialized() for lazy client creation in before_run()
    - Add FileSearchConfig-based vector store upload
    - Fix: background-completed docs in file_search mode now upload to vector
      store instead of injecting full markdown into context messages
    - Add _pending_uploads queue for deferred vector store uploads
    
    devui_file_search_agent/ (new sample):
    - DevUI agent combining CU extraction + OpenAI file_search RAG
    
    azure_responses_agent (existing sample fix):
    - Add AzureCliCredential support and AZURE_AI_PROJECT_ENDPOINT fallback
    
    Tests (19 new), Docs updated (AGENTS.md, README.md)
    
    * feat(cu): MIME sniffing, media-aware formatting, unified timeout, vector store expiration
    
    - Add three-layer MIME detection (fast path → filetype binary sniff → filename
      fallback) to handle unreliable upstream MIME types (e.g. mp4 sent as
      application/octet-stream). Adds filetype>=1.2,<2 dependency.
    - Media-aware output formatting: video shows duration/resolution + all fields
      as JSON; audio promotes Summary as prose; document unchanged.
    - Unified timeout for all media types (removed file_search special-case that
      waited indefinitely for video/audio). All files use max_wait with background
      polling fallback.
    - Vector store created with expires_after=1 day as crash safety net.
    - Add 8 MIME sniffing tests (TestMimeSniffing class).
    
    * fix: merge all CU content segments for video/audio analysis
    
    CU's prebuilt-videoSearch and prebuilt-audioSearch analyzers split long
    media files into multiple `contents[]` segments. Previously,
    `_extract_sections()` only read `contents[0]`, causing truncated
    duration, missing transcript, and incomplete fields for any video/audio
    longer than a single scene.
    
    Now iterates all segments and merges:
    - duration: global min(startTimeMs) → max(endTimeMs)
    - markdown: concatenated with `---` separators
    - fields: same-named fields collected into per-segment list
    - metadata (kind, resolution): taken from first segment
    
    Single-segment results (documents, short audio) are unaffected.
    
    Update test fixture to realistic 3-segment video structure and expand
    assertions to verify multi-segment merging. Add documentation for
    multi-segment processing and speaker diarization limitation.
    
    * refactor: improve CU context provider docs and remove ContentLimits
    
    - Improve class docstring: clarify endpoint (Azure AI Foundry URL with
      example), credential (AzureKeyCredential vs Entra ID), and analyzer_id
      (prebuilt/custom with auto-selection behavior and reference links)
    - Add SUPPORTED_MEDIA_TYPES comments explaining MIME-based matching
      behavior and add missing file types per CU service docs
    - Use namespaced logger to align with other packages
    - Remove ContentLimits and related code/tests
    - Rename DEFAULT_MAX_WAIT to DEFAULT_MAX_WAIT_SECONDS for clarity
    
    * feat: support user-provided vector store in FileSearchConfig
    
    - Add vector_store_id field to FileSearchConfig (None = auto-create)
    - Track _owns_vector_store to only delete auto-created stores on close()
    - Remove vector_store_name; use internal _DEFAULT_VECTOR_STORE_NAME
    - Add inline comments for private state fields
    - Document output_sections default in docstring
    - Update AGENTS.md, samples, and tests
    
    * fix: remove ContentLimits from README code block
    
    * refactor: create CU client in __init__ instead of __aenter__
    
    Follow Azure AI Search provider pattern: create the client eagerly in
    __init__, make __aenter__ a no-op. This ensures __aexit__/close() is
    always safe to call and eliminates the _ensure_initialized() workaround.
    
    * docs: add file_search param to class docstring
    
    * feat: introduce FileSearchBackend abstraction for cross-client support
    
    Replace direct OpenAI client usage with FileSearchBackend ABC:
    - OpenAIFileSearchBackend: for OpenAIChatClient (Responses API)
    - FoundryFileSearchBackend: for FoundryChatClient (Azure Foundry)
    - Shared base _OpenAICompatBackend for common vector store CRUD
    
    FileSearchConfig now takes a backend instead of openai_client.
    Factory methods from_openai() and from_foundry() for convenience.
    
    BREAKING: FileSearchConfig(openai_client=...) -> FileSearchConfig.from_openai(...)
    
    * refactor: FileSearchBackend abstraction + caller-owned vector store
    
    * fix: file_search reliability and sample improvements
    
    - Poll vector store indexing (create_and_poll) to ensure file_search
      returns results immediately after upload
    - Set status to failed when vector store upload fails
    - Skip get_analyzed_document tool in file_search mode to prevent
      LLM from bypassing RAG
    - Simplify sample auth: single credential, direct parameters
    - Use from_foundry backend for Foundry project endpoints
    
    * perf: set max_num_results=10 for file_search to reduce token usage
    
    * fix: move import to top of file (E402 lint)
    
    * chore: remove unused imports
    
    * fix: align azure-ai-contentunderstanding with MAF coding conventions
    
    - Add module-level docstrings to __init__.py and _context_provider.py
    - Use Self return type for __aenter__ (with typing_extensions fallback)
    - Use explicit typed params for __aexit__ signature
    - Add sync TokenCredential to AzureCredentialTypes union
    - Pass AGENT_FRAMEWORK_USER_AGENT to ContentUnderstandingClient
    - Remove unused ContentLimits from public API and tests
    - Fix FileSearchConfig tests to match refactored backend API
    - Fix lifecycle tests to match eager client initialization
    
    * refactor: improve CU context provider API surface and fix CI
    
    - Refactor _analyze_file to return DocumentEntry instead of mutating dict
    - Remove TokenCredential from AzureCredentialTypes (fixes mypy/pyright CI)
    - Remove OpenAIFileSearchBackend/FoundryFileSearchBackend from public API
      (internal to FileSearchConfig factory methods)
    - Remove DocumentStatus from public exports (implementation detail)
    - Update file_search comments to reflect backend-agnostic design
    - Add DocumentStatus enum, analysis/upload duration tracking
    - Add combined timeout for CU analysis + vector store upload
    
    * fix: improve file_search samples and move tool guidelines to context provider
    
    - Delete redundant devui_file_search_agent sample (duplicate of azure_openai variant)
    - Move tool usage guidelines from sample agent instructions into context provider
      (extend_instructions in step 6, applied automatically for all file_search users)
    - Fix file_search purpose: use from_foundry() for Azure OpenAI (purpose="assistants")
    - Add filename hint in upload instructions for targeted file_search queries
    - Reduce max_num_results from 10 to 3 in both devui samples
    - Simplify agent instructions in both samples (remove tool-specific guidance)
    
    * feat: improve source_id, integration tests, and content assertions
    
    - Rename DEFAULT_SOURCE_ID to "azure_ai_contentunderstanding" (matches
      azure_ai_search convention)
    - Improve source_id docstring to describe default value
    - Clarify _detect_and_strip_files docstring (CU-supported files)
    - Add invoice.pdf test fixture from Azure CU samples repo
    - Refactor integration tests to use invoice.pdf directly (assert instead
      of skip when fixture missing)
    - Add URI content test (Content.from_uri with external URL)
    - Add "CONTOSO LTD." content assertion to all integration tests
    - Use max_wait=None in integration tests (wait until complete)
    
    * feat: reject duplicate filenames, add integration tests and sample comments
    
    - Reject duplicate document keys in before_run (skip + warn LLM to rename)
    - Update _derive_doc_key docstring to document uniqueness constraint
    - Add unit tests for duplicate filename rejection (cross-turn and same-turn)
    - Add integration test for data URI content (from_uri with base64)
    - Add integration test for background analysis (max_wait timeout + resolve)
    - Add filename recommendation comments to all samples' Content.from_data()
    
    * chore: improve doc key derivation, comments, and README
    
    - Replace hash-based doc key with uuid4 for anonymous uploads (O(1), no payload traversal)
    - Remove hashlib import (no longer needed)
    - Add File Naming section to README (filename importance, duplicate rejection)
    - Improve inline comments (_derive_doc_key, _extract_binary, URL parsing)
    
    * test: strengthen _format_result assertions with exact expected strings
    
    - Replace loose 'in' checks with exact 'assert formatted == expected'
      for both multi-segment and single-segment format tests
    - Add object-type fields (ShippingAddress, Speakers) to test data
      to cover nested dict/list serialization
    - Add position-based ordering assertions to verify structural
      correctness (header -> markdown -> fields across segments)
    
    * refactor: move invoice.pdf to shared sample_assets directory
    
    - Move invoice.pdf from tests/cu/test_data/ to
      python/samples/shared/sample_assets/ as single source of truth
    - Add INVOICE_PDF_PATH constant in test_integration.py pointing
      to the shared location
    - Update document_qa.py, invoice_processing.py, large_doc_file_search.py
      to use invoice.pdf instead of sample.pdf
    
    * refactor: reorganize samples into numbered dirs and simplify auth
    
    - Move script samples into 01-get-started/ with numbered prefixes
      (01_document_qa, 02_multimodal_chat, 03_invoice_processing,
       04_large_doc_file_search)
    - Move devui samples into 02-devui/ with 01-multimodal_agent and
      02-file_search_agent/{azure_openai_backend,foundry_backend}
    - Move invoice.pdf to CU package-local samples/shared/sample_assets/
    - Replace kwargs dicts with direct constructor calls; support both
      API key (AZURE_OPENAI_API_KEY) and AzureCliCredential
    - Update README sample table with new paths
    
    * fix: resolve CI lint errors (D205, RUF001, E501)
    
    - Fix D205: single-line docstring summary for _detect_and_strip_files
    - Fix RUF001: replace EN DASH with HYPHEN-MINUS in segment headers
    - Fix E501: wrap long assertion lines in tests
    - Also includes samples reorg and auth simplification
    
    * refactor: overhaul samples — FoundryChatClient, sessions, remove get_analyzed_document
    
    Samples:
    - Switch all samples from deprecated AzureOpenAIResponsesClient to FoundryChatClient
    - Add 02_multi_turn_session.py showing AgentSession persistence across turns
    - Rewrite 03_multimodal_chat.py with real PDF + audio + video (parallel
      analysis), per-modality follow-ups, cross-document question, elapsed
      time, user prompts, and input token counts
    - Renumber: 02->03 multimodal, 03->04 invoice, 04->05 file_search
    
    Context provider:
    - Remove get_analyzed_document tool -- full content is in conversation
      history via InMemoryHistoryProvider, no retrieval tool needed
    - Remove follow-up turn instructions about tools
    - Only list_documents tool remains (for status queries)
    - Update README to reflect tool removal
    
    * feat: add 05_background_analysis sample and fix 04 session/max_wait
    
    - Add 05_background_analysis.py demonstrating non-blocking CU analysis
      with max_wait=1s, status tracking via list_documents(), and automatic
      background task resolution on subsequent turns
    - Fix 04_invoice_processing.py: add max_wait=None and AgentSession
    - Rename 05→06 large_doc_file_search
    - Update README sample table
    
    * docs: update README and fix sample 06
    
    README:
    - Switch Quick Start from AzureOpenAIResponsesClient to FoundryChatClient
    - Add AgentSession to Quick Start example
    - Fix status values: pending -> analyzing/uploading/ready/failed
    - Fix env var: AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME -> AZURE_OPENAI_DEPLOYMENT_NAME
    - Update samples section with new paths, link to samples/README.md
    - Update multi-segment description to reflect per-segment fields
    
    Sample 06:
    - Fix from_openai -> from_foundry for Azure endpoints
    - Add AgentSession and max_wait=None
    
    * docs: rewrite README — concise format, prerequisites, CU link
    
    * fix: resolve pyright errors in _format_result segment cast
    
    * docs: add numbered section comments and fresh sample output to all samples
    
    - Add numbered section comments (# 1. ..., # 2. ...) per SAMPLE_GUIDELINES
    - Re-run all 6 samples and update expected output with real results
    - Fix duplicate sample output blocks in 04 and 05
    - Update README code example to use public invoice URL
    
    * feat: add load_settings support for env var configuration
    
    - Make endpoint optional in constructor — auto-loads from
      AZURE_CONTENTUNDERSTANDING_ENDPOINT env var via load_settings()
    - Add ContentUnderstandingSettings TypedDict
    - Add env_file_path/env_file_encoding params for .env file support
    - Add 4 unit tests: env var loading, explicit override, missing
      endpoint error, missing credential error
    - Update README with env var auto-resolution docs
    - Follows framework convention used by all other packages
    
    * docs: polish README — fix duplicate env var, add Next steps, service limits link
    
    * chore: trim invoice fixture from 199K to 33 lines
    
    Keep only VendorName, InvoiceTotal, DueDate, InvoiceDate, InvoiceId
    fields and first 500 chars of markdown. Strip spans/source/coordinates.
    Reduces fixture from 6.6MB to 1.2KB.
    
    * feat: per-file analyzer_id override via additional_properties
    
    - Read analyzer_id from Content.additional_properties for per-file override
    - Resolution order: per-file > provider-level > auto-detect by media type
    - Update class docstring documenting filename and analyzer_id properties
    - Update sample 04 to demonstrate per-file override (prebuilt-invoice)
    - Add unit test for per-file analyzer override
    
    * Trim PDF test fixture and clarify unique filename requirement
    
    - Trim analyze_pdf_result.json from 4427 to 23 lines by removing
      pages, words, lines, paragraphs, sections, spans, and source
      fields that are not used by any unit test.
    - Add docstring note that filename must be unique within a session;
      duplicate filenames are rejected and the file will not be analyzed.
    
    * Update python/packages/azure-ai-contentunderstanding/agent_framework_azure_ai_contentunderstanding/_context_provider.py
    
    Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
    
    * Update python/packages/azure-ai-contentunderstanding/agent_framework_azure_ai_contentunderstanding/_context_provider.py
    
    Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
    
    * Update python/packages/azure-ai-contentunderstanding/samples/02-devui/02-file_search_agent/azure_openai_backend/agent.py
    
    Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
    
    * Update python/packages/azure-ai-contentunderstanding/samples/02-devui/01-multimodal_agent/agent.py
    
    Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
    
    * Update python/packages/azure-ai-contentunderstanding/samples/01-get-started/06_large_doc_file_search.py
    
    Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
    
    * Fix AGENTS.md to match implementation; remove unused variable in test helper
    
    AGENTS.md:
    - Remove _ensure_initialized() reference (client is created in __init__)
    - Fix multi-segment docs: segments kept as list, not merged into fields
    - Remove get_analyzed_document() reference (only list_documents registered)
    - Update sample names to match current directory structure
    
    test_context_provider.py:
    - Simplify _make_data_uri() — remove unused 'encoded' variable
    
    * Fix premature file_search instruction for background-completed docs
    
    - Change _resolve_pending_tasks() instruction from 'Use file_search'
      to 'being indexed' since the upload hasn't completed yet at that point.
    - Add LLM instruction on upload failure in step 1b so the agent can
      inform the user the document isn't searchable.
    
    * fix: wrap long line in devui agent instructions (E501)
    
    * Fix Copilot review: unused logger, stray code in README, await cancelled tasks
    
    - _file_search.py: Remove unused logger and logging import
    - 01-multimodal_agent/README.md: Remove accidentally pasted Python script
    - _context_provider.py close(): Await cancelled tasks before closing
      client to prevent 'Task destroyed but pending' warnings
    
    * Sanitize doc keys and fix duplicate filename re-injection
    
    - Add _sanitize_doc_key() to strip control characters, collapse
      whitespace, and cap length at 255 chars — prevents prompt injection
      via crafted filenames in extend_instructions() calls.
    - Track accepted doc_keys in step 3 so step 5 only injects content
      for files actually analyzed this turn, not pre-existing duplicates.
    - Soften duplicate upload instruction wording (remove IMPORTANT/caps).
    
    * fix: add type annotation to tasks_to_cancel for pyright
    
    * Move per-session mutable state to state dict for session isolation
    
    Previously _pending_tasks, _pending_uploads, and _uploaded_file_ids
    were stored on self, shared across all sessions. This caused
    cross-session leakage: Session A's background task results could be
    injected into Session B's context.
    
    Now these are stored in the per-session state dict. Global copies
    (_all_pending_tasks, _all_uploaded_file_ids) are kept on self only
    for best-effort cleanup in close().
    
    Add 2 new TestSessionIsolation tests verifying that background tasks
    and resolved content stay within their originating session.
    
    * Remove unused AnalysisSection enum values
    
    Only MARKDOWN and FIELDS are handled by _extract_sections().
    Remove FIELD_GROUNDING, TABLES, PARAGRAPHS, SECTIONS to avoid
    exposing dead options to users.
    
    * Recursively flatten object/array field values for cleaner LLM output
    
    - Use SDK .value property with recursive extraction for object/array fields
    - Object: AmountDue -> {Amount: 610, CurrencyCode: USD} (was raw SDK dict)
    - Array: LineItems -> list of flattened items (was raw SDK list)
    - Update invoice fixture with object/array fields from prebuilt-invoice
    - Add 3 unit tests for object, array, and nested object field extraction
    
    * Preserve sub-field confidence; compare full expected JSON in tests
    
    * Remove incorrect MIME aliases (audio/mp4, video/x-matroska)
    
    * feat: add AnalysisInput, content_range, warnings, and category support
    
    - Use SDK AnalysisInput model instead of raw body dict for begin_analyze
    - Forward content_range from additional_properties to CU (page/time ranges)
    - Extract CU warnings with code/message/target (ODataV4Format) into output
    - Include content-level category from classifier analyzers
    - Add 5 new tests: warnings, category, content_range forwarding
    - Fix pyright with explicit casts; fix en-dash lint (RUF002)
    
    * fix: falsy-0 bug in duration calc; improve test coverage
    
    - Fix start_time_ms=0 treated as falsy by 'or' short-circuit, use
      'is None' checks instead for duration and segment time extraction
    - Update warnings test to use RAI ContentFiltered codes
    - Enrich warnings extraction to include code/message/target (ODataV4Format)
    - Add multi-segment video category test with per-segment assertions
    
    * refactor: split _context_provider.py into focused modules
    
    - Extract _constants.py: SUPPORTED_MEDIA_TYPES, MIME_ALIASES, analyzer maps
    - Extract _detection.py: file detection, MIME sniffing, doc key derivation
    - Extract _extraction.py: result extraction, field flattening, LLM formatting
    - _context_provider.py delegates via thin wrappers (793 lines, was 1255)
    - Update test imports to use _constants.py for SUPPORTED_MEDIA_TYPES
    
    * docs: update AGENTS.md with DocumentStatus, FileSearchBackend, and _file_search.py
    
    * refactor: replace AnalysisSection enum with Literal type for simpler DX
    
    - Remove AnalysisSection(str, Enum) class, replace with Literal["markdown", "fields"] type alias
    - Users can now pass plain strings: output_sections=["markdown"] — no extra import needed
    - AnalysisSection type alias still exported for type annotation use
    - Update all samples, tests, and internal code to use string literals
    - Address PR review feedback (eavanvalkenburg)
    
    * refactor: replace asyncio.Task with continuation tokens for serializable state
    
    - Replace state["_pending_tasks"] (asyncio.Task — not serializable) with
      state["_pending_tokens"] (dict of continuation token strings) so the
      framework can persist session state to disk/storage
    - Resume pending analyses via Azure SDK continuation_token mechanism
    - Fix: resumed pollers have stale cached status (done() always False),
      use asyncio.wait_for(poller.result()) with 10s min timeout instead
    - Remove _background_poll(), _all_pending_tasks, and task cancellation
    - Address PR review feedback (eavanvalkenburg): state must be serializable
    
    * fix: resolve CI lint (RUF052) and mypy (call-overload) errors
    
    * feat: add structured output (Pydantic model) to invoice processing sample
    
    - Use response_format=InvoiceResult for schema-constrained LLM output
    - Use output_sections=["fields"] only (no markdown needed for structured output)
    - Add LowConfidenceField model with confidence values
    - Add comments about prebuilt-invoice extensive schema vs simplified model
    - Address PR review feedback (eavanvalkenburg): use structured response
    
    * fix: use FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL env vars in all samples
    
    Replace AZURE_AI_PROJECT_ENDPOINT → FOUNDRY_PROJECT_ENDPOINT and
    AZURE_OPENAI_DEPLOYMENT_NAME → FOUNDRY_MODEL across all sample .py and
    README.md files. Address PR review feedback (eavanvalkenburg).
    
    * refactor: remove background_analysis sample, use FoundryChatClient in DevUI
    
    - Remove 05_background_analysis.py (per reviewer feedback — discuss max_wait
      design separately from samples)
    - Renumber 06_large_doc_file_search.py → 05_large_doc_file_search.py
    - Replace AzureOpenAIResponsesClient with FoundryChatClient in all DevUI samples
    - Replace client.as_agent() with Agent(client=client, ...) everywhere
    - Add max_wait comments explaining interactive vs batch usage
    - Update README.md and AGENTS.md
    - Address PR review feedback (eavanvalkenburg)
    
    * fix: vector_stores API moved from beta namespace in OpenAI SDK
    
    * docs: add comments about multi-file support and CU service limits in file_search sample
    
    * fix: broken markdown links after sample removal and renumbering
    
    * fix: migrate BaseContextProvider to ContextProvider (non-deprecated)
    
    * fix: Message(text=) -> Message(contents=[]) for API compatibility
    
    * Inline _constants.py into consuming modules
    
    Remove _constants.py and move constants to where they are used:
    - SUPPORTED_MEDIA_TYPES, MIME_ALIASES → _detection.py
    - MEDIA_TYPE_ANALYZER_MAP, DEFAULT_ANALYZER → _context_provider.py
    
    Addresses review feedback to reduce file count.
    
    * Mark package as alpha per package management skill
    
    - Version: 1.0.0b260401 → 1.0.0a260401
    - Classifier: Development Status 4 - Beta → 3 - Alpha
    - Add to PACKAGE_STATUS.md as alpha
    
    Follows the alpha package checklist from python-package-management skill.
    
    * Replace extend_instructions with extend_messages for status notifications
    
    Status/error/result notifications now use extend_messages (conversation
    context) instead of extend_instructions (system prompt). This avoids
    system prompt bloat and keeps behavioral directives separate from
    event notifications.
    
    - 11 extend_instructions calls → extend_messages (role='user')
    - 1 extend_instructions retained: tool usage guidelines (behavioral)
    - 6 test assertions updated to check context_messages
    
    All 84 unit tests + 5 live integration tests pass.
    
    * Fix lint: E402 import order, ISC004 implicit string concatenation
    
    - Move constants after all imports to fix E402
    - Wrap multi-line strings in parentheses inside contents=[] to fix ISC004
    
    * Fix lint: remove unused json import in invoice sample
    
    * Fix CI: apply ruff format + fix E501 line length after reformatting
    
    ruff format expands Message() calls to multi-line, pushing string
    indentation deeper. Break long strings to fit within 120 char limit
    after formatting. Also removes unused json import in sample.
    
    * Address review feedback: keyword-only args, accept pre-built client, remove wrappers
    
    - All __init__ args now keyword-only (matches FoundryChatClient pattern)
    - New 'client' param accepts pre-built ContentUnderstandingClient
    - core dep bound: >=1.0.0rc5 → >=1.0.0,<2
    - Self import moved after local imports
    - Removed 9 static method wrappers; callsites use module functions directly
    - Tests updated to import derive_doc_key and format_result directly
    
    * fix: remove duplicate ContentUnderstandingClient instantiation
    
    The client was being created twice — once inside the if/else block and
    again unconditionally after it. The second instantiation overwrote the
    pre-built client path and failed type checking when credential was None.
    
    * rename: azure-ai-contentunderstanding → azure-contentunderstanding
    
    Package: agent-framework-azure-ai-contentunderstanding → agent-framework-azure-contentunderstanding
    Module: agent_framework_azure_ai_contentunderstanding → agent_framework_azure_contentunderstanding
    Directory: packages/azure-ai-contentunderstanding → packages/azure-contentunderstanding
    
    Per agreement with PM and MAF team to drop 'AI' from the package name.
    
    * feat: add ContentUnderstanding re-export to agent_framework.foundry namespace
    
    Enables: from agent_framework.foundry import ContentUnderstandingContextProvider
    
    Exports: ContentUnderstandingContextProvider, FileSearchConfig,
    FileSearchBackend, AnalysisSection, DocumentStatus
    
    Updates all samples and README to use the foundry namespace import.
    
    * fix: add missing copyright headers to standalone sample scripts
    
    * chore: remove .vscode/settings.json and add to .gitignore
    
    * refactor: reuse FoundryChatClient.client for vector store ops in file_search sample
    
    Address review feedback from TaoChenOSU:
    - 05_large_doc_file_search.py: use client.client instead of manually
      constructing AsyncAzureOpenAI; remove openai dependency
    - azure_openai_backend/agent.py: import reorder only (AIProjectClient
      kept — required for sync vector store creation in DevUI)
    
    * fix: skip closing client when caller passes pre-built client
    
    When a ContentUnderstandingClient is passed via client=, the caller
    owns its lifecycle. Added _owns_client flag so close() only closes
    the client when we created it internally.
    
    ---------
    
    Co-authored-by: yungshinlin <yungshin@msn.com>
    Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
  • Python: bump package versions for 1.2.1 release (#5536)
    * Python: bump package versions for 1.2.1 release
    
    PATCH bump (1.2.0 -> 1.2.1) for the released cohort. The release window
    covers two PRs, no new public APIs:
    
    - agent-framework-core: prevent inner_exception from being lost in
      AgentFrameworkException (#5167)
    - samples: add requirements.txt and .env.example to the a2a/ hosting
      sample for pip-based setup (#5510)
    
    Per lockstep convention, all 21 beta packages stamp 1.0.0b260428 and all
    3 alpha packages stamp 1.0.0a260428, regardless of per-package code
    churn. Every non-core package floor on agent-framework-core is raised to
    >=1.2.1 to keep cohort signaling consistent. Date stamp reflects the
    local (Asia) cut date 2026-04-28.
    
    * Python: silence pyright unknown-type warnings in hosted-env detection
    
    `azure.ai.agentserver.core` is probed at runtime via `importlib.util.find_spec`
    and is not a declared dependency. The existing `# pyright: ignore[reportMissingImports]`
    suppresses the missing-import warning, but at `lowest-direct` resolution pyright
    still reports the imported symbol (`AgentConfig`) and its members (`from_env`,
    `is_hosted`) as unknown, breaking `validate-dependency-bounds-test` for
    `packages/core`.
    
    Extend the existing ignore to cover `reportUnknownVariableType` on the import
    and `reportUnknownMemberType` on the call site so the bounds check returns to
    green. Behavior is unchanged.
    
    Latent since #5455 (shipped in 1.2.0).
    
    * Python: raise agent-framework-gemini lower bound to google-genai>=1.65.0
    
    The Gemini chat client references several `google.genai.types` symbols
    (`FileSearch`, `ThinkingLevel`, `SearchTypes`, `McpServer`,
    `StreamableHttpTransport`, plus call-site keyword args `mcp_servers` and
    `search_types`) that are not present at the lower bound of `google-genai>=1.0.0`.
    At `lowest-direct` resolution this caused `validate-dependency-bounds-test` to
    fail for `packages/gemini` with eleven `reportAttributeAccessIssue` /
    `reportUnknownVariableType` errors.
    
    Walking the upstream `google.genai.types` API:
    - `GoogleMaps`, `AuthConfig`: present from 1.40.0
    - `FileSearch`: introduced in 1.49.0
    - `ThinkingLevel`: introduced in 1.55.0
    - `SearchTypes`, `McpServer`, `StreamableHttpTransport`: introduced in 1.65.0
    
    Bump the lower bound to 1.65.0 — the minimum version that exposes every symbol
    the package actually uses. Keep the `<2.0.0` upper cap unchanged. With this
    bump `validate-dependency-bounds-test` passes for both lower and upper
    resolution scenarios across all 27 workspace packages.
    
    Latent since #4847 (Gemini package introduction in 1.1.0); aggravated by
    subsequent feature additions that pulled in newer `types.*` symbols.
    
    * Python: add dependabot bumps to 1.2.1 CHANGELOG
    
    Catalog the 15 dependabot dependency updates that merged on `upstream/main`
    between python-1.2.0 and the 1.2.1 cut window under a new Changed section:
    
    - Workspace dev/runtime deps: `rich`, `prek`, `python-multipart`, `pyasn1`,
      `pytest` (ag-ui, devui, lab), `uv` (lab)
    - Frontend deps: `vite` (devui, chatkit), `postcss` (devui, chatkit, handoff),
      `picomatch` (devui, handoff)
    
    CHANGELOG-only — no source or pyproject.toml changes. PRs themselves merged
    upstream independently of this release branch and will be brought in via the
    PR merge.
  • Python: Add requirements.txt and .env.example to the a2a/ sample for pip-based setup (#5510)
    * Add requirements.txt and .env.example to a2a sample
    
    Beginners following the a2a/ sample had no pip-based install path:
    the directory lacked requirements.txt and .env.example, unlike every
    other 04-hosting/ sample.
    
    - Add requirements.txt with editable local package paths matching the
      pattern used in azure_functions/ and similar hosting samples
    - Add .env.example documenting FOUNDRY_PROJECT_ENDPOINT, FOUNDRY_MODEL,
      and A2A_AGENT_HOST
    - Update README Quick Start to cover both pip (.venv) and uv workflows
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: Add `requirements.txt` and `.env.example` to the `a2a/` sample for pip-based setup
    
    Fixes #5395
    
    * fix(a2a-sample): address PR review feedback for issue #5395
    
    - Remove 'from repo root' wording from Option B uv heading in README
      to avoid contradicting the 'run from this directory' instruction
    - Fix A2A_AGENT_HOST default in .env.example from 5001 to 5000 to match
      function-tools flow; add clarifying comments about port usage
    - Add note for pip users explaining they can replace 'uv run python'
      with 'python' once the virtual environment is activated
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address review feedback for #5395: Python: [Samples][Python] a2a/ sample missing requirements.txt — beginners cannot install dependencies
    
    ---------
    
    Co-authored-by: Copilot <copilot@github.com>
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: fix: prevent inner_exception from being lost in AgentFrameworkException (#5167)
    * fix: prevent inner_exception from being lost in AgentFrameworkException
    
    The __init__ method unconditionally called super().__init__() after
    the conditional call with inner_exception, effectively overwriting the
    exception args and losing the inner_exception reference.
    
    Add else branch so super().__init__() is only called once with the
    correct arguments.
    
    Fixes #5155
    
    Signed-off-by: bahtya <bahtyar153@qq.com>
    
    * test: add explicit tests for AgentFrameworkException inner_exception handling
    
    - test_exception_with_inner_exception: verifies args include inner exception
    - test_exception_without_inner_exception: verifies args only contain message
    - test_exception_inner_exception_none_explicit: verifies explicit None
    
    Covers both branches of the if/else in __init__.
    
    * fix: export AgentFrameworkException from package
    
    Bahtya
    
    ---------
    
    Signed-off-by: bahtya <bahtyar153@qq.com>