mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
f390595188c1e8536af33bd8d1dfd731d6046828
829 Commits
-
Evan Mattson ·
2026-05-20 10:01:44 +09:00 -
Python: feat: add agent-framework-monty (Monty-backed CodeAct provider) (#5915)
* Python: feat: add agent-framework-monty (Monty-backed CodeAct) New alpha package that wraps pydantic-monty (a Rust-based Python interpreter) behind the same CodeAct API surface as agent-framework-hyperlight, so users can swap providers with minimal code change. Public API (agent_framework_monty): - MontyCodeActProvider — ContextProvider that injects a run-scoped execute_code tool plus dynamic CodeAct instructions. - MontyExecuteCodeTool — standalone FunctionTool for mixed-tool agents or manual static wiring. - FileMount / FileMountInput / MountMode — public types mirroring the Hyperlight names, with Monty's mode (read-only/read-write/overlay) and write_bytes_limit on FileMount. Constructor kwargs (both classes) mirror Hyperlight where possible: tools, approval_mode, workspace_root, file_mounts; plus a Monty-only resource_limits forwarding ResourceLimits to Monty.start(). Filesystem flow: - workspace_root auto-mounts at /input (read-write), matching Hyperlight. - file_mounts accepts string shorthand, (host, mount) tuple, or FileMount with mode + write cap. - Files written under read-write mounts are scanned post-execution and returned as Content.from_data items (mirrors Hyperlight /output). - overlay mounts buffer writes in-memory; read-only mounts reject writes. Internals: - _monty_bridge.InlineCodeBridge ports the inline (non-durable) bridge from anthonychu/maf-codeact-monty-python; handles FunctionSnapshot / FutureSnapshot pause/resume, dispatches direct typed calls + the call_tool fallback, forwards mount/limits to Monty.start(...). - generate_type_stubs emits per-tool stubs so Monty's `ty` type-checker rejects bad calls before any host tool runs. Alpha-policy compliance (per python-package-management skill): - Added agent-framework-monty = { workspace = true } to root pyproject.toml. - Added row to python/PACKAGE_STATUS.md. - Added monty entry under Experimental in python/AGENTS.md. - NOT added to core[all]; NO agent_framework.monty lazy shim (deferred to beta promotion). Samples (three sets, import from agent_framework_monty directly): - samples/02-agents/context_providers/code_act/monty_code_act.py (provider pattern) + updated local README. - samples/02-agents/tools/monty_code_interpreter/ (standalone + manual-wiring + README). - samples/04-hosting/foundry-hosted-agents/responses/11_monty_codeact/ (full hosted-agent layout with uv-based pyproject.toml + Dockerfile, Azure Monitor wiring via APPLICATIONINSIGHTS_CONNECTION_STRING + enable_instrumentation, ENABLE_INSTRUMENTATION and ENABLE_SENSITIVE_DATA env vars). The alpha wheel is vendored into ./wheels/ (gitignored) via vendor-wheel.sh; new row added to the parent Responses-API README. Tests: - 28 hermetic unit tests (stubbed pydantic_monty). - 18 integration tests marked @pytest.mark.integration, auto-skipped when pydantic_monty is unimportable; exercise the real Monty runtime: print round-trip, last-expression value, direct typed tool dispatch, call_tool fallback, async tool, asyncio.gather parallelism, ty type-check rejection, OS blocked by default, workspace_root read+write capture, read-only / overlay mount semantics, resource_limits.max_duration_secs abort, approval gating end-to-end, full Agent run with a scripted chat client. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: fix: monty FileMount test compares against the normalized POSIX path The shorthand string mount goes through _normalize_mount_path, which rewrites Windows drive letters like 'C:\\Users\\...' into '/C:/Users/...' (POSIX-style). The Windows CI runners surfaced this because tmp_path resolves to a backslashed Windows path; the test was comparing against the raw str(host_a) instead of the normalized form. Compare against _normalize_mount_path(str(host_a)) so the assertion is platform-independent. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: fix: address PR #5915 review feedback - _execute_code_tool docstring: clarify that the Monty backend supports scoped filesystem access via workspace_root / file_mounts (blocked by default). - _to_monty_mount: import pydantic_monty lazily through load_monty so missing-dependency errors surface as the same actionable RuntimeError the rest of the package raises (not a bare ImportError at module load). Renamed _load_monty -> load_monty for the same reason. - _python_type_repr: emit None for type(None) instead of Any, and normalize both typing.Union[...] and PEP-604 X | Y to PEP-604 syntax so Optional[X] / Union[..., None] / -> None signatures round-trip correctly through ty validation. Added a regression test. - _PrintCollector: track a running character count instead of recomputing sum(len(c) for c in self.chunks) per callback. Eliminates the O(n^2) cost on print-heavy code. - Instructions: mention that the value of the final expression is also returned alongside captured stdout (matches actual behavior). - 11_monty_codeact Dockerfile: pin ghcr.io/astral-sh/uv to 0.11.6 instead of :latest for reproducible builds. - 11_monty_codeact README: replace the bare "see parent README" pointer with sample-specific steps (./vendor-wheel.sh + uv sync + uv run), since the sample uses pyproject.toml + a vendored wheel rather than requirements.txt. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: sample: 11_monty_codeact installs agent-framework-monty from PyPI Drop the vendored-wheel scaffolding now that agent-framework-monty is on PyPI as an alpha (1.0.0a*) release: - pyproject.toml: remove [tool.uv.sources] override; keep [tool.uv] prerelease = "allow" so uv pulls the alpha automatically. - Dockerfile: drop the COPY wheels/ step. - README: drop the ./vendor-wheel.sh setup step and the not-yet-on-PyPI warning. - Delete vendor-wheel.sh and the gitignored wheels/ directory. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: fix(monty): harden post-execution file capture against symlink escape Same class of issue as the MSRC-reported Hyperlight finding: the post-execution capture walked workspace_root with Path.rglob() + is_file() + read_bytes() - all of which follow symlinks. An attacker who controls the workspace (cloned repo, extracted archive, shared workspace) could pre-place `workspace/leak.txt -> /etc/passwd` or `workspace/outside_dir -> /etc/` and have host files surface as captured Content items. Monty's mount layer already rejects symlink reads from inside the sandbox across all three modes (verified empirically), so the runtime path was safe. This commit closes the post-execution scan path. Changes: - New `_iter_real_files(root)` walker that uses iterdir() + is_symlink() to skip symlinks at every directory level and yields only real files. Replaces the previous `host_root.rglob("*")` calls in both `_snapshot_writable_mounts` and `_capture_written_files`. - Use `Path.lstat()` instead of `Path.stat()` so size/mtime can never be taken from a symlink target. - Three new integration tests reproducing the MSRC attack shape against the workspace_root flow: symlink-to-file outside workspace, symlink-to-directory outside workspace, and a guard ensuring legitimate sandbox writes are still captured when symlinks are present. Per user request, hyperlight is untouched in this commit (separate fix). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: fix(monty): skip symlink regression tests when unsupported Apply the same Windows-CI safety guard as the hyperlight fix in PR #5919: the three symlink integration tests create symlinks via Path.symlink_to(), which fails with OSError / NotImplementedError on unprivileged Windows runners. Add a local _symlinks_supported helper (mirroring the one in packages/core/tests/core/test_skills.py) and pytest.skip when symlinks aren't available, so the tests no longer fail for environment reasons. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: fix(monty): address PR #5915 follow-up review feedback - _invoke_tool: drop the inspect.iscoroutinefunction(...) branch and always `await self.tool_map[name](**kwargs)`. Every entry in tool_map is `partial(FunctionTool.invoke, skip_parsing=True)` and FunctionTool.invoke is `async def`, so the branching was dead code - and on Python versions affected by cpython#98590, iscoroutinefunction(partial(bound_async_method, ...)) returns False, causing the bridge to take the asyncio.to_thread path, return an unawaited coroutine, and surface it as a JSON-serialization failure for every tool call. Added a regression test test_invoke_tool_awaits_partial_wrapped_async_method. - generate_type_stubs: skip tools whose name is not a valid Python identifier or is a Python keyword. FunctionTool.name has no upstream validation, so a name like "weird-name" produced a syntax error in the stubs and a name like "broken\n pass\nasync def injected" would inject arbitrary stub source. Non-identifier names stay reachable via `call_tool("weird-name", ...)` at runtime; they just don't get type-checked stubs. Added regression test test_generate_type_stubs_skips_non_identifier_tool_names. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>Eduard van Valkenburg ·
2026-05-20 00:35:23 +00:00 -
Python: Bump Python package versions for a release (#5964)
* Bump Python package versions to 1.5.0 for a release * Promote orchestrations to 1.0.0rc1 * ci(python-setup): merge dynamic exclude into existing workspace exclude The python-setup action injected exclude = [...] verbatim into [tool.uv.workspace], producing a duplicate 'exclude' key when the section already had a static exclude. Scope the rewrite to the [tool.uv.workspace] section and append the package to the existing array when present; idempotent if the package is already excluded. * Address Copilot review feedback: raise inter-package floors to 1.5.0 - foundry, foundry-local: agent-framework-openai >=1.4.0 -> >=1.5.0 - azure-contentunderstanding: agent-framework-foundry >=1.4.0 -> >=1.5.0 - azurefunctions: pin agent-framework-durabletask to >=1.0.0b260519,<2 Keeps lockstep cohort consistent and avoids mixed 1.4.x / 1.5.0 installs. * Re-include azurefunctions and durabletask in the uv workspace The pinned durabletask>=1.4.0 floor is enough to make resolution succeed; the workspace exclude was over-correction and broke CI samples and pyright type-checking (re-exports in agent_framework/azure/__init__.pyi plus samples/04-hosting/{azure_functions,durabletask}/ could not resolve their imports). Dropping them from agent-framework-core[all] still stands so the metapackage does not pull them. * Restore azurefunctions and durabletask in agent-framework-core[all] The durabletask floor pin keeps users on the safe 1.4.0, so they are once again included in the metapackage. Update CHANGELOG to reflect the pin rather than an [all] removal. * Raise uvicorn ceiling in ag-ui and devui to allow 0.42+ The root override-dependencies pins uvicorn[standard]>=0.34.0 (no upper) and the workspace lock resolves to 0.47.0. The package ceiling <0.42.0 meant the workspace was no longer testing the declared supported range. Bump to <1 so the lock fits within the declared bounds. Also picked up by validate-dependency-bounds: refresh stale orchestrations RC pin in devui dev deps.Evan Mattson ·
2026-05-20 09:20:53 +09:00 -
Remove duplicate pop in InMemoryCacheProvider.remove (#5795)
The second self._cache.pop(key, None) call is a guaranteed no-op: the first pop has already removed the key (or returned None), and there is no await between the two statements that could allow another coroutine to re-add it. Removing the dead line clarifies intent without changing behavior.
Taisir Hassan ·
2026-05-19 14:02:20 +00:00 -
Python: fix: hyperlight skips symlinks when staging sandbox input (#5919)
* Python: fix(hyperlight): skip symlinks when staging files into the sandbox The helpers that populate the sandbox input tree (``_copy_path`` and the ``_path_tree_signature`` walker used for cache invalidation) relied on ``Path.is_file()``, ``Path.is_dir()`` and ``shutil.copy2`` - all of which follow symlinks by default. When the source tree contains symlinks, that let entries from outside the configured input source surface inside the sandbox. Harden both code paths to never follow symlinks: - ``_copy_path`` now bails out via ``Path.is_symlink()`` before any ``is_dir()`` / ``is_file()`` check, skips non-regular files, and uses ``shutil.copy2(..., follow_symlinks=False)`` as defense in depth. - New ``_iter_real_entries`` walker replaces the previous ``Path.rglob`` call inside ``_path_tree_signature`` (rglob follows directory symlinks). - ``_path_tree_signature`` switches to ``Path.lstat()`` so size/mtime are never read through a symlink target. Added regression tests covering: - A pre-placed file symlink in ``workspace_root`` (top level). - A pre-placed directory symlink in ``workspace_root``. - A nested file symlink inside a real subdirectory. - ``_path_tree_signature`` ignoring symlinks so the cache key reflects only what is actually staged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: fix(hyperlight): address PR #5919 review feedback - _iter_real_entries now yields directories and regular files only, skipping non-regular entries (sockets/FIFOs/devices). Keeps the cache-key signature consistent with what _copy_path actually stages. - The four new symlink regression tests skip when the platform does not support symlink creation (e.g. unprivileged Windows runners), via a local _symlinks_supported helper modelled on the one in packages/core/tests/core/test_skills.py. Prevents OSError / NotImplementedError from failing CI jobs that have nothing to do with the change under test. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: fix(hyperlight): address PR #5919 follow-up review feedback - _copy_path docstring: narrow the scope to "symlink entries present in the source tree at rest" and explicitly call out that the copy is NOT atomic with respect to concurrent mutation of the source tree. Callers who need that stronger guarantee should snapshot their workspace before passing it in. Avoids overpromising on a TOCTOU window that pathlib cannot express; closing it properly would need fd-based traversal (O_NOFOLLOW | O_DIRECTORY + os.scandir(fd)) with a separate Windows story, which is out of scope for this targeted fix. - _path_tree_signature: drop the `if path.is_symlink(): return ()` short-circuit. Resolve a symlink root to its real target before walking instead. The public construction flow already resolves workspace_root / file_mounts[].host_path up front so this never affected user-facing code, but the short-circuit was misleading and would have produced an empty, stable signature for any direct caller that builds a _RunConfig without going through the public constructor. Defense in depth: even if a future call site forgets to resolve the root, the cache key still reflects real contents. - Added regression test test_path_tree_signature_walks_through_symlinked_root: a symlinked workspace root must produce a non-empty signature, AND the signature must change when the real target's contents change so the cache key actually invalidates. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Eduard van Valkenburg ·
2026-05-19 11:41:53 +00:00 -
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>
Tao Chen ·
2026-05-19 06:38:53 +00:00 -
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 from394bcd607remains. 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 * CleanupEvan Mattson ·
2026-05-19 00:15:25 +00:00 -
Python: Fix GitHubCopilotAgent to include tools added by ContextProvider.before_run in session creation (#5780)
* Fix GitHubCopilotAgent ignoring tools from context providers (#5736) _create_session and _resume_session only forwarded self._tools (constructor tools) to CopilotClient.create_session, dropping any tools contributed by context providers via session_context.extend_tools() during before_run. Merge provider-contributed tools into runtime_options in both _run_impl and _stream_updates before session creation, mirroring how RawAgent handles the merge at lines 1435-1440 in _agents.py. Update _create_session and _resume_session to combine self._tools with the merged runtime tools. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Fix GitHubCopilotAgent to include tools added by ContextProvider.before_run in session creation Fixes #5736 * Fix provider tool merge to avoid mutating caller's list - Replace in-place .extend() with fresh list creation in both _run_impl and _stream_updates paths to prevent mutating the caller-provided options['tools'] list (shallow copy issue) - Also handles immutable Sequence types (e.g. tuple) correctly - Add test for provider tools forwarded via _resume_session path Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address review feedback for #5736: review comment fixes --------- Co-authored-by: Copilot <copilot@github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Giles Odigwe ·
2026-05-15 14:59:22 +00:00 -
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>
SergeyMenshykh ·
2026-05-15 09:47:00 +00:00 -
Python: bump package versions for 1.4.0 release (#5872)
* fixes * fixes * Python: bump package versions for 1.4.0 release Cuts the python-1.4.0 release. MINOR bump on the released cohort (agent-framework, agent-framework-core, agent-framework-openai, agent-framework-foundry: 1.3.0 -> 1.4.0), driven by breaking changes in experimental skills API and new features. All 21 beta packages stamp 1.0.0b260514, all 3 alpha packages stamp 1.0.0a260514, and ag-ui remains at 1.0.0rc1 (freshly promoted). Date stamp reflects 2026-05-14 Pacific. - Released cohort: 1.3.0 -> 1.4.0 - Beta packages (21): 1.0.0b260507 -> 1.0.0b260514 - Alpha packages (3): 1.0.0a260507 -> 1.0.0a260514 - ag-ui: stays at 1.0.0rc1 (dep bound updated only) - Inter-package dependency lower bounds updated (>=1.3.0 -> >=1.4.0) - Fix chatkit StructuredInputItem exhaustiveness for openai-chatkit 1.6.4 - Update CHANGELOG compare links - uv.lock refreshed Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Giles Odigwe ·
2026-05-15 09:31:03 +09:00 -
Python: Fix A2A v1.0 non-streaming response and sample runtime issues (#5849)
- Fix non-streaming empty response by accumulating intermediate WORKING status updates and flushing them when an empty terminal event arrives - Fix sample agent_executor.py to enqueue Task before status events (required by v1.0 ActiveTask validation) - Fix create_jsonrpc_routes() calls to include required rpc_url param - Fix TYPE_CHECKING imports in sample agent_definitions.py - Add tests for non-streaming content accumulation behavior Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Giles Odigwe ·
2026-05-14 22:28:02 +00:00 -
Python: forward MCP tool call metadata (#5815)
* Python: forward MCP tool call metadata * fix: preserve MCP tool meta after prompt reload
Yufeng He ·
2026-05-14 21:50:39 +00:00 -
Python: Reject path-traversal context ids in Foundry Hosting Checkpoint Storage (#5851)
* Reject path-traversal context ids in foundry workflow checkpoint storage Agent-Logs-Url: https://github.com/microsoft/agent-framework/sessions/fca3aae6-50eb-4726-8baf-2718217d4e79 Co-authored-by: lokitoth <6936551+lokitoth@users.noreply.github.com> * Address PR review feedback: clarify URL-decode comment, isolate test root, add e2e workflow rejection tests Agent-Logs-Url: https://github.com/microsoft/agent-framework/sessions/832f45a6-c01e-4da9-bf85-1ba7b5f302e6 Co-authored-by: lokitoth <6936551+lokitoth@users.noreply.github.com> * Clarify MSRC repro padding length in regression test Agent-Logs-Url: https://github.com/microsoft/agent-framework/sessions/832f45a6-c01e-4da9-bf85-1ba7b5f302e6 Co-authored-by: lokitoth <6936551+lokitoth@users.noreply.github.com> * add E2E http test for checkpoint context id rejection Agent-Logs-Url: https://github.com/microsoft/agent-framework/sessions/730258ef-2781-4a7d-b7cf-b5c40c11defc Co-authored-by: lokitoth <6936551+lokitoth@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: lokitoth <6936551+lokitoth@users.noreply.github.com> Co-authored-by: Jacob Alber <jaalber@microsoft.com>
Copilot ·
2026-05-14 21:38:37 +00:00 -
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>
SergeyMenshykh ·
2026-05-14 17:58:10 +00:00 -
Evan Mattson ·
2026-05-14 15:05:27 +00:00 -
Python: Bump agent-framework-ag-ui to release candidate stage (#5844)
* Bump agent-framework-ag-ui to release candidate stage * Mark agent-framework-ag-ui as rc in PACKAGE_STATUS
Evan Mattson ·
2026-05-14 14:56:34 +00:00 -
[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>
SergeyMenshykh ·
2026-05-14 10:28:22 +00:00 -
[BREAKING] Python: DevUI: tighten default access controls and CORS posture (#5740)
* Python: DevUI: tighten default access controls and CORS posture Adjusts the default configuration of the DevUI server so the out-of-the-box posture matches what most callers expect when running locally. Adds explicit opt-outs for callers who need the previous behavior. - DevServer gains auth_enabled and auth_token constructor params; auth is on by default. Auto-generates and logs a token when none provided. - CORS default is an empty allowlist on every host. Callers wanting cross-origin pass cors_origins explicitly. - Streaming /v1/responses no longer sets Access-Control-Allow-Origin directly; CORSMiddleware owns all CORS decisions. - Loopback binds enforce a Host-header allowlist. - /meta moved out of the auth bypass list (was alongside /health and /). - serve() default flipped to auth_enabled=True; passes auth args through to DevServer instead of using env-var indirection. - CLI: --auth opt-in replaced with --no-auth opt-out; --auth-token preserved. - Tests cover the eight behaviors above in test_server.py. * Python: DevUI: address PR review comments - /meta now derives auth_required from self.auth_enabled instead of reading DEVUI_AUTH_TOKEN, so the auto-generated and explicit auth_token paths report correctly. - Reorder middleware so the loopback Host-header allowlist is registered last; Starlette wraps later-added middleware around earlier-added ones, so the host check now runs outermost (before CORS/auth) as intended. - Rework comments to describe the behavior rather than threat scenarios. - Streaming-headers and CORS tests now construct the server with an explicit auth_token and send a Bearer header, so the assertions actually exercise the streaming/CORS path instead of short-circuiting in the auth middleware.
Evan Mattson ·
2026-05-14 00:37:46 +00:00 -
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.
Evan Mattson ·
2026-05-13 22:09:04 +00:00 -
[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>
SergeyMenshykh ·
2026-05-13 20:35:52 +00:00 -
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>
Giles Odigwe ·
2026-05-13 20:09:59 +00:00 -
Python: add ag-ui tool result display channel (#5762)
* Python: add ag-ui tool result display channel Key decisions: - Add TOOL_RESULT_DISPLAY_KEY and make state_update accept optional state plus a tool_result display payload. - Keep text as the LLM-bound tool result while using the display marker only for ToolCallResultEvent.content. - Reuse one outer/inner Content additional_properties extraction helper for state and display markers, preserving fallback behavior when display is absent. Files changed: - python/packages/ag-ui/agent_framework_ag_ui/_state.py - python/packages/ag-ui/agent_framework_ag_ui/_run_common.py - python/packages/ag-ui/tests/ag_ui/test_run_common.py - python/packages/ag-ui/tests/ag_ui/golden/test_scenario_deterministic_state.py - python/issues/done/01-tool-result-display-channel.md Blockers/notes: - Slice 1 is complete and moved to issues/done. - Slice 2 remains for docstring and README documentation. * Python: document ag-ui tool result display channel Key decisions: - Document state_update as the single helper for LLM text, UI-only tool_result display content, and durable shared state. - Keep the display guidance explicit that text remains LLM-bound while tool_result feeds ToolCallResultEvent.content. - List both reserved additional_properties markers in the docstring return contract. Files changed: - python/packages/ag-ui/agent_framework_ag_ui/_state.py - python/packages/ag-ui/README.md - python/issues/done/02-docs-tool-result-display.md Blockers/notes: - Slice 2 is complete and moved to issues/done. - Verification passed: uv run poe syntax -P ag-ui --check; uv run poe test -P ag-ui; uv run poe markdown-code-lint; uv run ruff check packages/ag-ui/agent_framework_ag_ui/_state.py. - Commit hooks were skipped after poe-check repeatedly rewrote uv.lock ordering; the same checks were run manually and passed. * Python: update gitignore
Evan Mattson ·
2026-05-12 22:12:04 +00:00 -
Python: [BREAKING] Migrate agent-framework-a2a to a2a-sdk v1.0 (#5752)
* Python: Migrate agent-framework-a2a to a2a-sdk v1.0 Upgrade the a2a-sdk dependency from v0.3.x to v1.0.0 and migrate all source, tests, samples, and documentation to the v1.0 API. Key changes: - Dependency: a2a-sdk>=1.0.0,<2 (was >=0.3.5,<0.3.24) - Types are now protobuf-based: Part replaces TextPart/FilePart/DataPart - Enums use SCREAMING_SNAKE_CASE (e.g. TaskState.TASK_STATE_COMPLETED) - Roles: Role.ROLE_AGENT, Role.ROLE_USER - Client: SendMessageRequest wrapper, subscribe() replaces resubscribe() - Server: A2AStarletteApplication replaced by Starlette + route factories - DefaultRequestHandler now requires agent_card parameter - TaskUpdater: final parameter removed, add_artifact gains last_chunk - AgentCard.url removed; use supported_interfaces with AgentInterface - Stream yields StreamResponse with WhichOneof('payload') Closes #5661 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR review: validate fallback URL, remove unused task_id vars - Raise ValueError with clear message when transport negotiation fails and no fallback URL is available (neither url arg nor supported_interfaces) - Remove unused task_id local in status_update branch - Inline artifact_event.task_id directly in artifact_update branch Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>Giles Odigwe ·
2026-05-11 22:46:12 +00:00 -
Python: bump package versions for 1.3.0 release (#5706)
* Python: bump package versions for 1.3.0 release MINOR bump on the released cohort (agent-framework, agent-framework-core, agent-framework-openai, agent-framework-foundry: 1.2.2 -> 1.3.0). All 22 beta packages stamp 1.0.0b260507 and all 3 alpha packages stamp 1.0.0a260507 per the lockstep convention. Date stamp reflects 2026-05-07 Pacific. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address review: bump foundry_local openai floor, fix devui orchestrations pin, clarify breaking scope - foundry_local: bump agent-framework-openai lower bound from >=1.1.0 to >=1.3.0 - devui: update stale agent-framework-orchestrations dev pin from 1.0.0b260402 to 1.0.0b260507 - CHANGELOG: clarify [BREAKING] applies to experimental skills API only Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Revert devui orchestrations pin to 1.0.0b260402 to avoid breaking DevUI Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Giles Odigwe ·
2026-05-08 08:57:02 +09:00 -
Python: Upgrade github-copilot-sdk to v1.0.0b2 with new features (#5665)
* Upgrade github-copilot-sdk to v1.0.0b1 and implement new features - Bump github-copilot-sdk dependency from 0.2.1 to 1.0.0b1 - Fix breaking type renames: ErrorClass -> ToolExecutionCompleteError, Result -> ToolExecutionCompleteResult - Add instruction_directories support in GitHubCopilotOptions (session-level) - Add copilot_home support in GitHubCopilotSettings (client-level) - Add sample: github_copilot_with_instruction_directories.py - Update README with new env var and sample entry - Add 8 new unit tests covering the new features (103 total, 96% coverage) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * mypy fix * small fix * Address PR feedback: fix resume path, remove copilot_home from Options, bump to beta.2 - Forward runtime_options through _resume_session (fixes silent drop of instruction_directories/model/etc on resumed sessions) - Remove copilot_home from GitHubCopilotOptions (client-level setting only consumed at startup, not per-call) - Bump github-copilot-sdk from 1.0.0b1 to 1.0.0b2 - Add test for instruction_directories override on resumed sessions - Update existing resume test to match new _resume_session signature Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Giles Odigwe ·
2026-05-07 21:43:47 +00:00 -
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>
SergeyMenshykh ·
2026-05-07 19:39:12 +00:00 -
Python: Fix
MCPStreamableHTTPToolleakingasyncio.CancelledErrorwhen 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>
Evan Mattson ·
2026-05-07 17:58:30 +00:00 -
Python: Add
base_urlparameter toAnthropicClientandRawAnthropicClient(#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>
Evan Mattson ·
2026-05-07 17:57:09 +00:00 -
Python: Add support for function approval flow in Foundry hosted agent (#5666)
* Add support for function approval flow in Foundry hosted agent * Address comments * Address comments * Address comments
Tao Chen ·
2026-05-07 14:55:26 +00:00 -
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>
Eduard van Valkenburg ·
2026-05-07 02:58:38 +00:00 -
Python: Remove bespoke Foundry toolbox helpers; standardize on MCP for toolbox consumption (#5671)
* Remove Foundry toolbox helpers; standardize on MCP for toolbox consumption - Remove RawFoundryChatClient.get_toolbox() and its fetch_toolbox import - Remove fetch_toolbox, select_toolbox_tools, get_toolbox_tool_name, get_toolbox_tool_type, FoundryHostedToolType, ToolboxToolSelectionInput from agent_framework_foundry._tools - Remove ExperimentalFeature.TOOLBOXES from _feature_stage.py (no consumers) - Drop toolbox re-exports from agent_framework_foundry/__init__.py and agent_framework.foundry namespace - Update _sanitize_foundry_response_tool docstring to remove toolbox framing; sanitization logic itself is unchanged - Update _agent.py docstring: 'toolbox-fetched MCP' → 'hosted MCP' - Delete tests/test_toolbox.py (all tests covered removed helpers) - Update test_foundry_chat_client.py: rename/redoc tests that mentioned toolbox but test sanitization that remains - Delete foundry_chat_client_with_toolbox.py (bespoke toolbox API sample) - Delete foundry_toolbox_context_provider.py (relied on select_toolbox_tools) - Rename foundry_chat_client_with_toolbox_mcp.py → foundry_chat_client_with_toolbox.py (canonical MCP pattern) - Rewrite 04_foundry_toolbox/main.py to use MCPStreamableHTTPTool - Update provider/README, context_providers/README, 04_foundry_toolbox/README Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(samples): update 06_files sample to consume toolbox via MCP (#5670) Replace removed get_toolbox/select_toolbox_tools APIs with MCPStreamableHTTPTool, using allowed_tools=["code_interpreter"] to select only the code interpreter from the toolbox endpoint. Update .env.example and README to use FOUNDRY_TOOLBOX_ENDPOINT instead of TOOLBOX_NAME. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(foundry): remove non-existent toolbox helper APIs from README (#5670) Remove the 'fetch, optionally filter, and pass tools directly' pattern from the FoundryChatClient toolbox documentation, as select_toolbox_tools and get_toolbox were removed. Only the MCP endpoint pattern is documented. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(foundry): remove residual toolbox docstring references and reproduction report Remove REPRODUCTION_REPORT.md (workflow artifact that should not be committed), and update two remaining docstring references that still said 'toolbox reads' /'toolbox definition' after the toolbox helpers were removed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Remove bespoke Foundry toolbox helpers; standardize on MCP for toolbox consumption Fixes #5670 * fix(#5670): resolve toolbox endpoint from TOOLBOX_NAME fallback; add namespace regression tests - Add _resolve_toolbox_endpoint() helper in 04_foundry_toolbox/main.py and 06_files/main.py that prefers FOUNDRY_TOOLBOX_ENDPOINT but falls back to deriving the MCP URL from FOUNDRY_PROJECT_ENDPOINT + TOOLBOX_NAME — fixing the startup KeyError when agents are deployed via azd provision (which injects TOOLBOX_NAME, not FOUNDRY_TOOLBOX_ENDPOINT). - Update 04_foundry_toolbox/.env.example to use FOUNDRY_TOOLBOX_ENDPOINT (consistent with 06_files). - Add TOOLBOX_NAME env var to 06_files/agent.yaml so deployed agents have it available for the fallback derivation. - Update both READMEs to document the two ways to supply the toolbox endpoint. - Add test_foundry_namespace_no_longer_exposes_toolbox_helpers() with negative assertions for FoundryHostedToolType, get_toolbox_tool_name, get_toolbox_tool_type, and select_toolbox_tools — guarding against accidental re-introduction of removed symbols. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(samples): fail fast on empty FOUNDRY_TOOLBOX_ENDPOINT; add unit tests Addresses review feedback for #5670: - In _resolve_toolbox_endpoint() (04_foundry_toolbox/main.py and 06_files/main.py) change the walrus-operator check from a truthy test to an explicit 'is not None' guard. An explicitly set empty string now raises ValueError immediately with a clear message instead of silently falling through to the fallback URL construction. - Add tests/samples/hosting/test_toolbox_endpoint.py covering both sample modules: (a) FOUNDRY_TOOLBOX_ENDPOINT set → returned as-is (b) FOUNDRY_TOOLBOX_ENDPOINT set to empty string → ValueError (c) fallback constructs URL from FOUNDRY_PROJECT_ENDPOINT + TOOLBOX_NAME, stripping trailing slashes (d) neither variable group set → KeyError Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address review feedback: remove extraneous test and docstring content - Remove test_foundry_namespace_no_longer_exposes_toolbox_helpers (no longer warranted) - Remove docstring from _agent.py _prepare_tools_for_openai (extraneous) - Trim _chat_client.py _prepare_tools_for_openai docstring to one-liner (toolbox references no longer relevant) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: remove remaining extraneous docstring from RawFoundryChatClient._prepare_tools_for_openai Address review comment on PR #5671: reviewer noted the description isn't warranted now that toolbox helpers have been removed. Matches the pattern in RawFoundryAgentChatClient which has no docstring. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <copilot@github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Evan Mattson ·
2026-05-06 23:56:16 +00:00 -
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>
SergeyMenshykh ·
2026-05-06 09:45:06 +00:00 -
Python: Add Python parity for InvokeMcpTool in declarative workflow (#5630)
* Add Python parity for HttpRequestAction in declarative workflow * Ran pyupgrade and pright to fix CI issues * Fix conversation ID dot parsing for http executor * Removed unnecessary export command * Initial implementation of invoke mcp tool in python * Update sample to support require approval to be toggled by environment variable. * Fix cache and PR comments * Update python/samples/03-workflows/declarative/invoke_mcp_tool/main.py Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com> --------- Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
Peter Ibekwe ·
2026-05-05 20:16:03 +00:00 -
Python: fix(bedrock): don't send toolChoice when no tools are configured (#5172)
* fix(bedrock): don't send toolChoice when no tools are configured BedrockChatClient was sending toolConfig.toolChoice even when no tools were configured (tools=None). AWS Bedrock requires toolConfig.tools to be present whenever toolChoice is specified, causing a 400 validation error. Only set toolChoice when tool_config has a 'tools' key present. Fixes #5165 Signed-off-by: bahtya <bahtyar153@qq.com> * test: add tests for toolChoice without tools - test_prepare_options_tool_choice_auto_without_tools_omits_tool_config - test_prepare_options_tool_choice_required_without_tools_omits_tool_config Verifies that toolConfig is omitted when tool_choice is set but no tools are provided, preventing ParamValidationError from Bedrock. * fix: address maintainer feedback — remove stray test file, raise ValueError for required without tools 1. Remove test_addition.py — stray duplicate of tests already in python/packages/bedrock/tests/test_bedrock_client.py, missing all necessary imports and would fail with NameError. 2. Change tool_choice='required' handling to raise ValueError when no tools are configured instead of silently falling through. Using 'required' without tools is a logical contradiction — the model must invoke a tool but none exist — so surfacing this as a ValueError helps callers catch the misconfiguration early. 3. Update the corresponding test to expect ValueError instead of silently omitted toolConfig. --------- Signed-off-by: bahtya <bahtyar153@qq.com>
bahtyar ·
2026-05-05 19:15:37 +00:00 -
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>
Eduard van Valkenburg ·
2026-05-05 18:08:08 +00:00 -
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>
Eduard van Valkenburg ·
2026-05-05 10:09:19 +00:00 -
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>
Eduard van Valkenburg ·
2026-05-05 10:06:16 +00:00 -
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>
Eduard van Valkenburg ·
2026-05-05 08:39:41 +00:00 -
Python: Fix incorrect workflow timings in DevUI by adding
created_atto executor events (#5615)* fix(devui): add created_at to custom output item events for correct workflow timings (#5545) CustomResponseOutputItemAddedEvent and CustomResponseOutputItemDoneEvent lacked a created_at field, causing the frontend to synthesize timestamps using integer-second precision with a forced +1s minimum gap between events. This made instant workflows appear to take 3+ seconds in the DevUI timeline. Fix: - Add optional created_at: float | None field to both custom event models - Populate created_at=float(time.time()) in the mapper for executor_invoked, executor_completed, and executor_failed events Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(devui): use event created_at for accurate workflow timeline timings workflow-view.tsx synthesized _uiTimestamp using Math.max(baseTimestamp, lastTimestamp + 1) with integer-second precision, forcing a minimum 1-second gap between every sequential event. This made instant workflows appear to take several seconds in the DevUI timeline. The fix prefers event.created_at (a float Unix timestamp populated by the backend mapper for all executor events) and only falls back to the synthetic timestamp when created_at is absent. This matches the pattern already used in devuiStore.ts:addDebugEvent. Added a regression test in test_mapper.py verifying that the mapper attaches created_at to all executor lifecycle events (invoked, completed, failed). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(devui): address review feedback for issue #5545 - Read data.timestamp (ISO string) and response.created_at in addition to top-level created_at when deriving _uiTimestamp, so response.workflow_event.completed events get a real server timestamp instead of a synthesized one - Change uniqueTimestamp tiebreaker: when a real server timestamp is available use Math.max(eventTimestamp, lastTimestamp) rather than lastTimestamp + 1, eliminating artificial 1-second gaps while still preserving monotonic ordering - Apply the same fix in the HIL streaming path (second setOpenAIEvents call in workflow-view.tsx) - Add assert event.created_at > 0 to regression test to guard against zero or negative timestamps - Add test_custom_output_item_event_models_have_created_at_field model- level test so removing the field produces a clear named failure rather than a downstream ValidationError Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(#5545): guard NaN timestamps, fix fallback ID uniqueness, add regression tests - workflow-view.tsx (×2): Wrap data.timestamp ISO→number conversion in a Number.isFinite() guard. Python's datetime.now().isoformat() emits microseconds without a trailing 'Z' (e.g. '2024-01-15T12:34:56.123456'), which some JS engines cannot parse, returning NaN. NaN !== undefined is true so the eventTimestamp !== undefined guard did not catch it, poisoning _uiTimestamp and resetting the monotonic ordering seed (NaN || 0 → 0). - execution-timeline.tsx: Replace uiTimestamp in the fallback syntheticItemId with the per-executor runNumber counter. Two runs of the same executor within the same second previously received identical _uiTimestamp values and therefore identical syntheticItemIds, causing their output buckets, state, and run entries to collide (execution-timeline.tsx:360–408). - Add missing test_workflow_timings_bug.py source file (only a stale .pyc existed). Three regression tests: · test_custom_event_models_lack_created_at_field – model field guard · test_workflow_executor_events_lack_created_at – mapper populates created_at · test_rapid_workflow_events_have_no_top_level_timestamps – confirms data.timestamp format that requires the frontend NaN guard Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address review feedback for #5545: Python: [Bug]: Workflow timings in DevUI are incorrect * devui: move timing regression tests into test_mapper.py, remove dedicated bug file - Delete test_workflow_timings_bug.py; tests belong in existing module files - The two tests already present in test_mapper.py (test_executor_events_carry_created_at_timestamp and test_custom_output_item_event_models_have_created_at_field) cover the same ground as the first two tests in the deleted file - Add test_executor_completed_maps_to_output_item_done_event to test_mapper.py, replacing the third test from the deleted file with a generic, issue-agnostic name and docstring Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address review feedback for #5545: review comment fixes --------- Co-authored-by: Copilot <copilot@github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Evan Mattson ·
2026-05-05 05:59:08 +00:00 -
fix(openai): drop completed continuation_token from shared options in tool loop (#5462)
Fixes #5394. When `background=True` is combined with local function tools, `FunctionInvocationLayer` calls `_inner_get_response(options=mutable_options)` repeatedly with the same dict reference across loop iterations. Once the first poll retrieves a completed background response, `continuation_token` stays in `mutable_options`, so every subsequent iteration takes the `continuation_token is not None` branch and `GET`s the same completed response instead of `POST`ing the tool results. The loop exits after `max_iterations` with empty text and the model never sees any tool output. After the retrieve, if the returned `ChatResponse.continuation_token` is `None` (the background response is no longer in progress), pop `continuation_token` and `background` from the shared options dict in place. The next loop iteration then falls through to the normal `responses.create`/`parse` path and posts tool results. The diagnosis and a verified runtime monkeypatch are in the issue; this is the same fix moved in-tree. Co-authored-by: Yufeng He <40085740+universeplayer@users.noreply.github.com>
Yufeng He ·
2026-05-04 21:22:56 +00:00 -
Python: Support GPT-5 verbosity option and restore Foundry agent_reference (#5619)
* Python: Support GPT-5 verbosity option and restore Foundry agent_reference Adds verbosity as a typed Literal["low","medium","high"] field on OpenAIChatOptions (Responses API) and OpenAIChatCompletionOptions (Chat Completions API), set in the same way as the existing reasoning options. For the Responses API, top-level verbosity is translated to the nested text.verbosity shape the OpenAI service expects. The same field flows through to FoundryChatClient via the existing FoundryChatOptions alias. Also fixes #5582: PR #5447 removed the agent_reference injection from RawFoundryAgentChatClient._prepare_options, so first-turn calls against a Foundry Prompt Agent went out without model and without agent_reference and were rejected by the Responses API with "Missing required parameter: 'model'". Restores the injection on the non-preview path (allow_preview=False) and adds a guard test that asserts the preview path does not inject agent_reference, since the preview SDK injects it via project_client.get_openai_client(agent_name=...). Closes #5516 Closes #5582 * Python: Address Copilot review on PR #5619 - Foundry verbosity sample docstring: replace the misleading "set deployment name on model=" instruction with the actual env-var pattern the sample relies on (FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL). - _build_agent_reference docstring: clarify the helper is used for both Prompt Agents and HostedAgents on the non-preview path. - Add a Responses API test that locks in the documented precedence rule: when both top-level verbosity and text["verbosity"] are supplied, the top-level value wins. * Python: Drop redundant Foundry verbosity sample and list OpenAI sample in README - Remove samples/02-agents/providers/foundry/foundry_chat_client_verbosity.py per review feedback. The verbosity functionality is identical across the OpenAI and Foundry clients (FoundryChatOptions is an alias of OpenAIChatOptions), so a single sample on the OpenAI side is sufficient. - Add the new client_verbosity.py entry to the OpenAI samples README.
Evan Mattson ·
2026-05-04 21:21:40 +00:00 -
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>
Eduard van Valkenburg ·
2026-05-04 21:19:50 +00:00 -
Python: Add Python parity for HttpRequestAction in declarative workflow (#5599)
* Add Python parity for HttpRequestAction in declarative workflow * Ran pyupgrade and pright to fix CI issues * Fix conversation ID dot parsing for http executor * Removed unnecessary export command
Peter Ibekwe ·
2026-05-01 23:04:07 +00:00 -
Python: Enforce approval_mode in Claude and GitHub Copilot agents (#5562)
* Python: Enforce approval_mode in Claude and GitHub Copilot agents Tools declared with approval_mode="always_require" were bypassed by the ClaudeAgent and GitHubCopilotAgent because their SDK-managed tool-calling loops invoke FunctionTool.invoke() directly via package-supplied handlers, skipping the standard _try_execute_function_calls approval gate. Per discussion on #5494, the fix lives in the agents (not in FunctionTool): any flag added to the tool itself can be spoofed by code with the same level of access, so the security boundary is the agent that owns the tool-calling loop. - Add on_function_approval option to ClaudeAgentOptions and GitHubCopilotOptions. Callback receives a FunctionCallContent describing the pending call and returns bool (sync or async). - Gate FunctionTool.invoke() inside each agent's existing tool-handler closure when approval_mode == "always_require". Default policy is deny; callbacks that raise also deny safely. - Deny path returns a tool-error to the model (Claude: text content; Copilot: ToolResult(result_type="failure", error="approval_denied")) so the LLM can react gracefully instead of silently failing. - Tests for both agents covering: deny by default, sync False, sync True, async True, callback-raises -> deny, no-op for never_require tools. - Samples demonstrating sync, async, and deny-by-default flows for both agents. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR review: preserve empty arg dicts, reject runtime approval override - _resolve_function_approval no longer collapses {} into None when building the FunctionCallContent passed to the callback (Claude + Copilot). - Claude _apply_runtime_options and Copilot _run_impl/_stream_updates now raise ValueError if on_function_approval is supplied via per-run options, instead of silently ignoring it. Approval policy must be set at agent construction time. - Drop unnecessary # type: ignore[attr-defined] on Content.name/.arguments in samples (Content is a unified class with both attributes defined). - Add regression tests for the new runtime-options validation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * warning when non callback handler and approval needed --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Eduard van Valkenburg ·
2026-05-01 14:11:28 +00:00 -
Python: Reduce flaky integration tests and improve CI signal quality (#5454)
* Enable Ollama integration tests in CI and rename report to Integration Test Report - Install Ollama, cache models (qwen2.5:0.5b + nomic-embed-text), and start server in the Misc integration job for both workflow files - Set OLLAMA_MODEL and OLLAMA_EMBEDDING_MODEL env vars so the 5 Ollama tests are no longer skipped - Rename Flaky Test Report to Integration Test Report throughout (job names, artifact names, cache keys, file names, script titles/docstrings) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Bump Ollama model to qwen2.5:1.5b for better instruction following The 0.5b model was too small to reliably follow simple prompts like 'Say Hello World', causing test assertion failures. The 1.5b model follows instructions more reliably while still being small enough for fast CI pulls (~1GB). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Re-enable reliable streaming integration tests Remove the hard skip on test_03_reliable_streaming tests that was temporarily disabled for instability investigation. CI infrastructure (Azurite, DTS emulator, Redis, func CLI) is already in place. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Re-enable skipped Functions/DurableTask tests and bump timeout to 480s - Remove hard skips from 4 tests in test_11_workflow_parallel.py - Remove hard skip from test_conditional_branching in test_06_dt_multi_agent_orchestration_conditionals.py - Increase pytest --timeout from 360 to 480 for Functions+DurableTask CI job - Updated in both python-merge-tests.yml and python-integration-tests.yml Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Re-skip failing Functions/DurableTask tests with specific root causes - test_11_workflow_parallel (4 tests): xdist worker crashes during execution - test_conditional_branching: orchestration fails with RuntimeError, not a timeout - Keep 480s timeout bump for remaining Functions tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix auth routing in samples 06/11: api_key -> credential for Azure OpenAI Both samples passed a bearer token provider via api_key= which caused the client to route to api.openai.com instead of Azure OpenAI, resulting in 401 Unauthorized. Changed to credential= which correctly triggers Azure routing and picks up AZURE_OPENAI_ENDPOINT from the environment. - samples/azure_functions/11_workflow_parallel/function_app.py: 1 fix - samples/durabletask/06_multi_agent_orchestration_conditionals/worker.py: 2 fixes - Re-enable 4 parallel workflow tests and 1 conditional branching test Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Re-skip parallel workflow tests: xdist worker distribution issue The 4 parallel workflow tests crash because xdist worksteal distributes them across separate workers, each spawning its own func process against shared emulators. Auth fix (api_key->credential) was valid and stays. test_conditional_branching now passes with the auth fix. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix E501 line-too-long in azurefunctions parallel test skip reasons Wrap skip reason strings to stay within 120 char line limit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add retry logic and port-conflict fix for Ollama CI setup - Kill any auto-started Ollama before launching serve (fixes port conflict: 'address already in use') - Retry ollama pull up to 3 times with 15s backoff (fixes 429 rate limit failures) - Applied to both python-merge-tests.yml and python-integration-tests.yml Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix flaky integration tests and re-enable skipped tests - Foundry agent: add allow_preview=True to custom client test - Foundry hosting: raise max_output_tokens 50->200, add temperature, relax assertion in test_temperature_and_max_tokens - Foundry embedding: update skip reason with root cause (endpoint mismatch) - OpenAI file search: fix vector store indexing race condition by polling file_counts before querying; fix get_streaming_response -> get_response(stream=True) - Azure OpenAI file search: remove skip (transient 500 resolved) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove temperature from foundry hosting test (unsupported by CI model) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Stabilize Ollama tool call integration tests with no-arg function Use a no-argument greet() function instead of hello_world(arg1) for integration tests. The 1.5B model in CI is unreliable at generating correct tool call arguments, causing 'Argument parsing failed' errors. A no-arg function eliminates this flakiness entirely. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Increase reliable streaming test timeouts from 30s to 60s The LLM call through Azure OpenAI + Redis streaming pipeline can exceed 30s in CI due to cold starts or throttling. Raise to 60s to reduce flaky timeouts while still bounded by pytest's 120s per-test limit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Re-enable workflow parallel tests with xdist_group marker The tests were skipped because xdist distributes module tests across workers, each spawning their own func process (port conflicts). Adding xdist_group forces all tests in this module onto a single worker so the module-scoped function_app_for_test fixture works correctly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Revert "Re-enable workflow parallel tests with xdist_group marker" This reverts commit
455c28da62. * Rename flaky_report to integration_test_report and add try/finally cleanup - Rename scripts/flaky_report/ to scripts/integration_test_report/ to reflect expanded scope beyond flaky-test detection - Update workflow references in both CI files - Wrap file search integration tests in try/finally to ensure vector store cleanup runs even on test failure or timeout Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix Ollama pull failure propagation and Azure OpenAI vector store readiness - Ollama CI: fail the step immediately if model pull fails after 3 retries instead of silently proceeding to tests - Azure OpenAI file search: add the same vector-store readiness polling that was applied to the non-Azure OpenAI tests, preventing eventual consistency race conditions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * remove load_dotenv from test file --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>Giles Odigwe ·
2026-05-01 00:41:39 +00:00 -
Python: Fix hosted MCP replay producing orphan function_call_output (#5581)
* Python: Fix hosted MCP replay producing orphan function_call_output Resolves part of #5546. After a turn ran a hosted MCP / Foundry-toolbox-MCP tool, the next turn's replayed input array carried a function_call_output with an mcp_* call_id and no matching function_call, and the Responses API returned a 400. Two layers covered here: * Chat-client serialize layer (packages/openai): adds mcp_server_tool_call and mcp_server_tool_result cases to _prepare_message_for_openai and _prepare_content_for_openai. Pairs are coalesced via a post-pass into a single mcp_call input item carrying both arguments and output. Orphan results are dropped (debug-logged) rather than serialized as orphan function_call_output, which is what the Responses API rejected. * Host read layer (packages/foundry_hosting): _item_to_message and _output_item_to_message now route custom_tool_call_output whose call_id.startswith("mcp_") to Content.from_mcp_server_tool_result. Non-mcp_ call_ids continue to produce Content.from_function_result. Symmetric with the host write-side choice for hosted-MCP results. Two further fixes (agentserver SDK additions, host write-side single-item emission) remain tracked on the issue and depend on an SDK release. * Python: Fix pyright unknown-type in _stringify_mcp_output cast(Sequence[Any], output) after the isinstance check so pyright stops flagging the loop variable as unknown. Also normalizes a couple of em-dashes in docstrings I introduced in the prior commit. * Python: Harden _stringify_mcp_output for dict-shaped MCP outputs Address Copilot review on PR #5581. Today the helper falls back to str() for any non-string, non-text-attribute entry, which produces Python repr (single-quoted dicts) for the canonical MCP raw-JSON text-content shape `{"type": "text", "text": "..."}` and any other dict-shaped output. Three small changes: * List-entry path: prefer plain string entries, then `.text` attribute (Content objects), then `entry["text"]` for Mapping entries in the canonical MCP shape, then JSON-encode anything else. * Final fallback: `json.dumps(output, default=str)` so Mappings and scalars produce valid JSON rather than Python repr. * Two new unit tests covering the dict-with-text shape and the non-text-dict JSON fallback. * Python: Suppress mypy redundant-cast on _stringify_mcp_output narrowing The cast is needed by pyright (reportUnknownVariableType) but mypy considers it redundant after the preceding isinstance narrowing. Pyright's behavior is correct for the strict-mode reporting we run, so keep the cast and silence mypy on the line.
Evan Mattson ·
2026-04-30 21:01:52 +00:00 -
Python: Support OpenAI and Gemini
allowed_toolstool 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>
Giles Odigwe ·
2026-04-29 17:43:47 +00:00 -
Python: bump package versions for 1.2.2 release (#5561)
* Python: bump package versions for 1.2.2 release PATCH bump (1.2.1 -> 1.2.2) for the released cohort. Five PRs land in this window: - agent-framework-openai: fix file_search citations breaking the assistant- message history roundtrip (#5557) — drives the released-tier PATCH - agent-framework-orchestrations: [BREAKING] standardize orchestration terminal outputs as AgentResponse (#5301) - agent-framework-core, agent-framework-declarative: preserve Workflow.run() shared state across calls, accept list[Message] in declarative start executor, and coerce Enum values when serializing PowerFx symbols (#5531) - agent-framework-foundry-hosting: add hosted Durable Workflow support (#5531) - agent-framework-azure-contentunderstanding: new alpha package — Azure AI Content Understanding context provider (#4829) - dependencies: workspace package dependency refresh (#5555) Per lockstep convention, all 21 beta packages stamp 1.0.0b260429 and all 4 alpha packages (now including the new contentunderstanding) stamp 1.0.0a260429. Date stamp reflects 2026-04-29 Pacific. Every non-core package floor on agent-framework-core is raised to >=1.2.2; the new contentunderstanding package's stale >=1.0.0 floor is brought into line. Two follow-on fixes bundled to keep validate-dependency-bounds-test green at lowest-direct resolution: - Bump agent-framework-azure-contentunderstanding's azure-ai-content understanding lower bound from >=1.0.0 to >=1.0.1 (1.0.0 ships without proper typing — pyright reports 65 unknown-type errors) - Add pyright ignore comments to core/foundry/__init__.pyi for the new alpha package's type-stub imports, since alpha packages are not in core's [all] extra and therefore aren't installed at lowest-direct * Python: add #5552 to 1.2.2 CHANGELOG Add the streaming-span observability fix to the Fixed section. PR is on upstream/main but not yet pulled into origin/main; the code itself will land via the PR merge. * Python: address PR #5561 review feedback on dependency bounds Two packaging fixes flagged in review: 1. agent-framework-azure-contentunderstanding: add agent-framework-foundry as a runtime dependency. The package's README directs users to `pip install agent-framework-azure-contentunderstanding --pre` and the basic example imports `FoundryChatClient` from `agent_framework.foundry`, so the documented install path was failing with ImportError. Pulling agent-framework-foundry into deps makes the advertised entry path self-contained. 2. agent-framework-foundry: bump agent-framework-openai lower bound from >=1.1.0 to >=1.2.2,<2. Foundry imports private modules from agent_framework_openai (`_chat_client.py:22`, `_agent.py:34`), so resolvers were free to pair foundry==1.2.2 with older OpenAI versions that lack this release's coordinated Responses/history fix. Lockstep the floor with the released cohort to prevent mismatched installs. Both changes pass `validate-dependency-bounds-test` lower + upper at their respective packages.
Evan Mattson ·
2026-04-29 17:51:48 +09:00 -
Python: Fix spans not correctly nested when using streaming (#5552)
* Fix spans not correctly nested when using streaming * fix pre commit * Address comments
Tao Chen ·
2026-04-29 08:21:28 +00:00 -
Python: Fix file_search citations breaking assistant history roundtrip (#5557)
* Python: Fix file_search citations breaking assistant history roundtrip The Responses API rejects 'input_file' inside an assistant message, but the SDK was emitting it whenever an assistant Message contained a hosted_file content (which is what file_search citations become). Three coordinated fixes: 1. _prepare_content_for_openai now skips hosted_file for the assistant role instead of mapping to input_file (which the API rejects there). 2. The streaming response.output_text.annotation.added handler attaches file_citation, container_file_citation, and file_path as annotations on text content, matching the non-streaming path. Previously streaming produced standalone HostedFileContent items that always tripped (1). 3. output_text serialization preserves Annotation objects on roundtrip via a new _annotations_to_output_text helper instead of hardcoding 'annotations' to []. file_search citations now survive multi-agent forwarding. Closes #5556. * Address PR review - _annotations_to_output_text: fan out one entry per annotated_region for url_citation/container_file_citation (Annotation.annotated_regions is a Sequence; the API form carries one start/end per entry). - Validate region span bounds are ints before emitting; skip otherwise. - Add test for the file_path branch (annotation with file_id only). - Add test verifying streamed citation events coalesce onto surrounding text via _finalize_response so span indices reference the merged text, not the empty-text streaming carrier.
Evan Mattson ·
2026-04-29 07:38:19 +00:00