Commit Graph

304 Commits

  • 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: 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: 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>
  • [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: 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: 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: 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: [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: 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: 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: 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 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>
  • Python: (core): Add functional workflow API (#4238)
    * Add functional workflow api
    
    * cleanup
    
    * More cleanup
    
    * address copilot feedback
    
    * Address PR feedbacK
    
    * updates
    
    * PR feedback
    
    * Address review comments on functional workflow samples
    
    - Swap 05/06 get-started samples: agent workflow first (motivates
      why workflows exist), simple text workflow second
    - Rename text_pipeline → text_workflow, poem_pipeline → poem_workflow
    - Add @step to agent workflow sample (05) to demonstrate caching
    - Switch agent samples to AzureOpenAIResponsesClient with Foundry
    - Remove .as_agent() from agent_integration.py to focus on the key
      difference between inline agent calls vs @step-cached calls
    - Add commented-out Agent.run example in hitl_review.py
    - Add clarifying comment in _functional.py that event streaming is
      buffered (not true per-token streaming)
    - Add naive_group_chat.py functional sample: round-robin group chat
      as a plain Python loop
    - Update READMEs to reflect new file names and group chat sample
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fix pyright type errors
    
    * Address PR review comments on functional workflow API
    
    1. Allow request_info inside @step: Auto-inject RunContext into step
       functions that declare a RunContext parameter (by type or name 'ctx'),
       and expose get_run_context() for programmatic access.
    
    2. Handle None responses: Log a warning when a response value is None,
       and document the behavior in request_info docstring.
    
    3. Add executor_bypassed event type: Replace executor_invoked +
       executor_completed with a single executor_bypassed event when a step
       replays from cache, making cached vs live execution explicit.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Add regression tests for PR review comments on functional workflow API
    
    The three review comments (request_info in @step, None response handling,
    executor_bypassed event type) were already addressed in 7da7db4e. This
    commit adds cross-cutting regression tests that exercise the interactions
    between these features:
    
    - HITL in step with caching: preceding step bypassed on resume
    - Full checkpoint lifecycle with HITL step (interrupt -> resume -> restore)
    - None response inside step-level request_info logs warning
    - WorkflowInterrupted from step does not emit executor_failed
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address PR #4238 review comments on functional workflow API
    
    Comment 1 (request_info in @step): Already supported. Added comment in
    StepWrapper.__call__ explaining why WorkflowInterrupted (BaseException)
    safely bypasses the except Exception handler.
    
    Comment 2 (None response): Added docstring to _get_response clarifying
    the (found, value) return tuple semantics and None handling.
    
    Comment 3 (bypass event type): executor_bypassed is already a dedicated
    event type in WorkflowEventType. Updated comment at the bypass site to
    make the deliberate event type choice explicit.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Add experimental API warnings to functional workflow module
    
    Mark all public classes and decorators (workflow, step, RunContext,
    FunctionalWorkflow, StepWrapper, FunctionalWorkflowAgent) as
    experimental and subject to change or removal.
    
    * Address PR #4238 review comments from @eavanvalkenburg
    
    - RunContext docstring leads with purpose (opt-in handle for HITL,
      custom events, state) so readers importing it from the public surface
      understand its role before the mechanics (#2993513452).
    - Rename `06_first_functional_workflow.py` to
      `06_functional_workflow_basics.py`; the previous filename was
      confusing since it followed `05_functional_workflow_with_agents.py`
      (#2993531979).
    - Simplify `05_functional_workflow_with_agents.py` to call agents
      directly without a @step wrapper; the step-vs-no-step contrast lives
      in `03-workflows/functional/agent_integration.py`, keeping the
      get-started sample minimal (#2993525532).
    - Switch functional samples to `FoundryChatClient` for consistency with
      the rest of 01-get-started and 03-workflows (follow-up on #2876988570).
    - Use walrus in `hitl_review.py` final-state assertion (#2993572182).
    - Add expected-output block to `basic_streaming_pipeline.py` (#2993557609).
    - Clarify in `parallel_pipeline.py` that `@step` composes with
      `asyncio.gather` (#2993597282).
    - `naive_group_chat.py` threads `list[Message]` between turns instead
      of stringifying the transcript, preserving role/authorship (#2993583231).
    
    Drive-by: pre-commit hook sorts an unrelated import block in
    `samples/04-hosting/foundry-hosted-agents/responses/02_local_tools/main.py`.
    
    * Fix 10 functional-workflow API bugs from /ultrareview pass
    
    - bug_001: `ctx.request_info()` without an explicit `request_id` now derives
      a deterministic `auto::<index>` id from the call-counter, so HITL resume
      works correctly on the documented default path.  A uuid was regenerated on
      every replay, making resume impossible.
    
    - bug_002: `StepWrapper.__call__` no longer deepcopies arguments on the
      cache-hit replay branch.  The copy is only performed on the live-execution
      path (for the event log) and falls back to the original mapping if deepcopy
      fails, so steps whose args aren't deepcopyable (locks, sockets, sessions)
      can still resume from checkpoint.
    
    - bug_007: `_set_responses` now prunes each resolved `request_id` from
      `_pending_requests`, and the cache-hit branch in `request_info` does the
      same.  Previously, answered requests were re-serialized into every
      subsequent checkpoint and the final checkpoint falsely claimed pending
      requests even after the workflow completed.
    
    - bug_008: `_compute_signature_hash` now mixes the function's `co_code` and
      `co_names` into the checkpoint signature, so changes to the workflow body
      invalidate older checkpoints even when steps are accessed via module /
      class attributes (which `_discover_step_names` can't see statically).
      `RunContext._record_observed_step` records observed step names for
      diagnostics.
    
    - bug_010: `FunctionalWorkflow.run()` docstring corrected — says "at least
      one of message/responses/checkpoint_id" and explicitly notes `responses`
      may be combined with `checkpoint_id` (the validator already allowed this).
    
    - bug_013: `FunctionalWorkflowAgent` now surfaces `request_info` events as
      `FunctionApprovalRequestContent` items (mirroring graph `WorkflowAgent`),
      threads `responses=` and `checkpoint_id=` through to the underlying
      workflow, and exposes `pending_requests`.  Previously `.as_agent()`
      returned empty `AgentResponse` for HITL workflows — effectively unusable.
    
    - bug_014: `FunctionalWorkflow` now clears `_last_message`,
      `_last_step_cache`, and `_last_pending_request_ids` on clean completion.
      `run()` validates that `responses=` keys intersect the currently-pending
      request set (or raises with a clear error) instead of silently replaying
      against stale singleton state from a prior run.
    
    - bug_015: `FunctionalWorkflow.as_agent` signature now matches graph
      `Workflow.as_agent`: accepts `name`, `description`, `context_providers`,
      and `**kwargs`.  `FunctionalWorkflowAgent` stores the overrides.
    
    - bug_017: `RunContext.set_state` raises `ValueError` for underscore-
      prefixed keys (the framework's `_step_cache` / `_original_message` keys
      would silently clobber user state on checkpoint save and user
      underscore-prefixed state was dropped on restore).  Docstring documents
      the reserved prefix.
    
    - merged_bug_003: Workflow function arity is validated at decoration time.
      Multiple non-ctx parameters raise `ValueError` immediately (previously
      every arg past the first was silently dropped at call time).  Passing a
      non-None `message` to a ctx-only workflow raises `ValueError` instead of
      silently discarding the message.
    
    Test coverage: +18 regression tests covering every fix.  Full workflow
    suite now 766 passed, 1 skipped, 2 xfailed; full core suite 2338 passed.
    
    * Deslop functional.py fix commit
    
    - Remove dead instrumentation added in the prior commit that was never
      consumed: `RunContext._observed_step_names`,
      `RunContext._record_observed_step`, `FunctionalWorkflow._runtime_step_names`,
      and `FunctionalWorkflowAgent._extra_kwargs`.  The signature hash relies on
      `co_code` alone, which covers the attribute-access case without the
      collection-scaffolding.
    - Trim over-explanatory comments that restated what the code does or what
      it no longer does.  Keep only the comments that answer "why" for the
      non-obvious bits (deterministic id contract, defensive deepcopy, stale
      replay guard).
    - Compress the `_compute_signature_hash` and FunctionalWorkflow `__init__`
      block docstrings without losing the user-facing reasoning.
    
    Net -49 lines.  Regression lock preserved (766 passed, 1 skipped, 2 xfailed).
    
    * Fix functional workflow review feedback
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    Co-authored-by: Copilot <copilot@github.com>
  • Python: Fix user agent prefix (#5455)
    * Fix hosting user agent missing
    
    * Fix other providers
    
    * Add more tests
    
    * comments
    
    * Fix tests
  • Python: Hyperlight: thread-confine sandbox, skip parsing on host callbacks, schema/tool cleanup (#5424)
    * improved parsing of tool call results and tweaks
    
    * Address PR review: skip_parsing flag, broader registry close, comment fix
    
    - FunctionTool.invoke now takes a boolean skip_parsing flag instead of the
      SKIP_PARSING sentinel; the sentinel is still accepted as result_parser at
      construction time to opt out of parsing for every call. The two paths are
      equivalent.
    - _SandboxRegistry.close now invokes any sandbox close/shutdown hook on the
      entry's own worker thread (PyO3 unsendable), then shuts the worker down,
      then cleans up the per-entry temporary directories.
    - Clarified the _SandboxWorker.shutdown comment to describe the actual
      ThreadPoolExecutor.shutdown(wait=False, cancel_futures=False) semantics.
    - Hyperlight host callback uses skip_parsing=True (the new flag).
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Drop redundant 'is not SKIP_PARSING' guard that mypy 1.x flags
    
    After callable(configured_parser) the sentinel is already excluded; the extra
    identity check tripped mypy's non-overlapping identity warning.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fixed sandbox working on copy of tool
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Add second approval-required tool (set_stop_loss) to concurrent_builder_tool_approval sample (#4875)
    * Add set_stop_loss tool to concurrent_builder_tool_approval sample
    
    Add a second approval-gated tool (set_stop_loss) to the concurrent workflow
    tool approval sample to demonstrate handling approval requests for different
    tools in the same concurrent workflow.
    
    Changes:
    - Add set_stop_loss(symbol, stop_price) with approval_mode='always_require'
    - Include new tool in both agents' tool lists
    - Update agent instructions and prompt to encourage stop-loss usage
    - Update docstring to reflect two approval-gated tools
    - Update sample output to show mixed approval requests
    
    Fixes #4874
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Print tool name and arguments in concurrent sample's process_event_stream (#4874)
    
    Align process_event_stream in concurrent_builder_tool_approval.py to print
    the tool name and arguments when collecting approval requests, matching the
    sample output comment and the sequential_builder_tool_approval.py pattern.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Add None-guard for function_call access in tool approval sample (#4874)
    
    Add explicit None-checks before accessing function_call.name and
    function_call.arguments in concurrent_builder_tool_approval.py. The
    function_call field is typed Content | None, so direct attribute access
    without a guard could raise AttributeError and required type: ignore
    comments. The None-guard is consistent with the pattern used in
    _agent_run.py and removes the suppression comments.
    
    Also add a regression test verifying that function_call defaults to None
    and that the None-guard pattern is safe.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Apply same function_call None-guard to sibling tool-approval samples (#4874)
    
    Apply the same fix to sequential_builder_tool_approval.py and
    group_chat_builder_tool_approval.py, which had the identical pattern
    of accessing function_call.name/arguments without a None-guard.
    
    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: Foundry hosted agent V2 (#5379)
    * Python: Wrapper + Samples 1st (#5177)
    
    * Experiment
    
    * Update dependency and add non streaming
    
    * Add more samples
    
    * Rename samples
    
    * Add invocations
    
    * Comments 1
    
    * Comments 2
    
    * Comments 3
    
    * Improve README
    
    * Add local shell sample
    
    * WIP: Add eval and memory samples
    
    * Update user agent prefix
    
    * Update user agent prefix doc
    
    * Update dependency (#5215)
    
    * Add tests and more content types (#5235)
    
    * Add tests
    
    * fix tests and sample
    
    * Fix formatting
    
    * Remove function approval contents
    
    * Python: Refine samples and upgrade packages (#5261)
    
    * Refine samples and upgrade pacakges
    
    * Upgrade to a new package that fixes a bug
    
    * Update model env var
    
    * Move samples (#5281)
    
    * Python: Upgrade agentserver packages (#5284)
    
    * Upgrade agentserver packages
    
    * Fix new types
    
    * Python: Add special handling for workflows (#5298)
    
    * Add special handling for workflows
    
    * Address comments
    
    * Improve samples (#5372)
    
    * Python: Add more types (#5378)
    
    * Add more type supports
    
    * Upgrade packages
    
    * Remove TODOs in README
    
    * Fix README
    
    * Comments and mypy
    
    * User agent scoped
    
    * Fix README
    
    * Fix pre commit
    
    * Fix pre commit 2
    
    * Fix pre commit 3
    
    * Fix pre commit 4
    
    * Fix pre commit 5
    
    * Fix pre commit 6
    
    * Add azure-monitor-opentelemetry to dev deps
    
    Fixes Samples & Markdown CI failure. The PR's new transitive dep on
    azure-monitor-opentelemetry-exporter (via azure-ai-agentserver-core) makes
    pyright resolve the azure.monitor.opentelemetry namespace, flipping the
    check_md_code_blocks diagnostic for `configure_azure_monitor` from
    reportMissingImports (filtered) to reportAttributeAccessIssue (not filtered).
    Installing the umbrella azure-monitor-opentelemetry package in dev makes
    pyright resolve the symbol correctly, matching the install guidance the
    observability README already gives users.
    
    ---------
    
    Co-authored-by: Evan Mattson <evan.mattson@microsoft.com>
  • Python: Add support for Foundry Toolboxes (#5346)
    * Add support for the Foundry Toolbox in MAF
    
    Introduces a Foundry Toolbox integration: FoundryChatClient gains a
    get_toolbox() helper plus select_toolbox_tools(), normalize_tools in
    the core package flattens tool-collection wrappers (ToolboxVersionObject
    and generic iterables, while leaving Pydantic BaseModel instances
    alone), and the new agent_framework.foundry namespace re-exports the
    toolbox helpers. Ships with unit tests, a sample, and a design doc.
    
    azure-ai-projects is pinned to the public >=2.0.0,<3.0 range and the
    lockfile resolves from public PyPI. The toolbox test module skips when
    Toolbox* types are unavailable so CI stays green until the public 2.1.0
    SDK lands. OMC tooling directories (.omc/, .omx/) are gitignored.
    
    * Update to latest azure ai projects package
    
    * Improve sample
    
    * Rename ADR to 0025
    
    * Update ADR
    
    * Apply suggestion from @alliscode
    
    Co-authored-by: Ben Thomas <ben.thomas@microsoft.com>
    
    * Improve samples
    
    * Update test
    
    ---------
    
    Co-authored-by: Ben Thomas <ben.thomas@microsoft.com>
  • Python: Feat: Add finish_reason support to AgentResponse and AgentResponseUpdate (#5211)
    * feat: add finish_reason support to AgentResponse and AgentResponseUpdate
    
    Add finish_reason field to AgentResponse and AgentResponseUpdate classes,
    propagate it through _process_update() and map_chat_to_agent_update(),
    and add comprehensive unit tests.
    
    Fixes #4622
    
    * feat: add finish_reason to AgentResponse and AgentResponseUpdate
    
    * style: add copyright header to test_finish_reason.py
    
    * docs: add finish_reason to AgentResponse and AgentResponseUpdate docstrings
    
    * refactor: move finish_reason tests into test_types.py per review feedback
    
    Move all finish_reason test cases from the separate test_finish_reason.py
    file into test_types.py as requested by eavanvalkenburg. Tests are placed
    in a new '# region finish_reason' section at the end of the file.
    
    * fix: use model instead of model_id in _process_update
    
    Address PR review feedback from @eavanvalkenburg — ChatResponse and
    ChatResponseUpdate both use 'model', not 'model_id'.
    
    * fix: resolve SIM102 lint error in _process_update
    
    Combine nested if statements for AgentResponse finish_reason check
    to satisfy ruff SIM102 rule, with line wrapping to stay under 120 chars.
    
    * fix: resolve pyright reportArgumentType in map_chat_to_agent_update
    
    Add type: ignore[arg-type] for FinishReason NewType widening when
    passing ChatResponseUpdate.finish_reason to AgentResponseUpdate.
    Matches existing patterns in the codebase (40+ similar ignores).
  • Python: Fix Gemini client support for Gemini API and Vertex AI (#5258)
    * Add Gemini and Vertex AI client support
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address Gemini PR review feedback
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * removed sample run readme part
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com>
  • Add AgentExecutorResponse.with_text() to preserve conversation history through custom executors (#5255)
    Fixes #5246
    
    When a custom @executor transforms agent output and sends a plain str,
    the downstream AgentExecutor.from_str handler loses the full conversation
    context. This adds a with_text() helper that creates a new
    AgentExecutorResponse with replaced text while preserving the prior
    conversation chain, so AgentExecutor.from_response is invoked instead.
    
    - Add with_text(text) method to AgentExecutorResponse dataclass
    - Add 3 regression tests in test_full_conversation.py
    
    Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com>
  • Python: Add OpenAI types to default checkpoint encoding allow list (#5297)
    * Add OpenAI types to default checkpoint encoding allow list
    
    * Address comments
  • Python: Add context_providers and description to workflow.as_agent() (#4651)
    * Add context_providers and description to `workflow.as_agent()`
    
    * Add default workflow name and description
    
    * Positional
    
    * Move import
    
    ---------
    
    Co-authored-by: Tao Chen <taochen@microsoft.com>
    Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com>
  • Python: add experimental file history provider (#5248)
    * add experimental file history provider
    
    * Improve file history provider writes
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * typo
    
    * cleanup
    
    * cleanup
    
    * fix in readme
    
    * added security messages
    
    * Refine file history provider locking
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * added additional sample
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Move InMemory history provider injection to the first invocation (#5236)
    * Move InMemory history provider injection to the first invocation
    
    * Add tests
  • Python: skill name validation improvements (#4530)
    * Initial plan
    
    * Port .NET validation improvements to Python skills: reject consecutive hyphens and enforce directory name match
    
    Co-authored-by: SergeyMenshykh <68852919+SergeyMenshykh@users.noreply.github.com>
    
    * Fix E501 lint error: split long error message string in _validate_skill_metadata
    
    Co-authored-by: SergeyMenshykh <68852919+SergeyMenshykh@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
    Co-authored-by: SergeyMenshykh <68852919+SergeyMenshykh@users.noreply.github.com>
  • Python: Restrict persisted checkpoint deserialization by default (#4941)
    * Harden Python checkpoint persistence defaults
    
    Add RestrictedUnpickler to _checkpoint_encoding.py that limits which
    types may be instantiated during pickle deserialization.  By default
    FileCheckpointStorage now uses the restricted unpickler, allowing only:
    
    - Built-in Python value types (primitives, datetime, uuid, decimal,
      collections, etc.)
    - All agent_framework.* internal types
    - Additional types specified via the new allowed_checkpoint_types
      parameter on FileCheckpointStorage
    
    This narrows the default type surface area for persisted checkpoints
    while keeping framework-owned scenarios working without extra
    configuration.  Developers can extend the allowed set by passing
    "module:qualname" strings to allowed_checkpoint_types.
    
    The decode_checkpoint_value function retains backward-compatible
    unrestricted behavior when called without the new allowed_types kwarg.
    
    Fixes #4894
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix: resolve mypy no-any-return error in checkpoint encoding
    
    Add explicit type annotation for super().find_class() return value
    to satisfy mypy's no-any-return check.
    
    Fixes #4894
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Simplify find_class return in _RestrictedUnpickler (#4894)
    
    Remove unnecessary intermediate variable and apply # noqa: S301 # nosec
    directly on the super().find_class() call, matching the established
    pattern used on the pickle.loads() call in the same file.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address review feedback for #4894: Python: Harden Python checkpoint persistence defaults
    
    * Restore # noqa: S301 on line 102 of _checkpoint_encoding.py (#4894)
    
    The review feedback correctly identified that removing the # noqa: S301
    suppression from the find_class return statement would cause a ruff S301
    lint failure, since the project enables bandit ("S") rules. This
    restores consistency with lines 82 and 246 in the same file.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address review feedback for #4894: Python: Harden Python checkpoint persistence defaults
    
    * Address PR review comments on checkpoint encoding (#4894)
    
    - Move module docstring to proper position after __future__ import
    - Fix find_class return type annotation to type[Any]
    - Add missing # noqa: S301 pragma on find_class return
    - Improve error message to reference both allowed_types param and
      FileCheckpointStorage.allowed_checkpoint_types
    - Add -> None return annotation to FileCheckpointStorage.__init__
    - Replace tempfile.mktemp with TemporaryDirectory in test
    - Replace contextlib.suppress with pytest.raises for precise assertion
    - Remove unused contextlib import
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address PR #4941 review comments: fix docstring position and return type
    
    - Move module docstring before 'from __future__' import so it populates
      __doc__ (comment #4)
    - Change find_class return annotation from type[Any] to type to avoid
      misleading callers about non-type returns like copyreg._reconstructor
      (comment #2)
    
    Comments #1, #3, #5, #6, #7, #8 were already addressed in the current code.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address review feedback for #4894: review comment fixes
    
    * fix: use pickle.UnpicklingError in RestrictedUnpickler and improve docstring (#4894)
    
    - Change _RestrictedUnpickler.find_class to raise pickle.UnpicklingError
      instead of WorkflowCheckpointException, since it is pickle-level concern
      that gets wrapped by the caller in _base64_to_unpickle.
    - Remove now-unnecessary WorkflowCheckpointException re-raise in
      _base64_to_unpickle (pickle.UnpicklingError is caught by the generic
      except Exception handler and wrapped).
    - Expand decode_checkpoint_value docstring to show a concrete example of
      the module:qualname format with a user-defined class.
    - Add regression test verifying find_class raises pickle.UnpicklingError.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix: address PR #4941 review comments for checkpoint encoding
    
    - Comment 1 (line 103): Already resolved in prior commit — _RestrictedUnpickler
      now raises pickle.UnpicklingError instead of WorkflowCheckpointException.
    
    - Comment 2 (line 140): Add concrete usage examples to decode_checkpoint_value
      docstring showing both direct allowed_types usage and FileCheckpointStorage
      allowed_checkpoint_types usage. Rename 'SafeState' to 'MyState' across all
      docstrings for consistency, making it clear this is a user-defined class name.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix: replace deprecated 'builtin' repo with pre-commit-hooks in pre-commit config
    
    pre-commit 4.x no longer supports 'repo: builtin'. Merge those hooks into
    the existing pre-commit-hooks repo entry.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * style: apply pyupgrade formatting to docstring example
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix: resolve pre-commit hook paths for monorepo git root
    
    The poe-check and bandit hooks referenced paths relative to python/
    but pre-commit runs hooks from the git root (monorepo root). Fix
    poe-check entry to cd into python/ first, and update bandit config
    path to python/pyproject.toml.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fix pre-commit config paths for prek --cd python execution
    
    Revert bandit config path from 'python/pyproject.toml' to 'pyproject.toml'
    and poe-check entry from explicit 'cd python' wrapper to direct invocation,
    since prek --cd python already sets the working directory to python/.
    
    Also apply ruff formatting fixes to cosmos checkpoint storage files.
    
    Fixes #4894
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix: add builtins:getattr to checkpoint deserialization allowlist
    
    Pickle uses builtins:getattr to reconstruct enum members (e.g.,
    WorkflowMessage.type which is a MessageType enum). Without it in the
    allowlist, checkpoint roundtrip tests fail with
    WorkflowCheckpointException.
    
    Fixes #4894
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address review feedback for #4894: review comment fixes
    
    ---------
    
    Co-authored-by: Copilot <copilot@github.com>
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Fix response_format crash on background polling with empty text (#5146)
    * Guard against empty text in _parse_structured_response_value (#5145)
    
    When using response_format with background=True (Responses API), polling
    an in-progress response produces empty text. _parse_structured_response_value
    unconditionally passed this to model_validate_json/json.loads, causing
    ValidationError or JSONDecodeError.
    
    Add an early return of None when text is empty, matching the existing
    guard for response_format=None. This allows .value to safely return None
    for in-progress background responses.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: Fix `response_format` crash on background polling with empty text
    
    Fixes #5145
    
    ---------
    
    Co-authored-by: Copilot <copilot@github.com>
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Raise clear handler registration error for unresolved TypeVar annotations (#4944)
    * Raise clear handler registration error for unresolved TypeVar (#4943)
    
    Detect unresolved TypeVar in message parameter annotations during handler
    registration in both _validate_handler_signature (Executor) and
    _validate_function_signature (FunctionExecutor). Raises a ValueError with
    an actionable message recommending @handler(input=..., output=...) or
    @executor(input=..., output=...) instead of letting TypeVar leak through
    to a confusing TypeCompatibilityError during workflow edge validation.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address review feedback for #4943: reorder checks and harden function executor
    
    - Move TypeVar check before validate_workflow_context_annotation in
      _executor.py so users see the more actionable error first
    - Wrap get_type_hints in try/except in _function_executor.py matching
      the defensive pattern in _executor.py
    - Repurpose duplicate test to cover bounded TypeVar rejection
    - Add test_function_executor_allows_concrete_types for test symmetry
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Narrow get_type_hints except clause and add missing tests (#4943)
    
    - Narrow `except Exception` to `except (NameError, AttributeError, RecursionError)`
      in both _executor.py and _function_executor.py so unexpected failures in
      get_type_hints are not silently swallowed.
    - Add test_handler_unresolvable_annotation_raises to test_function_executor_future.py
      exercising the except branch of get_type_hints in the function executor path.
    - Add test_function_executor_rejects_bounded_typevar_in_message_annotation to
      test_function_executor.py for parity with the Executor bounded TypeVar test.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Add error ordering test for TypeVar vs WorkflowContext priority (#4943)
    
    Add test_handler_typevar_error_takes_priority_over_context_error to verify
    that when a handler has both a TypeVar message and an unannotated ctx, the
    TypeVar error is raised first (the more actionable issue).
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: Fix image content serialization sending null file_id to Foundry API
    
    Omit file_id from input_image dict when not present instead of including
    it as null, which Azure AI Foundry's stricter schema validation rejects.
    
    * Python: Fix Foundry API rejecting rich content in function_call_output
    
    Azure AI Foundry does not support list-format output in function_call_output
    items. Add SUPPORTS_RICH_FUNCTION_OUTPUT flag (default True) to
    RawOpenAIChatClient, set to False in RawFoundryChatClient so Foundry
    falls back to string output for tool results with images/files.
    
    Also omit file_id from input_image dicts when not set, since Foundry
    rejects explicit nulls.
    
    * Python: Surface rich tool content as user message when Foundry lacks support
    
    When SUPPORTS_RICH_FUNCTION_OUTPUT is False, image/file items from tool
    results are injected as a follow-up user message so the model can still
    process the visual content via Foundry's supported user message format.
    
    * Xfail Foundry image integration test for the meantime
    
    ---------
    
    Co-authored-by: Copilot <copilot@github.com>
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>