Commit Graph

497 Commits

  • Python: Integrate shell tool into harness agent (#6451)
    * Integrate shell tool into AgentHarness
    
    * Validate shell_executor exposes as_function() with a clear TypeError
    
    Addresses PR review feedback: a public factory should fail fast with an
    actionable error rather than a cryptic AttributeError when an incompatible
    shell_executor is supplied. Validation happens upfront, regardless of whether
    the client supports shell tools.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Type shell harness params via TYPE_CHECKING import
    
    Addresses PR review feedback: type shell_executor and
    shell_environment_provider_options instead of Any, using a TYPE_CHECKING
    import from agent_framework_tools.shell. The import never executes at
    runtime, so there is no circular dependency, and the lazy runtime import of
    ShellEnvironmentProvider is retained. Since ShellExecutor is a protocol
    without as_function(), the validated getattr result is invoked directly.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Add tool approval middleware (#6414)
    * Add Python tool approval middleware
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fix tool approval restored state handling
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Gate hidden approvals on explicit approval responses
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Handle string inputs in approval replay scan
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Cover argument-scoped approval rules
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Refine tool approval state and budgets
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fix tool approval PR CI failures
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Revert DevUI Aspire README link change
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: [Generated by SRE Agent] Fix MCP allowed_tools empty list handling (#6296)
    * Fix MCP allowed_tools empty list handling
    
    When allowed_tools is set to an empty list [], the falsy check
    'if not self.allowed_tools' incorrectly treats it as unconfigured
    (same as None), causing all tools to be exposed. Change to an
    explicit 'is None' check so that an empty list correctly results
    in no tools being allowed.
    
    Co-authored-by: Azure SRE Agent <noreply@microsoft.com>
    
    * Clarify allowed_tools docstring: None vs [] semantics
    
    Per Eduard's review on PR #6296: explicitly document that None exposes all tools and [] exposes none, across all four MCPTool / MCPStdioTool / MCPStreamableHTTPTool / MCPWebsocketTool docstrings.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * allowed_tools docstring: recommend load_tools=False for full disable
    
    Per Eduard's follow-up on PR #6296: `load_tools=False` is the cleaner idiom when you don't want to expose any tools. Reframe `allowed_tools=[]` in the docstring as a runtime guard / inspection-only path and cross-reference `load_tools`.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Azure SRE Agent <noreply@microsoft.com>
    Co-authored-by: Giles Odigwe <79032838+giles17@users.noreply.github.com>
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: HarnessAgent: Disable compaction when max tokens not provided (#6410)
    * HarnessAgent: Disable compaction when max tokens not provided
    
    * Fix regression.
    
    * Address PR comments
    
    * Require max_output_tokens to be positive
    
    Reject max_output_tokens=0 (must be positive), mirroring
    max_context_window_tokens. Addresses PR review feedback.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Parse MCP CallToolResult.structuredContent field to prevent tool results returning None (#6421)
    * Parse structuredContent from MCP CallToolResult (#3313)
    
    The _parse_tool_result_from_mcp method only iterated over the content
    field from CallToolResult, ignoring the structuredContent field entirely.
    MCP servers that return JSON data via structuredContent (e.g., Power BI
    MCP) appeared to return None.
    
    Add handling for structuredContent: when present, serialize it as JSON
    text and append it to the result list. This preserves the data for the
    LLM while maintaining backward compatibility with existing behavior.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: Parse MCP CallToolResult.structuredContent field to prevent tool results returning None
    
    Fixes #3313
    
    * Address review feedback: add default=str to json.dumps and remove .checkpoints/
    
    - Add default=str to json.dumps for structuredContent serialization so
      non-JSON-serializable values (e.g. bytes) degrade gracefully instead
      of raising TypeError
    - Remove all .checkpoints/ runtime artifacts from the repository
    - Add **/.checkpoints/ to .gitignore to prevent future accidental commits
    - Add test for non-serializable structuredContent values
    
    Fixes #3313
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address review feedback for #3313: Python: MCP CallToolResult.structuredContent field is not parsed, causing tool results to return None
    
    ---------
    
    Co-authored-by: Copilot <copilot@github.com>
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: [BREAKING] Add sampling guardrails to MCP tools (#6413)
    * Add sampling guardrails to MCP tools
    
    Add approval, token, and request-count controls to the MCP sampling
    callback used when an MCPTool is configured with a chat client.
    
    - Add `sampling_approval_callback`, `sampling_max_tokens`, and
      `sampling_max_requests` parameters to `MCPTool` and its
      `MCPStdioTool`, `MCPStreamableHTTPTool`, and `MCPWebsocketTool`
      subclasses, positioned directly after `client`.
    - Gate each server-initiated `sampling/createMessage` request behind the
      approval callback, which denies by default when no callback is provided.
    - Clamp the requested `maxTokens` to `sampling_max_tokens` and enforce a
      per-session request count via `sampling_max_requests`.
    - Log incoming sampling requests at WARNING level (counts only).
    - Export `SamplingApprovalCallback` from the public API.
    - Add tests, a sample, and documentation updates.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Make sampling denial message context-aware
    
    Distinguish the deny-by-default case (no approval callback configured)
    from an explicit denial by a configured `sampling_approval_callback`, so
    the returned ErrorData message is accurate for callback-driven denials
    and exceptions.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: bump package versions for 1.8.1 release (#6420)
    * Python: bump package versions for 1.8.1 release
    
    * Python: bump agent-framework-foundry-hosting for 1.8.1 release
    
    * Python: bump ag-ui and azurefunctions for 1.8.1 release
    
    * Remove incorrect agent-framework-foundry changelog entry for #6259
    
    * Add [1.8.1] changelog compare link and update [Unreleased] base
    
    ---------
    
    Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
  • Python: [Generated by SRE Agent] docs: clarify checkpoint storage security model and deserialization trust boundaries (#6295)
    * docs: clarify checkpoint storage security model and deserialization trust boundaries
    
    Add Security Model documentation sections to the checkpoint encoding and
    Azure Functions serialization modules explaining:
    - Checkpoint storage is a trusted data source requiring access controls
    - The RestrictedUnpickler allowlist is defense-in-depth, not a security boundary
    - Developer responsibilities for securing storage backends
    - Guidance on using allowed_types and strip_pickle_markers
    
    Co-authored-by: Azure SRE Agent <noreply@microsoft.com>
    
    * Apply suggestions from code review
    
    Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Azure SRE Agent <noreply@microsoft.com>
    Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
  • Python: Filter MCP tool kwargs to declared params via allowlist (#6399)
    * Filter MCP tool kwargs to declared params via allowlist
    
    Previously MCPTool combined framework runtime kwargs (from
    FunctionInvocationContext.kwargs) with the LLM-supplied arguments and
    stripped only a hardcoded denylist of known framework keys before
    forwarding to the MCP server. Any new framework-injected kwarg leaked to
    the server unless the denylist was updated.
    
    Switch to an allowlist built from each tool's declared parameters
    (inputSchema.properties). Only declared params are forwarded; everything
    else is stripped. Add an `additional_tool_argument_names` constructor
    argument so users can opt extra names back in, globally (Sequence[str])
    and/or per remote tool name (Mapping with reserved "*" global key). The
    existing denylist is kept as a safety net for framework-named params a
    server declares in its schema; explicitly opted-in extras always win. The
    reserved _meta handling is unchanged.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address MCP allowlist review comments and fix reload arg loss
    
    - Fix pyright reportUnknownArgumentType in _load_tools (cast schema properties).
    - Register declared param names before the existing-tool skip guard so that
      tool-list reloads preserve the allowlist for already-loaded tools (previously
      unchanged tools silently dropped all declared args after a background reload).
    - Handle bare-string values in an additional_tool_argument_names mapping instead
      of iterating their characters.
    - Clarify the framework denylist comment: explicit extras override the denylist.
    - Make the extras-override-denylist test unambiguous (opt in a denylisted name).
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Fix per-service-call history persistence with server-storing clients (#6310)
    * Fix per-service-call history persistence with server-storing clients
    
    When an Agent set require_per_service_call_history_persistence=True together
    with a HistoryProvider, and the chat client stored history server-side by
    default (e.g. OpenAIChatClient, STORES_BY_DEFAULT=True), the external history
    provider was silently never persisted.
    
    Unify persistence on the per-service-call middleware: when the flag is set and
    a HistoryProvider exists, the middleware is always installed and owns
    persistence. service_stores_history now only selects middleware behavior:
    - service does not store: load providers and drive the function loop with a
      local sentinel conversation id, or
    - service stores: skip loading (the service owns history) and persist each
      service call while the real conversation id flows through.
    
    Also rationalize chat-options handling in _prepare_run_context:
    - _merge_options now skips None overrides and strips remaining None values, so
      an unset `store` is never forwarded and the service decides its own default.
    - Resolve `store` and `conversation_id` once from a single combined view
      (effective_options) instead of probing both default and runtime dicts; the
      auto-injection and per-service-call resolution now agree on conversation_id.
    
    Fixes #5798
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Correct as_agent() docstring: persistence is per service call, not once per run
    
    Address PR review: when the client stores history server-side, the
    per-service-call middleware still persists after each model call; only
    provider loading is skipped. The previous "persist once per run()" wording
    contradicted the implementation.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address PR review: docs, missing-conversation-id warning, and tests
    
    - Clarify that require_per_service_call_history_persistence is a no-op when no
      HistoryProvider is present (docstrings in _agents.py and _clients.py).
    - Warn on every service call when the client stores history server-side but
      returns no conversation_id, so the (uncommon) loss of cross-turn resumability
      cannot fail silently.
    - Add tests: storing client + existing conversation_id does not raise and the id
      propagates; two runs on the same session keep persisting with a stable
      service_session_id and no provider loading; storing-without-conversation-id
      warns per call.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: feat(python): Add MCP client OTel spans per GenAI semantic conventions (#6349)
    * feat(python): Add MCP client OTel spans per GenAI semantic conventions
    
    Implement MCP client spans per the OTel GenAI Semantic Conventions for MCP
    (https://opentelemetry.io/docs/specs/semconv/gen-ai/mcp/#client).
    
    Operations instrumented:
    - initialize: CLIENT span capturing MCP session setup
    - tools/list: CLIENT span for tool listing (per-page)
    - prompts/list: CLIENT span for prompt listing (per-page)
    - tools/call: CLIENT span (nested under execute_tool when called via FunctionTool)
    - prompts/get: CLIENT span
    
    Span attributes follow the MCP semantic conventions:
    - Required: mcp.method.name
    - Conditional: error.type, gen_ai.tool.name, gen_ai.prompt.name
    - Recommended: gen_ai.operation.name, mcp.protocol.version, mcp.session.id,
      network.transport, server.address, server.port
    
    Transport-specific attributes per subclass:
    - MCPStdioTool: network.transport=pipe
    - MCPStreamableHTTPTool: network.transport=tcp, network.protocol.name=http
    - MCPWebsocketTool: network.transport=tcp, network.protocol.name=websocket
    
    All span creation gated behind OBSERVABILITY_SETTINGS.ENABLED.
    
    Closes #3624
    Closes #4697
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * refactor: simplify MCP spans — remove enrichment logic and protocol version caching
    
    - Always create nested CLIENT spans for tools/call instead of enriching
      the parent execute_tool span
    - Remove _ACTIVE_TOOL_EXECUTION_SPAN contextvar (no longer needed)
    - Remove enrich_span_with_mcp_attributes() helper
    - Remove _otel_error_type preservation in FunctionTool.invoke()
    - Remove _mcp_protocol_version instance variable; protocol version is
      only set on the initialize span where it is available
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Refine copilot solution
    
    * fix: enable automatic exception recording on MCP spans
    
    Remove record_exception=False and set_status_on_exception=False from
    create_mcp_client_span. Let OTel handle exception recording and status
    setting automatically. The manual set_mcp_span_error calls for tools/call
    still correctly set error.type (which OTel's automatic handling doesn't
    touch), so tool_error is preserved.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Reduce number of lines
    
    * Add comment to sample
    
    * test: address PR review comments on MCP observability tests
    
    - Fix initialize test to call mocked session.initialize() and read
      protocolVersion from the result instead of hardcoding it
    - Add tools/call McpError error-path test
    - Add prompts/get McpError error-path test
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fix export error
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Refactor workflow as agent pending request handling (#6259)
    * WIP: Refactor Workflow as agent pending request handling
    
    * WIP: debugging empty message bug
    
    * Working: Workflow as agent with function approval
    
    * Address Copilot comments
    
    * Fix mypy
    
    * Address comments and fix pipeline
    
    * Request info non function approval now becomes function call
    
    * Revert uv.lock
    
    * Fix mypy
    
    * Bump min version of azure-ai-project
    
    * Remove RequestInfoFunctionArgs
    
    * fix tests
    
    * Fix failing tests
    
    * Fix sample
  • Python: MCP long-running task support in Python (#6319)
    * MCP long-running task support in Python
    
    * Fix pyupgrade and AGENTS.md reconnect description
    
    - pyupgrade: drop forward-reference string annotations in _mcp.py (Python 3.10+ resolves them natively now that MCPTaskOptions is defined before use).
    
    - AGENTS.md: align reconnect description with current behavior. Phase 1 (initial tools/call) does NOT retry on connection loss; raises 'connection lost; task state unknown' instead, so a server that accepted the request but lost the response cannot start the operation twice. Phase 2 (tasks/get / tasks/result) still reconnects once against the same task_id.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fix bandit nosec marker for CI pipeline
    
    * Address PR feedbacks
    
    * Clarifiied comments and addressed more PR feedbacks.
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: bump package versions for 1.8.0 release (#6351)
    - Released cohort (core, openai, foundry, root): 1.7.0 -> 1.8.0
    - agent-framework-github-copilot: promote to RC (1.0.0rc1)
    - agent-framework-orchestrations: rc2 -> rc3 (bug fix)
    - Beta/alpha packages with changes: a2a, anthropic, azurefunctions, bedrock,
      foundry-hosting, mistral bumped to new date stamp (260604)
    - Inter-package dependency bounds updated for changed packages
    - CHANGELOG.md and PACKAGE_STATUS.md updated
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Fix compaction message-id collisions and tool-loop summary persistence (#6299)
    * Fix compaction message-id collisions and tool-loop summary persistence
    
    Fixes two bugs in the compaction strategies:
    
    - #5237: incremental group annotation assigned message ids by position
      within the re-annotated slice, so moving the re-annotation start back to
      a previous group start restarted ids at 0 and produced collisions
      (e.g. a user message reusing an assistant message's id), merging groups
      and causing tool-result compaction to wrongly exclude messages.
      group_messages/_ensure_message_ids now take an id_offset and guard
      against existing-id collisions; annotate_message_groups threads the
      slice start index through as the offset.
    
    - #4991: the function-invocation loop copied the message list each
      iteration, so summaries inserted by compaction landed in a throwaway
      copy and were lost across tool-loop iterations (only the persistent
      excluded flags survived). _prepare_messages_for_model_call now compacts
      the list in place when messages is a list, so inserted summaries persist.
    
    Adds regression tests (incremental id uniqueness, existing-id collision
    avoidance, idempotency, and tool-loop summary persistence including
    streaming and conversation-id modes).
    
    Also adds a summarization.py sample demonstrating SummarizationStrategy
    directly with a real client, and reworks advanced.py with tool-call
    groups and a real summarizer.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Guard incremental message-id assignment against prefix-id collisions
    
    Addresses PR review on #5237: _ensure_message_ids only guarded against
    collisions within the re-annotated slice. A preexisting (e.g. user-supplied)
    id in the preserved prefix could still be reassigned in the suffix when the
    id was numerically out of position, merging groups across the re-annotation
    boundary again.
    
    group_messages/_ensure_message_ids now accept reserved_ids, and
    annotate_message_groups passes the preserved prefix's ids so auto-assigned
    suffix ids never collide across the full list. Adds a regression test
    reproducing the out-of-position prefix-id collision.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: run sync tools off the event loop (#5773)
    * fix: run sync tools off event loop
    
    * chore: silence harness tool marker type check
  • Python: Add MCP-based skills discovery (McpSkillsSource) (#6169)
    * Add MCP-based skills discovery (McpSkill, McpSkillsSource, McpSkillResource)
    
    Implement Agent Skills discovery over MCP following the SEP-2640 convention:
    - McpSkillsSource: reads skill://index.json to discover skills served by an MCP server
    - McpSkill: lazily fetches SKILL.md content via resources/read on demand
    - McpSkillResource: wraps MCP resource results (text and binary)
    - Path traversal protection in get_resource for defense in depth
    - Samples for Foundry Toolbox and standalone MCP skills server
    - Comprehensive unit tests (514 lines)
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address PR review comments: rename to MCP* convention, fix error handling and samples
    
    - Rename McpSkill/McpSkillResource/McpSkillsSource to MCPSkill/MCPSkillResource/MCPSkillsSource
    - Add data-URI prefix stripping for blob resource decoding
    - Let non-McpError exceptions propagate from get_resource()
    - Fix contradictory test comment
    - Use interactive input() in mcp_based_skill sample
    - Remove misleading sample output block
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Restore debug logging for McpError in get_resource()
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Use AzureCliCredential in Foundry toolbox skills sample for consistency
    
    Replace DefaultAzureCredential with AzureCliCredential to match the
    credential convention used in all other samples.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Use MCPStreamableHTTPTool in MCP skills sample
    
    Replace raw mcp library imports (ClientSession, streamable_http_client)
    with the framework's MCPStreamableHTTPTool to keep MCP server connections
    consistent regardless of whether skills are enabled.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Branch on McpError.error.code so only not-found errors return empty
    
    Previously _try_read_index() and get_resource() swallowed every McpError
    as 'no skills available', making auth failures, server crashes, and
    connection drops indistinguishable from a server that simply has no
    skills.
    
    Now only two codes are treated as not-found:
    - -32002 (MCP-spec Resource not found)
    - -32601 (METHOD_NOT_FOUND — server lacks resources/read)
    
    All other McpError codes and non-McpError exceptions propagate with a
    warning log, surfacing real failures visibly.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Add tests for non-McpError and non-not-found error propagation in MCP skills
    
    Cover the re-raise branch in MCPSkill.get_resource for plain
    ConnectionError/TimeoutError, the generic McpError (code 0) propagation
    on get_resource, and TimeoutError propagation in _try_read_index.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Revert "Use MCPStreamableHTTPTool in MCP skills sample"
    
    This reverts commit f31ed0ded9.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Introduce MCP_SKILLS experimental feature for MCP skill classes
    
    Add a separate MCP_SKILLS feature ID to ExperimentalFeature enum and
    use it for MCPSkillResource, MCPSkill, and MCPSkillsSource, since their
    promotion timeline is partly outside of our control.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: progressive tool exposure via FunctionInvocationContext (#6233)
    * Python: progressive tool exposure via FunctionInvocationContext
    
    Add first-class progressive tool exposure to the Python core function-calling
    loop. Tools can now add or remove real FunctionTool schemas at runtime via the
    injected FunctionInvocationContext, taking effect on the next iteration of the
    loop.
    
    - FunctionInvocationContext gains a live `tools` list plus experimental
      `add_tools()` / `remove_tools()` helpers (feature: PROGRESSIVE_TOOLS).
    - The function-calling loop establishes a run-local, normalized tools list and
      threads it into the context at both invocation paths so mutations propagate.
    - Add a sample (dynamic_tool_exposure.py) and a tools samples README, including
      a note that CodeAct providers (Monty/Hyperlight) use their own provider-level
      tool management instead.
    
    Supersedes #3877.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Validate non-negative input in dynamic_tool_exposure sample tools
    
    Address review feedback: factorial and fibonacci now return an error
    message for negative n instead of producing incorrect results.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Make add_tools atomic and surface swallowed function errors
    
    Address review feedback on progressive tool exposure:
    
    - add_tools now validates the full batch against a throwaway copy before
      committing, so a duplicate-name clash partway through a sequence leaves
      the live tool list unchanged (all-or-nothing).
    - _auto_invoke_function now logs a warning (with traceback) when a tool
      raises, so contract errors such as a duplicate-name ValueError from
      add_tools are debuggable without enabling include_detailed_errors.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Avoid retaining tracebacks when logging swallowed function errors
    
    Logging with exc_info=exc fed the exception traceback to the logging
    machinery, whose frame references created reference cycles collected
    lazily by the cyclic GC. On Windows that could drop a hyperlight
    WasmSandbox on a non-owning thread ("unsendable, dropped on another
    thread"), crashing the xdist worker. Log a pre-formatted message with
    the exception repr instead, so no traceback object is retained.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * added missing decorator
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Promote agent-framework-declarative package to RC (#6256)
    * Promote agent-framework-declarative package to RC
    
    * Update missed package status file.
  • Python: Fix OTLP HTTP base-endpoint losing /v1/{signal} auto-append (#5913)
    * Python: Fix OTLP HTTP base-endpoint losing /v1/{signal} auto-append
    
    Per the OTel spec, OTEL_EXPORTER_OTLP_ENDPOINT is a *base* URL for HTTP —
    the SDK auto-appends /v1/traces, /v1/metrics, /v1/logs when it reads the
    env var directly. Signal-specific endpoint env vars are *full* URLs used
    verbatim.
    
    _get_exporters_from_env read the base endpoint and forwarded it as the
    constructor ``endpoint=`` argument, which the SDK always treats as a full
    signal URL. As a result, with OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
    and HTTP protocol, the exporter sent to http://localhost:4318 instead of
    http://localhost:4318/v1/traces (and likewise for metrics/logs).
    
    Replicate the spec's auto-append here when falling back to the base
    endpoint under HTTP. gRPC behavior is unchanged.
    
    * Python: Fix mypy type errors in OTLP endpoint assignment
    
    Pre-declare traces_endpoint, metrics_endpoint, logs_endpoint as
    str | None before the if/else block. Mypy inferred str from the
    if-branch f-string assignments and then rejected the str | None
    expressions in the else-branch as incompatible.
  • Python: feat(evals): Foundry Adaptive Evals integration (rubric-generation) (#6101)
    * Python: feat(evals): RubricScore type + EvalScoreResult.dimensions
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: feat(foundry-evals): RubricDimension + GeneratedEvaluatorRef + accept in evaluators=
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: feat(evals): parse rubric_scores from output items + assertion helpers
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: feat(evals): BaseAgent.as_eval_source / Workflow.as_eval_source
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: feat(foundry-evals): EvalGenerationSource + generate_rubric helper
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: feat(foundry-evals): YAML config loader + sample
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: fix(evals): address PR review feedback
    
    Addresses 4 Copilot review comments on PR #6101:
    
    1. assert_dimension_score_at_least: drop the (not evaluator or found_any) guard so require_applicable=True correctly raises when the named evaluator produces no entries for the dimension. Adds TestRubricAssertions covering the regression.
    
    2. GeneratedEvaluatorRef docstring: reword to describe actual behaviour (pinning recommended, not required) so it matches the dataclass default and FoundryEvals warning path.
    
    3. _poll_generation_job: switch from asyncio.get_event_loop() to get_running_loop() and bound the per-iteration sleep by remaining time, matching _poll_eval_run.
    
    4. generate_rubric: type category as Literal['quality','safety'] and validate at the entry point with a ValueError; drop the silent 'invalid -> quality' rewrite in _generation_job_to_ref. Adds a regression test.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: feat(foundry-evals): hosted-agent-aware rubric generation
    
    * Auto-detect hosted Foundry agents in agent_as_eval_source: when the
      agent's chat_client exposes a string agent_name (the convention used
      by RawFoundryAgentChatClient for PromptAgents/HostedAgents), emit a
      type='agent' EvalGenerationSource so the service fetches instructions
      and tools from the agent registry instead of relying on the local
      wrapper (which holds neither for hosted agents).
    * Add hosted_agent_version kwarg and a new agent_version field on
      EvalGenerationSource so PromptAgent runs can pin to a specific hosted
      version for reproducible rubric generation.
    * Add force_prompt_source escape hatch to bypass auto-detection and
      always emit a rendered prompt dossier - useful when the local wrapper
      carries overrides the service-side agent doesnt see.
    * Fix _to_sdk_source for dataset sources: SDK ctor takes name=/version=,
      not dataset_name=/dataset_version=. The mismatch would raise TypeError
      against the real azure-ai-projects 2.3.0a* SDK; only unmocked
      integration paths were affected.
    
    Tests cover: auto-detection happy path, versionless hosted agent,
    explicit hosted_agent_version forwarding, force_prompt_source override,
    non-string chat_client attrs (MagicMock test doubles) not mis-detected,
    agent_version forwarded through _to_sdk_source, and the corrected
    dataset SDK kwarg names.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix(foundry-evals): accept canonical dimension_scores key per docs
    
    The published Foundry rubric-evaluator output (Microsoft Learn 'Rubric evaluators' reference) places per-dimension breakdowns under properties.dimension_scores, not properties.rubric_scores. The parser now tries dimension_scores first and falls back to rubric_scores for preview-build compatibility, and tolerates non-list payloads (e.g. MagicMock auto-attrs) by trying the next candidate when parsing yields zero entries.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * feat(foundry-evals): add manual create_rubric_evaluator
    
    Adds FoundryEvals.create_rubric_evaluator as the agent-framework surface over project_client.beta.evaluators.create_version. This is the manual counterpart to generate_rubric: callers supply RubricDimension instances (authored locally, ported from another framework, or hand-tuned) and we POST a RubricBasedEvaluatorDefinition. The service auto-attaches the non-editable residual dimension (general_quality for quality, general_policy_compliance for safety).
    
    Per the Microsoft Learn 'Rubric evaluators' reference, the auto-generation path (create_generation_job) is primarily a portal/UI feature; external SDK clients with rich local agent context are better served by manual create_version. This keeps generate_rubric for users who want to round-trip through a Foundry-registered agent.
    
    Validation up front: weight must be in [1,10], ids unique, descriptions non-empty, pass_threshold in [0,1]. The returned GeneratedEvaluatorRef is identical in shape to one obtained from generate_rubric, so downstream evaluators= lists work unchanged.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * samples(foundry-evals): manual rubric sample + namespace re-exports
    
    Adds evaluate_with_manual_rubric_sample.py demonstrating the end-to-end dev scenario for FoundryEvals.create_rubric_evaluator: hand-author a list of RubricDimension, register via create_rubric_evaluator, then use the pinned GeneratedEvaluatorRef alongside built-in evaluators in an agent regression run.
    
    Also re-exports RubricDimension, GeneratedEvaluatorRef, build_sources, and load_evals_config from agent_framework.foundry (both the lazy runtime shim and the type stub) so the rubric samples can import everything from a single namespace; the auto-generate sample was previously broken because the shim was missing build_sources / load_evals_config.
    
    Updates the foundry-evals README with a chooser entry for the two rubric paths.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * feat(foundry-evals): remove rubric creation flows; keep consumption only
    
    Reframes agent-framework as a pure consumer of Foundry rubric evaluators: scoring against rubrics that already exist (authored in the Foundry portal or via the dedicated SDK / REST surface) instead of creating them from the SDK.
    
    Removed creation surface area:
    
    - FoundryEvals.generate_rubric (auto-generate path) and create_rubric_evaluator (manual path), plus all _GenerationSdkTypes / _ManualRubricSdkTypes / _to_sdk_dimensions / _coalesce_generation_sources / _to_sdk_source / _poll_generation_job / _generation_job_to_ref / _evaluator_version_to_ref / _get_beta_evaluators / _import_*_sdk_types helpers.
    
    - EvalGenerationSource (the input source discriminator), RubricDimension (the input dimension type), agent_as_eval_source / workflow_as_eval_source / _detect_hosted_foundry_agent helpers, and the YAML-config loader (_evals_config.py with RubricGenerationSpec / RubricSourceSpec / parse_evals_config / load_evals_config / build_sources).
    
    - BaseAgent.as_eval_source / Workflow.as_eval_source plus the _render_agent_dossier / _render_workflow_dossier helpers in core. These existed only to feed the now-removed generation pipeline.
    
    - Samples evaluate_with_generated_rubric_sample.py, evaluate_with_manual_rubric_sample.py, and evaluators.yaml. Replaced with a short README section showing how to reference an existing rubric evaluator via GeneratedEvaluatorRef.
    
    Kept (consumption surface):
    
    - GeneratedEvaluatorRef, slimmed to (name, version, display_name). Still accepted alongside built-in evaluator strings in FoundryEvals(evaluators=[...]). Versionless refs still warn.
    
    - RubricScore on EvalScoreResult.dimensions plus EvalResults.assert_dimension_score_at_least for per-dimension CI gates.
    
    - _parse_dimension_entries / _extract_rubric_scores output parsing (both canonical dimension_scores and the legacy rubric_scores key).
    
    Tests: 160/160 foundry unit tests and 71/71 core local-eval tests pass; pyright is clean across changed files. The pre-existing tests/core/test_telemetry.py::test_detect_hosted_fallback_import_error failure is unrelated and reproduces on the prior commit.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * samples(foundry-evals): add evaluate_with_rubric_sample
    
    Adds a runnable end-to-end sample showing how to consume a pre-existing rubric evaluator created in Foundry: reference it with GeneratedEvaluatorRef(name, version), mix it with built-in evaluators in FoundryEvals, and gate CI with assert_dimension_score_at_least on a specific dimension.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix(foundry-evals): satisfy mypy on _fetch_output_items
    
    mypy infers OutputItemListResponse.sample as dict[str, object] | None while pyright correctly infers the typed Sample model. Cast to Any so both type checkers accept the attribute access pattern, rename the local to avoid shadowing the inner-loop sample binding, and drop the now-stale pyright suppressions.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * docs(foundry-evals): drop unpublished rubric-evaluators learn.microsoft.com link
    
    The Adaptive Evals authoring docs are not yet published on Microsoft Learn, so the link 404s. Keep the descriptive text without the broken hyperlink; we can re-add it once the docs ship.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * test(foundry-evals): hoist repeated local imports to module top
    
    Per code review feedback (eavanvalkenburg): the test file repeated 'from agent_framework_foundry._foundry_evals import ...' inside 22 test bodies and 'from agent_framework_foundry import GeneratedEvaluatorRef' inside 8 more. Move all of them to the existing top-level imports; the symbols are the same across tests and the local imports were redundant.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Ben Thomas <25218250+alliscode@users.noreply.github.com>
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Fix core observability unsafe serialization of function-call arguments containing dataclass/framework objects (#6026)
    * fix: safely serialize function-call arguments in core observability
    
    Apply make_json_safe() to content.arguments in _to_otel_part() before
    building the otel message dict, so that dataclass/framework payloads
    (e.g. workflow request_info events) do not cause a TypeError when
    _capture_messages() calls json.dumps().
    
    Lift make_json_safe() into agent_framework._serialization (no new
    external deps — dataclasses/datetime only) so the core observability
    path can use it without a dependency on the ag-ui adapter.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix(core): safely serialize workflow request_info payloads in observability (#5733)
    
    - Add make_json_safe() helper to recursively convert non-serializable objects
    - Use make_json_safe() in _to_otel_part() for function_call arguments
    - Fix CustomPayload test class to use @dataclass (resolves B903 lint error)
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix(serialization): guard callability and normalize dict keys in make_json_safe (#5733)
    
    - Use callable(getattr(obj, method, None)) instead of hasattr() so that
      non-callable attributes named model_dump/to_dict/dict do not raise
      TypeError at runtime.
    - Wrap each call in try/except TypeError to handle callables with
      mandatory arguments gracefully.
    - Convert dict keys to str() so that non-string keys (e.g. datetime,
      int) cannot cause json.dumps to raise TypeError.
    - Add regression tests for both scenarios.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address observability serialization review feedback
    
    ---------
    
    Co-authored-by: Copilot <copilot@github.com>
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: refresh dev dependencies and validate runtime bounds (#6238)
    Updates third-party dev dependencies across the Python workspace and
    validates that all runtime dependency bounds still hold at both ends.
    
    Dev dependency bumps (root, lab, declarative, durabletask):
    - uv 0.11.6 -> 0.11.17, ruff 0.15.8 -> 0.15.15,
      pytest-asyncio 1.3.0 -> 1.4.0, mcp 1.27.0 -> 1.27.2,
      azure-monitor-opentelemetry 1.8.7 -> 1.8.8,
      poethepoet 0.42.1 -> 0.46.0, prek 0.3.9 -> 0.4.3,
      types-python-dateutil and types-PyYaml stub bumps.
    - Transitive Dependabot items swept via lock: idna 3.11 -> 3.17,
      pip 26.0.1 -> 26.1.2.
    
    Deliberately excluded:
    - opentelemetry-sdk stays 1.40.0: azure-monitor-opentelemetry (incl.
      1.8.8) hard-pins opentelemetry-sdk==1.40.
    - mypy stays 1.20.0 and pyright stays 1.1.408: the 2.1.0 / 1.1.409
      bumps introduce new diagnostics that fail type checking and need
      dedicated PRs.
    - rich kept as a range: agentlightning (lab[lightning]) forces
      rich==13.9.4.
    
    Code/formatting changes driven by the ruff upgrade:
    - devui lifespan now uses try/finally so shutdown cleanup always runs
      (ruff RUF075).
    - Removed unused TYPE_CHECKING imports in core and foundry flagged by
      ruff 0.15.15.
    - Reapplied ruff 0.15.15 formatting to the files it changed.
    
    Validation: validate-dependency-bounds-test "*" passes (31/31 lower +
    31/31 upper); typing 62/62; lint 31/31; devui tests pass.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Add background agent support to harness agent (#6155)
    * Add background agent support to harness agent
    
    * Address PR comments
  • Python: coalesce code interpreter history chunks (#5801)
    * fix: coalesce code interpreter history chunks
    
    * fix: narrow content item list types
    
    * fix: remove redundant content list casts
  • Python: consolidate MCP reliability fixes (#6145)
    * Python: consolidate MCP reliability fixes
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fix MCP cleanup and metadata typing
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Satisfy MCP metadata mypy typing
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fix Pyright metadata mapping type
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Adding AgentFileStore and FileAccessProvider to support file access operations. (#6099)
    * Adding AgentFileStore and FileAccessProvider to support file ased operations for agents.
    
    * Address PR review feedback on FileAccessProvider
    
    - Probe symlinks on the unresolved candidate path so in-root symlinks
      cannot silently pass and out-of-root symlinks surface the correct
      error message.
    - Validate matching_lines elements in FileSearchResult.from_dict and
      raise a clean ValueError for non-mapping entries.
    - Cap search regex pattern length (256 chars) via a new
      _compile_search_regex helper to mitigate ReDoS, and surface the cap
      in the file_access_search_files tool description.
    - Skip non-UTF-8 files during filesystem search instead of aborting
      the entire directory walk.
    - Replace the module-scope trailing string in the data-processing
      sample with comments to avoid Ruff B018.
    - Remove the checked-in working/region_totals.md sample artifact so
      the save flow works from a clean checkout.
    - Expand the Windows stdout reconfiguration comment in task_runner.py
      for clarity.
    - Add tests for invalid/oversize regex, non-UTF-8 file search, and
      in-root symlink rejection.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fix mypy redundant-cast in FileSearchResult.from_dict
    
    Use cast(list[object], ...) instead of cast(list[Any], ...) so the
    cast represents a real type change (lists are invariant) and is no
    longer flagged by mypy as redundant, while still satisfying pyright's
    reportUnknownVariableType. Matches the existing pattern in _memory.py.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Tighten path normalization and directory resolution in FileAccess
    
    - _normalize_relative_path now strips surrounding whitespace up front
      so leading/trailing spaces never leak into file segments, and
      rejects trailing path separators for file paths so 'foo/' is no
      longer silently coerced to 'foo'.
    - FileSystemAgentFileStore._resolve_safe_directory_path normalizes
      with is_directory=True and maps an empty normalized result to the
      root. This matches InMemoryAgentFileStore so whitespace-only
      directory inputs resolve to the root instead of raising.
    - Added tests for whitespace stripping, trailing-separator rejection,
      and whitespace-only directory listing on the filesystem store.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Harden FileAccess search and atomic save in store API
    
    - Add wall-clock timeout (10s) around regex scans so a pathological pattern (e.g. `(a+)+`) below the length cap cannot stall the event loop.
    - Offload the InMemoryAgentFileStore regex scan to a worker thread, matching the filesystem store.
    - Fail closed when `Path.is_symlink` raises during the safe-path probe so a permission error cannot silently bypass the symlink/reparse-point rejection.
    - Add `overwrite: bool = True` to `AgentFileStore.write_file`; the in-memory store performs the check under the existing lock and the filesystem store uses `open(mode='x')` so concurrent callers cannot race past `overwrite=False`.
    - `file_access_save_file` now relies on the atomic store call instead of a separate `file_exists` round-trip.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fix Python 3.10 timeout handling and add directory arg to list/search tools
    
    - Catch asyncio.TimeoutError in _run_search_with_timeout. In Python 3.10
      asyncio.wait_for raises asyncio.exceptions.TimeoutError, which is
      distinct from the builtin TimeoutError (the two were unified in 3.11).
      Catching the asyncio alias works on every supported version.
    - Add an optional directory parameter to file_access_list_files and
      file_access_search_files so agents can enumerate / scope searches to
      nested folders, not just the store root.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address FileAccess review feedback: case, errors, signal, TOCTOU
    
    - InMemoryAgentFileStore now stores (display_name, content) so list_files
      and search_files return the original-case names callers wrote, matching
      the behaviour of FileSystemAgentFileStore on case-preserving filesystems
      and removing the silent in-memory vs. on-disk contract divergence.
    - FileSystemAgentFileStore.read_file raises ValueError instead of letting
      UnicodeDecodeError bubble for binary / non-UTF-8 input, restoring
      symmetry with search_files (which still skips) and giving the tool
      layer a recoverable type to translate.
    - Tool wrappers now catch ValueError and OSError around every operation
      and surface them as readable strings, so 'you used ..' and 'the file
      already exists' are both reported to the model the same way instead of
      the former crashing out as an unhandled exception.
    - _search_files_sync logs per skipped non-UTF-8 file at WARNING and an
      aggregate INFO summary so operators can distinguish 'no matches' from
      'half the corpus was unreadable'.
    - FileSystemAgentFileStore softens its docstrings to acknowledge the
      inherent probe-then-open TOCTOU window. On POSIX both read and write
      now pass O_NOFOLLOW so the kernel refuses if the leaf segment becomes
      a symlink between the probe and the open. Windows has no equivalent
      flag; the limitation is documented.
    - Tests cover: case preservation on list/search, ValueError on non-UTF-8
      read at the store and tool layer, tool-layer string responses for
      path-traversal and oversized-regex inputs, search-skip log output,
      symlink rejection on delete/search/list, and symlinked intermediate
      directory rejection.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address FileAccess nit comments: docstrings, enumerate, opt-in delete approval
    
    - Expand FileSearchMatch/FileSearchResult.to_dict docstrings to explain why
      the override is needed (__slots__ defeats the mixin's __dict__ iteration)
      and why exclude/exclude_none are accepted-but-ignored (mixin signature
      compatibility for callers like to_json).
    - Use enumerate(lines, start=1) in _search_file_content so the +1 below is
      no longer needed; rename loop variable to line_number for clarity.
    - Add opt-in require_delete_approval: bool = False on FileAccessProvider.
      When True, file_access_delete_file is registered with approval_mode
      'always_require' so the host must approve every delete. Default False
      preserves current behaviour and matches the .NET reference, but
      deployments that want a safer-by-default posture can enable it.
    - Add tests covering both delete approval modes.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * FileAccess: require delete approval by default
    
    Flip the default for FileAccessProvider(require_delete_approval=...) from
    False to True so destructive deletes are gated by host approval out of the
    box. Callers that want the previous autonomous behaviour (which matches the
    .NET reference) can pass require_delete_approval=False.
    
    Tests updated accordingly.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fixing linkinspector by installing Chrome for puppeteer first.
    
    ---------
    
    Co-authored-by: Ben Thomas <25218250+alliscode@users.noreply.github.com>
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: [Breaking] Refactor Skill API to async resource and script lookup (#6135)
    Port of .NET commit 08541ee5a9.
    
    Replace property-based Skill.content/resources/scripts with async
    by-name lookup methods:
    - content property -> async get_content() -> str
    - resources property -> async get_resource(name) -> SkillResource | None
    - scripts property -> async get_script(name) -> SkillScript | None
    
    SkillsProvider now always includes all three tools (load_skill,
    read_skill_resource, run_skill_script) and both instruction blocks
    regardless of whether any skills have resources or scripts.
    
    ClassSkill retains resources/scripts properties as overridable hooks
    for subclass reflection-based discovery.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Bump Python package versions for 1.7.0 release (#6142)
    Bumps the released 1.6.0 packages agent-framework, agent-framework-core, agent-framework-foundry, and agent-framework-openai to 1.7.0, with root continuing to exactly pin agent-framework-core[all]. Bumps the changed prerelease packages agent-framework-a2a, agent-framework-chatkit, agent-framework-declarative, agent-framework-devui, and agent-framework-foundry-hosting to the 260528 date stamp, raises core floors on the packages included in this release, raises Foundry's OpenAI floor alongside OpenAI, and raises ChatKit's openai-chatkit floor to the minimum version required by the current typed API usage. No beta cohort bump was applied; the absent mistal/mistral package was intentionally not bumped because no such package exists in this branch.
  • Python: Align c# and python TodoProvider tool names (#6107)
    * Align c# and python TodoProvider tool names
    
    * Potential fix for pull request finding
    
    Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
    
    * Address PR review: remove __slots__ and add typed schemas for tool params
    
    - Remove __slots__ from TodoItem, TodoInput, and TodoCompleteInput classes
      (not needed for low-instance-count objects and hinders dev scenarios)
    - Add _TodoAddItemSchema and _TodoCompleteItemSchema TypedDicts to provide
      proper JSON schema for todos_add and todos_complete tool parameters
    - Use typing_extensions for Python 3.10 compatibility
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • feat(a2a): add A2AAgentSession with reference_task_ids and input-required support (#5980)
    * feat(a2a): link follow-up messages via reference_task_ids
    
    Track the task_id from A2A responses (task, status_update, artifact_update,
    and message payloads) on session.state and include it as reference_task_ids
    on subsequent outgoing messages. This enables remote agents to correlate
    follow-up messages as task refinements per the A2A spec.
    
    Resolves #5938
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * feat(a2a): add A2AAgentSession for typed protocol state tracking
    
    Introduce A2AAgentSession (subclass of AgentSession) with context_id,
    task_id, and task_state properties. This follows the DurableAgentSession
    pattern and mirrors the .NET A2AAgentSession design.
    
    - Track task_id, context_id, and task_state from all response payload types
    - Validate context_id consistency (raise on mismatch)
    - Auto-assign server-generated context_id when not set
    - Only A2AAgentSession gets reference tracking (no state dict fallback)
    - Plain AgentSession continues to work without reference tracking
    - Add serialization support (to_dict/from_dict)
    - Export via agent_framework.a2a and agent_framework_a2a
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * style: remove unnecessary string annotation (pyupgrade)
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix: use AgentSession.from_dict for state deserialization
    
    Avoids importing private _deserialize_state, matching the
    DurableAgentSession pattern.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix: track context_id from message payloads in A2AAgentSession
    
    Previously, context_id was only captured from task, status_update, and
    artifact_update payloads. Message-only responses (which carry context_id
    but may lack task_id) were silently lost. This fix:
    
    - Captures msg.context_id in the message handler
    - Persists session state when either last_task_id or last_context_id is
      present (not only when task_id is truthy)
    - Only updates task_id/task_state when a task_id was actually returned
    - Adds a test for message-only context_id tracking
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * addressed comments
    
    * Gate status content to INPUT_REQUIRED/terminal states (match .NET)
    
    Match .NET's GetUserInputRequests pattern: only emit TaskStatusUpdateEvent
    message content when state is INPUT_REQUIRED or terminal. Intermediate
    status text (WORKING, SUBMITTED) is no longer surfaced to callers.
    
    When state is INPUT_REQUIRED, set additional_properties['input_required']
    = True so callers can distinguish input requests from final responses.
    
    Closes #5937
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address review: remove message task_id tracking, defensive fallbacks, and input_required flag
    
    - Do not track task_id from Message payloads (simple interactions
      without task tracking)
    - Remove 'or last_task_id' fallback from status_update and
      artifact_update handlers (spec guarantees task_id is always set)
    - Remove additional_properties['input_required'] flag (content gating
      to INPUT_REQUIRED/terminal states is the signal itself)
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Add a HarnessAgent with available features and sample (#6041)
    * Add a HarnessAgent with available features and sample
    
    * Fix formatting
    
    * Address PR comments and fix mypy error
    
    * Add web search support to HarnessAgent
    
    * Fix build warning
    
    * Apply suggestions from code review
    
    Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
    
    * Address PR comments
    
    * Address PR comments
    
    * Address further PR comments.
    
    * Fix markdown broken link
    
    ---------
    
    Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
  • Python: feat(foundry): add to_prompt_agent / deploy_as_prompt_agent (experimental) (#5959)
    * feat(foundry): add experimental to_prompt_agent converter
    
    Adds `to_prompt_agent(agent)`, an experimental converter
    (`ExperimentalFeature.TO_PROMPT_AGENT`) that turns an Agent Framework
    `Agent` into a Foundry `PromptAgentDefinition` ready to publish via
    `AIProjectClient.agents.create_version(...)`.
    
    Behaviour:
    
    * `agent.client` must be a `FoundryChatClient` (or subclass); otherwise
      `TypeError` is raised. The model deployment name is lifted from the
      bound client so the same Agent definition used for local runs can be
      published as a hosted prompt agent without restating the model.
    * Foundry SDK tool instances (from `FoundryChatClient.get_*_tool()`) are
      passed through unchanged. AF `FunctionTool`s (and `@tool`-decorated
      callables) are emitted as Foundry `FunctionTool` declarations.
    * Local AF MCP tools cannot be expressed in a `PromptAgentDefinition`;
      the converter raises `ValueError` and points at
      `FoundryChatClient.get_mcp_tool()` for hosted MCP servers.
    * The converter walks both `agent.default_options["tools"]` and
      `agent.mcp_tools` because `normalize_tools()` splits local MCP off
      into its own list.
    
    Re-exported through the `agent_framework.foundry` lazy-loading namespace
    (updates both `__init__.py` and the `__init__.pyi` type stub).
    
    Adds a portable-agent sample showing the same `Agent` driven through
    both `agent.run(...)` and `to_prompt_agent(agent)`, and a README section
    covering the new converter.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * chore(samples): remove snippet tags from portable agent sample
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * chore(samples): inline FoundryChatClient and enable prompt-agent publish
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * chore(samples): drop async credential context manager
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * docs(foundry): trim README to_prompt_agent example to publish-only flow
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * docs(foundry): note FoundryAgent runs @tool callables for deployed prompt agents
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix(foundry): address review comments on to_prompt_agent converter
    
    * Construct `PromptAgentDefinition` `Tool` from a dict via `**tool_item`
      unpacking rather than the positional Mapping constructor \u2014 cleaner and
      matches the typical Pydantic / Azure SDK pattern.
    * Drop the redundant `isinstance(mcp_tool, MCPTool)` guard in
      `_convert_tools`; the parameter is already typed `Iterable[MCPTool]` so
      the second `raise` was unreachable. The remaining single `raise`
      fires for every entry as intended.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix(foundry): match Agent.__init__ model resolution in to_prompt_agent
    
    * Read the model from `agent.default_options.get("model")` first,
      falling back to `agent.client.model`. This mirrors the order
      `Agent.__init__` uses (`_agents.py:740`) when assembling
      default_options, so the model the agent runs with is the same model
      the converter publishes \u2014 e.g. when the caller passes
      `default_options={"model": "..."}` to override the bound client.
    * Updated the missing-model error message to point at both the client
      and the default_options paths.
    * Added tests:
      * tool-only agent with no `instructions` produces a definition
        where `instructions` is `None` and is omitted from the dict
        payload (`Agent.__init__` strips None values from default_options
        before storing them).
      * `default_options['model']` wins over the bound client's model.
      * Fallback to client.model when default_options has no model.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * feat(foundry): add deploy_as_prompt_agent helper + samples
    
    Adds `deploy_as_prompt_agent(agent)`, a convenience wrapper around
    `to_prompt_agent` that reuses the bound FoundryChatClient's project
    client to call `project_client.agents.create_version(...)`. Defaults
    `agent_name` / `description` from `agent.name` / `agent.description`
    so the Agent stays the single source of truth.
    
    * Exposed from `agent_framework_foundry` and the lazy-loading
      `agent_framework.foundry` namespace (including the .pyi stub).
    * Marked experimental with the existing
      `ExperimentalFeature.TO_PROMPT_AGENT` tag.
    * Tests cover the happy path, name/description defaulting, explicit
      override, no-name error, metadata + description forwarding, extra
      kwargs passthrough, and the experimental metadata.
    
    Samples:
    * Renamed the existing sample to `creating_prompt_agents.py`, drops
      'portable' wording, presents `deploy_as_prompt_agent` first as the
      recommended path and `to_prompt_agent` + `AIProjectClient` as the
      two-step alternative, and adds a cleanup step that deletes the
      published agent so re-runs stay idempotent.
    * New `using_prompt_agents.py` shows the end-to-end loop: deploy the
      agent, connect to it with `FoundryAgent` passing the same local
      `@tool` callable, run a query against the deployed prompt agent,
      then clean up.
    
    README updated to introduce `deploy_as_prompt_agent` as the
    recommended path and link to both runnable samples.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix(foundry): restore missing-model ValueError in to_prompt_agent
    
    The check was accidentally dropped while reworking docstrings in the
    previous commit. Test `test_to_prompt_agent_rejects_missing_model`
    exercises this path and was failing on CI as a result.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * refactor(foundry): rename deploy_as_prompt_agent -> create_prompt_agent
    
    Renames the helper across the foundry package, core lazy-loader stubs,
    tests, README and samples. The new name better matches the action
    performed (a prompt-agent definition is created in Foundry) and is
    consistent with the surrounding ''create_*'' API surface.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * refactor(foundry): drop create_prompt_agent, enrich to_prompt_agent params
    
    Remove the create_prompt_agent helper and consolidate on to_prompt_agent.
    Expose every PromptAgentDefinition parameter that has either an Agent
    Framework equivalent (sourced from default_options) or no equivalent
    (accepted as a keyword argument).
    
    * default_options-sourced (with kwarg overrides):
      temperature, top_p, string tool_choice
    * kwarg-only Foundry knobs:
      reasoning, text, structured_inputs, rai_config, ToolChoiceParam tool_choice
    
    Precedence is always: explicit keyword > default_options entry > unset.
    
    Tests cover every path (defaults, default_options, kwargs, kwarg override).
    Samples and README rewritten around the enriched to_prompt_agent.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * refactor(foundry): single source of truth for prompt-agent options
    
    Stop duplicating the generation-parameter surface between FoundryChatOptions
    and to_prompt_agent. Translate every field with an Agent Framework equivalent
    (temperature, top_p, tool_choice, reasoning, response_format/text/verbosity)
    from agent.default_options via a new RawFoundryChatClient helper
    _prepare_prompt_agent_options. Only Foundry-specific fields with no AF
    equivalent — structured_inputs and rai_config — remain as keyword arguments
    on to_prompt_agent.
    
    - tool_choice is dropped when there are no tools (mirrors _prepare_options
      semantics and avoids polluting tool-less prompt agents with Agent.__init__'s
      'auto' default).
    - response_format Pydantic models route through
      openai.lib._parsing._responses.type_to_text_format_param; dict shapes go
      through the existing _prepare_response_and_text_format helper.
    - default_options is not mutated; text dict is defensively copied.
    
    Tests, README, and creating_prompt_agents.py sample updated to reflect the
    new single-source model.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * docs(foundry): consolidate prompt-agent sample
    
    Drop creating_prompt_agents.py (the publish-only variant) and rename
    using_prompt_agents.py to foundry_prompt_agents.py so the single sample
    covers the full convert -> publish -> connect -> run loop. Update the
    README link list accordingly.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * docs(foundry): run local Agent + deployed agent in same sample
    
    Add an agent.run() call against the local Agent before publishing, then run
    the deployed prompt agent on the same query. Expand the docstring with a
    compare-and-contrast covering runtime/latency, configurability, and
    persistence/sharing differences between the two execution paths.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * test(foundry): cover conflicting response_format + text.format in to_prompt_agent
    
    Exercises the ValueError path when a Pydantic response_format would overwrite
    an explicit text.format mapping with a different shape. Lifts _chat_client.py
    coverage from 89% to 90%.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * refactor(foundry): move _prepare_prompt_agent_options into _to_prompt_agent
    
    Lift the translation helper off RawFoundryChatClient and into the
    _to_prompt_agent module as a module-private function that takes the client
    as its first argument. The chat client no longer needs to carry a method
    whose only consumer is the prompt-agent converter, while still serving as
    the source of the request-path helper (_prepare_response_and_text_format)
    that the converter reuses for dict-shaped response_format values.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * docs(python): codify GA terminology + post-run docs review
    
    Add two pieces of guidance to python/AGENTS.md:
    
    * Terminology - reserve 'GA' for hosted services; use 'released' or 'stable'
      for Agent Framework code/features to match the feature-lifecycle stages.
    * Maintaining Documentation - review AGENTS.md and skills at the end of every
      run and update any guidance the conversation made stale; before adding a
      new principle, ask the user to confirm it should be captured.
    
    Also pulls in a docstring fix in foundry_prompt_agents.py that swaps the
    stray 'GA' for 'released', applying the new terminology rule.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * address PR review: strict=True default, Tool._deserialize dispatch, sample cleanup safety
    
    - FunctionTool published as strict=True so the server-side schema validation
      matches what the local FoundryAgent(tools=[same_callable]) dispatcher
      enforces. AF FunctionTool has no 'strict' attribute, so the safer default
      is used uniformly instead of silently downgrading to a permissive contract.
    - _validate_mapping_tool now dispatches through ProjectsTool._deserialize so
      dict-shaped tools rehydrate to the concrete subclass (FunctionTool,
      WebSearchTool, ...) via the 'type' discriminator instead of returning a
      generic Tool. Added a test that asserts isinstance(WebSearchTool) and a
      new test for the function-typed dict path.
    - foundry_prompt_agents.py sample now wraps credential + project client in
      async with and the create_version / run flow in try/finally so a failure
      on connect or run still deletes the published prompt agent rather than
      leaving an orphaned, billable resource in the user's Foundry project.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix(ci): correct linkspector ignorePattern typo (./pulls -> ./pull)
    
    GitHub PR URLs use the singular segment /pull/N (compare to /issues/N
    for issues). The existing './pulls' ignore pattern never matched
    anything as a result, so legitimately stale PR links (e.g. PRs deleted
    from forks) surface as linkspector failures on unrelated PRs.
    
    This is the same convention the './issues' rule above already follows.
    Fixes the markdown-link-check failure on a dangling link in
    dotnet/src/Microsoft.Agents.AI.DurableTask/CHANGELOG.md.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Add a BackgroundAgentsProvider for python (#6069)
    * Add a BackgroundAgentsProvider for python
    
    * Address PR comments and fix linting warnings
    
    * Address PR comment
  • Python: Align ModeProvider tool names and instructions (#6071)
    * Align ModeProvider tool names and instructions
    
    * Address PR comments