Compare commits

...
Author SHA1 Message Date
Tao Chen ea052ab511 Merge branch 'main' into local-branch-python-add-reset-to-workflow 2026-06-15 09:50:34 -07:00
Tao Chen d08cec5379 Addres race condition when stream is dropped midway 2026-06-15 09:40:21 -07:00
40a2dd5cd0 .NET: Restore ambient client-header scope between non-streaming ClientHeadersAgent runs (#6517)
* Restore ambient client-header scope between non-streaming runs (#6516)

Make ClientHeadersAgent.RunCoreAsync async + await so the per-run
ClientHeadersScope is unwound on return, matching the streaming path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Assert per-run on wire instead of brittle exact request count

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-15 13:21:53 +00:00
7e9c043c4c Python: Improve PR template and breaking-change label automation (#6473)
* Improve PR template and breaking-change label automation

- Add a structured "Related Issue" section using GitHub closing keywords
- Add a Review Guide prompt (major changes, impact, reviewer focus) with a
  note that the focus item is for human reviewers only
- Add checklist items for issue linkage / no duplicate PRs and invert the
  breaking-change item (checked = not breaking)
- Extend label-title-prefix to prepend [BREAKING] when the "breaking change"
  label is added
- Add label-breaking-change workflow to apply the "breaking change" label
  when a PR title contains [BREAKING]

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add pull-requests agent skill with dotnet/python links

- Add root .github/skills/pull-requests/SKILL.md covering PR description
  authoring (following the PR template) and the review-comment workflow
  (review -> plan -> user review -> implement -> reply to all -> resolve)
- Symlink the skill from python/.github/skills and dotnet/.github/skills
- Reference the skill from python/AGENTS.md and dotnet/AGENTS.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fold breaking-change labeling into label-pr workflow

Move the title -> 'breaking change' label logic into the existing label-pr
workflow (which already applies the python/.NET labels) and drop the separate
label-breaking-change workflow.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address PR title prefix review feedback

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Pin patched MessagePack for .NET restore

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Revert MessagePack central pin

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Move title prefix tests out of tracked GitHub tests

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Exclude skill docs from CI path filters

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Match skill symlinks in CI path exclusions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Exclude AGENTS docs from CI path filters

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Scope title-prefix normalization to a real prefix

The normalization branch in addTitlePrefix matched ^Python (no colon), so
titles like "Python samples improvements" or "Pythonic refactor" were treated
as already-prefixed and only re-cased, never receiving the "Python: " prefix.
Scope the match to ^<prefix>:\s* so only an actual existing prefix is
normalized; otherwise the prefix is prepended. Same fix applies to the .NET
prefix (e.g. ".NETStandard bump").

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-15 10:55:23 +00:00
d7e8d2206d Python: Fix Python OTel usage detail attributes (#6493)
* fix python otel usage detail attributes

Map cached/read/reasoning usage detail fields to standard OTel GenAI attributes while preserving provider-specific legacy keys.

Add focused coverage for direct response spans, aggregated agent spans, and provider usage parsing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* address usage detail review feedback

Omit missing OpenAI Responses usage detail counts while preserving zero-valued counts.

Record zero-valued token usage in OTel histograms and add regression coverage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-15 07:10:14 +00:00
d7027fc1f9 Python: [BREAKING] Align FileAccess tools with .NET — directory discovery and recursive search (#6476)
* Align FileAccess tools with .Net; add directory discovery and recursive search

* Fix choices field description: spacing, line length, grammar

Addresses PR review: separate concatenated string literals with proper
spacing/newlines, wrap lines under the 120-char Ruff limit, and fix
"doesn't" -> "don't".

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address PR comments

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-15 06:55:21 +00:00
df0bd4da82 Python: Fix ollama_chat_client.py sample: pass tools via options dict (#6480)
* Fix ollama_chat_client.py sample: pass tools via options dict

The sample was passing tools as a direct keyword argument to
get_response(), which caused a TypeError. The tools parameter
must be passed inside the options dict per the SupportsChatGetResponse
protocol.

Fixes #6411

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Wrap tools in a list as expected by OllamaChatClient

_prepare_tools_for_ollama iterates the tools value, so it must be a
list rather than a bare FunctionTool instance.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-15 06:52:14 +00:00
Peter IbekweandGitHub ed4ff188fc Python: [Breaking] Additional bug fix for declarative workflows (#6489)
* Fix declarative object parsing bug

* Remove unnecessary comment

* Address PR comments

* Address PR comments.

* Fix CI failures.

* declarative action approval bugfix

* Address PR comments

* Inlined single use variables.
2026-06-12 16:58:35 +00:00
0f483fa968 Set ApplicationName on CosmosClientOptions for UserAgent telemetry (#6481)
Added CosmosOptionsHelper (in Microsoft.Agents.AI.CosmosNoSql namespace)
that sets CosmosClientOptions.ApplicationName per component, producing
wire-visible UserAgent suffixes:

- CosmosChatHistoryProvider: Microsoft.Agents.CosmosNoSql.ChatHistory/{version}
- CosmosCheckpointStore: Microsoft.Agents.CosmosNoSql.Checkpoint/{version}

This ensures Cosmos DB requests from the Agent Framework are identifiable
in telemetry, enabling usage tracking and diagnostics queries that can
distinguish between chat history and checkpoint workloads.

Addressed review feedback:
- Truncates ApplicationName to 64 chars (Cosmos SDK max length)
- Moved helper to Microsoft.Agents.AI.CosmosNoSql namespace (scoped ownership)
- Uses StringComparison.Ordinal for IndexOf call

When users provide their own CosmosClient instance, the ApplicationName
is not overridden - users retain full control.

Co-authored-by: TheovanKraay <TheovanKraay@users.noreply.github.com>
2026-06-12 16:32:07 +00:00
westeyandGitHub 5e830f4dc9 .NET: Only use the output from the last message for structured output (#6499)
* Only use the output from the last message for structured output

* Address PR comments

* Address PR comment

* Address PR comments
2026-06-12 15:47:32 +00:00
1acd242550 Python: Add AgentLoopMiddleware for re-running agents in a loop (#6174)
* Python: Add AgentLoopMiddleware for re-running agents in a loop

Add `AgentLoopMiddleware`, an `AgentMiddleware` that re-runs the wrapped
agent in a loop. A single configurable class covers three common patterns,
each with a convenience classmethod factory:

- Ralph loop (`.ralph(...)`): no exit criteria, with feedback tracking
  (`record_feedback`/`progress`), progress injection (`inject_progress`),
  optional fresh context per iteration (`fresh_context`), and an early-stop
  completion signal (`is_complete`).
- Predicate (`.with_predicate(...)`): loop while a `should_continue` callable
  returns True (e.g. paired with `todos_remaining`/`background_tasks_running`).
- Judge (`.with_judge(...)`): a second chat client decides whether the original
  request was answered, using a `JudgeVerdict` structured-output response.

The loop also auto-resolves pending function-approval / user-input requests via
an `on_approval_request` callable (bounded by `max_approval_rounds`), and the
next iteration's input is controlled by `next_message`. Supports both streaming
and non-streaming runs.

Exports `AgentLoopMiddleware`, `JudgeVerdict`, `todos_remaining`, and
`background_tasks_running`. Adds tests, a sample, and docs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: Refine AgentLoopMiddleware API and sample

- with_judge: add criteria list with {{criteria}} templating into judge
  instructions plus an agent-side instruction; add fresh_context, additional
  judge feedback relay; default judge max_iterations.
- should_continue is now required and positional; supports (bool, str|None)
  feedback tuples surfaced to next_message/record_feedback via feedback kwarg.
- Judge forwards full multi-modal request and response messages.
- Default max_iterations=10 (explicit None = unbounded); removed is_complete and
  Ralph terminology; ShouldContinueResult is a real TypeAlias.
- Sample: stream all loops, print iteration counts via injected user-block
  boundaries (robust to function calling), <role>: content formatting, per-method
  expected output, and a looping todo sample.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: Fix CI checks for AgentLoopMiddleware

- Resolve pyright errors in _loop.py: drop the always-true final_result None
  check (the while loop always assigns it) and cast finish_reason to the
  AgentResponse constructor's expected type.
- Apply pyupgrade --py310-plus: import TypeAlias from typing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: Resolve mypy/pyright disagreement on finish_reason

pyright infers AgentResponse.finish_reason as including str and rejects the
direct assignment, while mypy considers a cast redundant. Drop the cast and
suppress only pyright with a targeted reportArgumentType ignore, satisfying
both type checkers.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: Add todo+judge AgentLoopMiddleware sample

Add a second AgentLoopMiddleware sample that composes two criteria in one
should_continue predicate: a TodoProvider check (evaluated first) and a
report-style judge chat client (evaluated once todos are complete) that grades
the assembled report against shared requirements. Register it in the middleware
samples README.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: Compose todo+judge loops as two middleware

Rework the todo+judge sample to compose two AgentLoopMiddleware on the agent
itself (middleware=[judge_loop, todo_loop]) instead of a single hand-written
predicate. The inner todos_remaining loop drafts the report todo-by-todo and the
outer with_judge loop re-runs it until an editor chat client judges the report
publication-ready, reusing the built-in helpers.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Reset session for fresh_context loops via snapshot/restore

AgentLoopMiddleware.fresh_context previously only reset context.messages,
so with an attached session each iteration still reloaded the local
transcript or re-threaded the service-side conversation id and the model
saw the accumulated history. Snapshot the session once before the loop
(via to_dict) and restore it (from_dict + field copy) between iterations,
so every pass starts from the pre-loop baseline. The final iteration's
pass is persisted (no restore after the terminating iteration), so a
subsequent agent.run continues from there.

Removed the obsolete warning, updated docstrings and core AGENTS.md, and
added tests: a snapshot/restore round-trip, a session-reset
streaming x fresh_context x inject_progress x store matrix across multiple
runs and loop iterations, and response_format parsing across the loop.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Updated samples and docstrings

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-12 14:35:54 +00:00
westeyandGitHub 3f77c555cf .NET: [BREAKING] Align FileAccess tools with Python; add directory discovery and recursive search (#6474)
* Align FileAccess with python and improve functionality

* Addressing PR comments
2026-06-12 14:28:26 +00:00
westeyandGitHub cd512da731 .NET: Updating MessagePack to latest version (#6497)
* Updating MessagePack to latest version

* Remove MessagePack from package directly, since CentralPackageTransitivePinningEnabled is true
2026-06-12 13:45:14 +00:00
76b2b1bf39 Python: Add opt-in AG-UI thread snapshot persistence and hydration (#6471)
* feat(ag-ui): add thread snapshot store primitives

Key decisions:\n- Introduce an AGUIThreadSnapshot model limited to replayable messages, optional Shared State, and optional interrupt state.\n- Define AGUIThreadSnapshotStore as an async protocol keyed by explicit Snapshot Scope and AG-UI Thread id.\n- Add InMemoryAGUIThreadSnapshotStore as memory-only, latest-only, bounded local/demo/test storage; no file-backed store is introduced.\n- Require snapshot_scope_resolver whenever an endpoint is configured with a snapshot store, including pre-wrapped runners, so thread ids are not authorization boundaries.\n\nFiles changed:\n- packages/ag-ui/agent_framework_ag_ui/_snapshots.py\n- packages/ag-ui/agent_framework_ag_ui/__init__.py\n- packages/ag-ui/agent_framework_ag_ui/_agent.py\n- packages/ag-ui/agent_framework_ag_ui/_workflow.py\n- packages/ag-ui/agent_framework_ag_ui/_endpoint.py\n- packages/core/agent_framework/ag_ui/__init__.py\n- packages/core/agent_framework/ag_ui/__init__.pyi\n- packages/ag-ui/tests/ag_ui/test_snapshots.py\n- packages/ag-ui/tests/ag_ui/test_endpoint.py\n- packages/ag-ui/tests/ag_ui/test_public_exports.py\n- packages/ag-ui/AGENTS.md\n\nVerification:\n- uv run pytest packages/ag-ui/tests/ag_ui/test_snapshots.py packages/ag-ui/tests/ag_ui/test_public_exports.py packages/ag-ui/tests/ag_ui/test_endpoint.py::test_endpoint_requires_snapshot_scope_resolver_when_store_configured packages/ag-ui/tests/ag_ui/test_endpoint.py::test_endpoint_accepts_snapshot_store_with_scope_resolver -q\n- uv run pytest packages/ag-ui/tests/ag_ui/test_endpoint.py::test_endpoint_requires_snapshot_scope_resolver_when_store_configured packages/ag-ui/tests/ag_ui/test_endpoint.py::test_endpoint_requires_snapshot_scope_resolver_when_wrapped_runner_has_store packages/ag-ui/tests/ag_ui/test_endpoint.py::test_endpoint_accepts_snapshot_store_with_scope_resolver -q\n- uv run poe syntax -P ag-ui -C\n- uv run poe pyright -P ag-ui\n- uv run poe syntax -P core -C\n- uv run poe pyright -P core\n- uv run poe typing -P ag-ui\n- uv run poe typing -P core\n- uv run poe test -P ag-ui\n- uv run poe check -P ag-ui\n- git diff --check\n- git diff --cached --check\n\nBlockers / next iteration:\n- No blockers. Next slice can use the store contract to capture and hydrate agent snapshots.\n- uv repeatedly refreshed azure-ai-projects in uv.lock during local runs; reverted the generated lockfile churn because this change does not alter dependencies.\n- The poe-check commit hook was skipped after manual verification because it reformatted unrelated core MCP files outside this task.

* feat(ag-ui): hydrate agent threads from snapshots

Key decisions:
- Resolve Snapshot Scope per endpoint request and pass it to the AG-UI runner only when snapshot storage is active.
- Treat empty messages with no resume payload as an agent Hydrate Request when a scoped snapshot store is configured, replaying stored Shared State and message snapshots without invoking the wrapped agent.
- Save the latest replayable agent message snapshot and Shared State at normal completion under Snapshot Scope plus AG-UI Thread id; no durable or file-backed store is introduced.

Files changed:
- packages/ag-ui/agent_framework_ag_ui/_agent_run.py
- packages/ag-ui/agent_framework_ag_ui/_endpoint.py
- packages/ag-ui/agent_framework_ag_ui/_snapshots.py
- packages/ag-ui/tests/ag_ui/test_endpoint.py

Verification:
- uv run pytest packages/ag-ui/tests/ag_ui/test_endpoint.py::test_agent_endpoint_hydrates_stored_thread_snapshot_without_invoking_agent -q
- uv run pytest packages/ag-ui/tests/ag_ui/test_endpoint.py::test_agent_endpoint_hydrates_stored_thread_snapshot_without_invoking_agent packages/ag-ui/tests/ag_ui/test_endpoint.py::test_agent_endpoint_hydrates_snapshots_by_scope_and_thread -q
- uv run pytest packages/ag-ui/tests/ag_ui/test_endpoint.py::test_endpoint_empty_messages packages/ag-ui/tests/ag_ui/test_endpoint.py::test_agent_endpoint_hydrates_stored_thread_snapshot_without_invoking_agent packages/ag-ui/tests/ag_ui/test_endpoint.py::test_agent_endpoint_hydrates_snapshots_by_scope_and_thread -q
- uv run poe syntax -P ag-ui -C
- uv run poe pyright -P ag-ui
- uv run poe typing -P ag-ui
- uv run poe test -P ag-ui
- uv run poe check -P ag-ui
- git diff --check
- git diff --cached --check

Blockers / next iteration:
- No blockers. Next slice can reconstruct normal new-user agent turns from stored snapshots.
- uv repeatedly refreshed azure-ai-projects in uv.lock during local runs; reverted the generated lockfile churn because this change does not alter dependencies.
- The poe-check commit hook was skipped after manual verification because it refreshed unrelated uv.lock dependency resolution.

* feat(ag-ui): reconstruct agent turns from snapshots

Key decisions:
- Load scoped thread snapshots for non-hydrate agent requests only when snapshot storage is active and no resume payload is present.
- Rebuild prior AG-UI history from stored snapshot messages, preserving the incoming new user suffix and treating stored snapshot content as authoritative over conflicting prior client history.
- Merge stored Shared State with request state overrides before schema defaults and existing state-context injection.

Files changed:
- packages/ag-ui/agent_framework_ag_ui/_agent_run.py
- packages/ag-ui/tests/ag_ui/test_endpoint.py

Verification:
- uv run pytest packages/ag-ui/tests/ag_ui/test_endpoint.py::test_agent_endpoint_prepends_stored_snapshot_for_new_user_turn -q
- uv run pytest packages/ag-ui/tests/ag_ui/test_endpoint.py::test_agent_endpoint_deduplicates_full_history_and_merges_fresh_state -q
- uv run pytest packages/ag-ui/tests/ag_ui/test_endpoint.py::test_endpoint_empty_messages packages/ag-ui/tests/ag_ui/test_endpoint.py::test_agent_endpoint_hydrates_stored_thread_snapshot_without_invoking_agent packages/ag-ui/tests/ag_ui/test_endpoint.py::test_agent_endpoint_hydrates_snapshots_by_scope_and_thread packages/ag-ui/tests/ag_ui/test_endpoint.py::test_agent_endpoint_prepends_stored_snapshot_for_new_user_turn packages/ag-ui/tests/ag_ui/test_endpoint.py::test_agent_endpoint_deduplicates_full_history_and_merges_fresh_state -q
- uv run pytest packages/ag-ui/tests/ag_ui/test_endpoint.py -q
- uv run poe syntax -P ag-ui -C
- uv run poe pyright -P ag-ui
- uv run poe test -P ag-ui
- uv run poe check -P ag-ui
- uv run poe typing -P ag-ui
- git diff --check
- git diff --cached --check

Blockers / next iteration:
- No blockers. Next slice can enable workflow AG-UI Thread Snapshot persistence and hydration.
- uv repeatedly refreshed azure-ai-projects in uv.lock during local runs; reverted the generated lockfile churn because this change does not alter dependencies.
- The poe-check commit hook was skipped after manual verification because it refreshes unrelated uv.lock dependency resolution.

* feat(ag-ui): hydrate workflow threads from snapshots

Key decisions:
- Handle workflow Hydrate Requests before resolving or invoking the wrapped workflow when snapshot storage and Snapshot Scope are active.
- Capture only replayable workflow protocol data: workflow-emitted state snapshots, workflow-emitted message snapshots, and synthesized messages from text/tool output.
- Keep workflow snapshot capture inactive without configured persistence, and skip saving snapshots when the workflow stream emits RUN_ERROR.

Files changed:
- packages/ag-ui/agent_framework_ag_ui/_workflow.py
- packages/ag-ui/tests/ag_ui/test_endpoint.py

Verification:
- uv run pytest packages/ag-ui/tests/ag_ui/test_endpoint.py::test_workflow_endpoint_hydrates_emitted_snapshots_without_invoking_workflow packages/ag-ui/tests/ag_ui/test_endpoint.py::test_workflow_endpoint_hydrates_synthesized_text_and_tool_snapshot -q
- uv run pytest packages/ag-ui/tests/ag_ui/test_endpoint.py -q
- uv run pytest packages/ag-ui/tests/ag_ui/golden/test_scenario_workflow.py -q
- uv run poe syntax -P ag-ui -C
- uv run poe pyright -P ag-ui
- uv run poe test -P ag-ui
- uv run poe typing -P ag-ui
- uv run poe check -P ag-ui
- git diff --check
- git diff --cached --check

Blockers / next iteration:
- No blockers. Next slice can preserve interruption state and protect snapshots on errors across agent and workflow endpoints.
- uv repeatedly refreshed azure-ai-projects in uv.lock during local runs; reverted the generated lockfile churn because this change does not alter dependencies.
- The poe-check commit hook was skipped after manual verification because it refreshes unrelated uv.lock dependency resolution.

* feat(ag-ui): preserve interrupted thread snapshots

Key decisions:
- Capture workflow RUN_FINISHED interrupt metadata in replayable AG-UI Thread Snapshots so Hydrate Requests can restore pending workflow actions without invoking or resuming the workflow.
- Keep failed agent and workflow runs from replacing the last good snapshot; RUN_ERROR streams leave the previous snapshot available for hydration.
- Verify interruption hydration through endpoint-level AG-UI streams for both agent and workflow wrappers, including Shared State replay and no wrapped runner invocation.

Files changed:
- packages/ag-ui/agent_framework_ag_ui/_workflow.py
- packages/ag-ui/tests/ag_ui/test_endpoint.py

Verification:
- uv run pytest packages/ag-ui/tests/ag_ui/test_endpoint.py::test_workflow_endpoint_hydrates_interrupted_thread_without_invoking_workflow -q
- uv run pytest packages/ag-ui/tests/ag_ui/test_endpoint.py::test_agent_endpoint_hydrates_interrupted_thread_without_invoking_agent packages/ag-ui/tests/ag_ui/test_endpoint.py::test_agent_endpoint_run_error_does_not_overwrite_previous_snapshot packages/ag-ui/tests/ag_ui/test_endpoint.py::test_workflow_endpoint_hydrates_interrupted_thread_without_invoking_workflow packages/ag-ui/tests/ag_ui/test_endpoint.py::test_workflow_endpoint_run_error_does_not_overwrite_previous_snapshot -q
- uv run pytest packages/ag-ui/tests/ag_ui/test_endpoint.py -q
- uv run pytest packages/ag-ui/tests/ag_ui/golden/test_scenario_workflow.py -q
- uv run poe syntax -P ag-ui -C
- uv run poe pyright -P ag-ui
- uv run poe test -P ag-ui
- uv run poe typing -P ag-ui
- uv run poe check -P ag-ui
- git diff --check
- git diff --cached --check

Blockers / next iteration:
- No blockers. Next slice can document AG-UI Thread Snapshot security and usage.
- uv repeatedly refreshed azure-ai-projects in uv.lock during local runs; reverted the generated lockfile churn because this change does not alter dependencies.
- The poe-check commit hook was skipped after manual verification because it refreshes unrelated uv.lock dependency resolution.

* docs(ag-ui): document thread snapshot security

Key decisions:
- Document AG-UI Thread Snapshot persistence as opt-in and disabled unless a snapshot_store is configured.
- Place Snapshot Scope guidance next to endpoint authentication guidance, making clear that AG-UI Thread ids identify threads but do not authorize snapshot access.
- Describe built-in storage as in-memory only, process-local, latest-only, and not durable production storage; durable stores remain app-owned implementations of AGUIThreadSnapshotStore.
- Call out snapshot confidentiality impact and that no file-backed AG-UI snapshot store is provided.

Files changed:
- packages/ag-ui/README.md

Verification:
- uv run python scripts/check_md_code_blocks.py packages/ag-ui/README.md --no-glob
- git diff --check
- git diff --cached --check
- commit hook without SKIP ran changed-package lint/format and AG-UI README markdown-code-lint successfully before stopping because uv.lock was modified
- uv run poe markdown-code-lint (failed due existing unrelated packages/mistral/README.md missing agent_framework_mistral import resolution; changed AG-UI README blocks passed)

Blockers / next iteration:
- No blockers. Local issue/PRD planning artifacts remain uncommitted.
- uv refreshed azure-ai-projects in uv.lock during markdown lint and the commit hook; reverted the generated lockfile churn because this documentation change does not alter dependencies.
- The poe-check commit hook was skipped after manual verification because it refreshes unrelated uv.lock dependency resolution.

* fix(ag-ui): harden thread snapshot persistence edge cases

- Persist the completed confirm_changes turn with interrupt=None so hydration
  no longer replays a stale pending interrupt after the user responds; resume
  requests prepend stored history so the persisted thread is not truncated.
- Defer endpoint default_state application to the runners when snapshot
  persistence is active, filling only keys missing from both the stored
  snapshot state and the request state so defaults never reset persisted
  Shared State.
- Always fold the turn's output into the persisted messages snapshot even when
  the outbound MESSAGES_SNAPSHOT event is suppressed for predictive tools
  without confirmation.
- Load the stored snapshot on workflow follow-up turns, reconstruct full
  thread history into the run input, and seed the snapshot builder with merged
  state so saving a new turn no longer replaces prior history.
- Move snapshot message reconstruction helpers to _run_common for reuse by the
  workflow runner; load stored agent snapshots on resume turns for state merge.
- Add endpoint regression tests for all four scenarios.

* fix(ag-ui): protect snapshot history on resume and harden suffix trust

- Prepend stored thread history when persisting snapshots for resume runs on
  both the agent and workflow paths, so a resumed interrupt no longer
  overwrites the stored thread with just the resume turn's output.
- Filter the incoming message suffix during thread reconstruction: only user
  turns and tool results answering backend-issued tool calls (stored tool
  calls or pending interrupts) may extend authoritative history. Client-forged
  assistant and tool messages are dropped and logged instead of being
  persisted and replayed.
- Close the workflow snapshot builder's tool-call group when a tool result or
  text message lands, so synthesized transcripts keep tool results adjacent to
  their tool_calls message and stay valid as provider replay history.
- Export DEFAULT_MAX_THREAD_SNAPSHOTS from agent_framework_ag_ui and expose
  SnapshotScopeResolver through the core ag_ui facade and stub.
- Add regression tests for agent and workflow resume history preservation,
  forged suffix rejection, builder tool-call grouping, and the export surface.

* fix(ag-ui): tolerate snapshot save failures and scope workflow cache

- Wrap snapshot_store.save() on both the agent and workflow paths so a
  transient store failure (timeout, connection refused) is logged instead of
  propagating. Previously a failing save converted an already-streamed
  successful run into RUN_ERROR, and on the workflow path emitted RUN_ERROR
  after RUN_FINISHED, violating the single-terminal-event invariant. The
  previous snapshot stays available for hydration.
- Key the workflow_factory instance cache by (snapshot_scope, thread_id). The
  Snapshot Scope is the authorization boundary, so the same thread id under
  different scopes no longer shares an in-memory workflow instance.
  clear_thread_workflow accepts an optional snapshot_scope and clears all
  scopes for the thread when omitted.
- Add tests: save-failure tolerance for agent and workflow endpoints,
  scope-isolated workflow cache, async snapshot_scope_resolver support, and
  in-memory store key validation errors.

* fix(ci): ignore all dotnet.microsoft.com links in linkspector

The existing ignore pattern only matched https://dotnet.microsoft.com/download,
but Microsoft sites insert a locale segment between host and path
(e.g. /en-us/download/dotnet/10.0), so localized links slip past the pattern
and get checked. dotnet.microsoft.com bot-blocks CI link checkers with
intermittent 403s across the whole site, which fails markdown-link-check on
unrelated pull requests since linkspector scans the entire repository.

Ignore the domain wholesale, matching how platform.openai.com is already
handled for the same reason. A 403 from bot blocking is indistinguishable
from a removed page, so the checker cannot produce a meaningful signal for
this domain either way.

* ag-ui: simplify raw_messages assignment and drop OrderedDict

- Replace list(cast(...)) with a typed annotation for raw_messages
  (_agent_run.py:866) per review suggestion
- Replace OrderedDict with a plain dict in InMemoryAGUIThreadSnapshotStore
  (_snapshots.py:136); regular dicts are insertion-order-safe since
  Python 3.7, so OrderedDict is unnecessary. Update _evict_oldest to use
  next(iter(...)) for FIFO removal instead of popitem(last=False).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address review feedback for #2458: review comment fixes

---------

Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-12 08:29:38 +00:00
Tao Chen 422f7e7382 Address comments 2026-06-11 16:54:42 -07:00
Yufeng HeandGitHub 4c1b9efa8c .NET: fix: filter filesystem checkpoint index by session (#6132)
* fix: filter filesystem checkpoint index by session

* fix: filter checkpoint index by parent

* .NET: preserve legacy checkpoint index discovery
2026-06-11 22:35:57 +00:00
Peter IbekweandGitHub e7937947d9 Python: Bug fix for declarative workflows (#6468)
* Fix declarative object parsing bug

* Remove unnecessary comment

* Address PR comments

* Address PR comments.

* Fix CI failures.
2026-06-11 22:34:15 +00:00
Tao Chen 96af0cd15a Merge branch 'main' into local-branch-python-add-reset-to-workflow 2026-06-11 15:28:27 -07:00
Tao Chen b0d0224ed4 Address comments 2026-06-11 15:27:36 -07:00
3d5421edc1 Python: Integrate shell tool into harness agent (#6451)
* Integrate shell tool into AgentHarness

* Validate shell_executor exposes as_function() with a clear TypeError

Addresses PR review feedback: a public factory should fail fast with an
actionable error rather than a cryptic AttributeError when an incompatible
shell_executor is supplied. Validation happens upfront, regardless of whether
the client supports shell tools.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Type shell harness params via TYPE_CHECKING import

Addresses PR review feedback: type shell_executor and
shell_environment_provider_options instead of Any, using a TYPE_CHECKING
import from agent_framework_tools.shell. The import never executes at
runtime, so there is no circular dependency, and the lazy runtime import of
ShellEnvironmentProvider is retained. Since ShellExecutor is a protocol
without as_function(), the validated getattr result is invoked directly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-11 20:51:59 +00:00
Tao Chen ed27241543 Add tests 2026-06-11 11:43:41 -07:00
8b0405de1b .NET: Fix CopySessionConfig() and CopyResumeSessionConfig() to preserve SessionConfig.Streaming value (#6463)
* Fix CopySessionConfig and CopyResumeSessionConfig ignoring Streaming value (#4732)

CopySessionConfig() and CopyResumeSessionConfig() hardcoded Streaming = true,
ignoring the caller's explicitly set SessionConfig.Streaming value. This made it
impossible to disable streaming when using AsAIAgent() with the GitHub Copilot SDK.

Changed both methods to use source.Streaming ?? true (and source?.Streaming ?? true
for the nullable overload), preserving the caller's value when set while maintaining
backward compatibility by defaulting to true when unset.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix non-streaming response path for SessionConfig.Streaming=false (#4732)

The config-copy fix (preserving Streaming=false via null-coalescing) was
already in place, but ConvertToAgentResponseUpdate(AssistantMessageEvent)
always emitted raw AIContent without text—assuming delta events had already
delivered it. When streaming is disabled there are no delta events, so the
assistant's final text was silently dropped.

Changes:
- Add isStreaming parameter to ConvertToAgentResponseUpdate for
  AssistantMessageEvent so it emits TextContent in non-streaming mode.
- Capture the resolved streaming flag in RunCoreStreamingAsync and pass
  it through the event subscription closure.
- Add/update unit tests for both streaming and non-streaming paths.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add test for null Data path in ConvertToAgentResponseUpdate (#4732)

Add a regression test covering the null-propagation path where
AssistantMessageEvent.Data is null. The production code already handles
this via ?. operators, but no test previously verified the 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>
2026-06-11 18:18:05 +00:00
Tao Chen 9da83347c8 Remove reset 2026-06-11 11:14:36 -07:00
df29af611c Python: Add tool approval middleware (#6414)
* Add Python tool approval middleware

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix tool approval restored state handling

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Gate hidden approvals on explicit approval responses

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Handle string inputs in approval replay scan

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Cover argument-scoped approval rules

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Refine tool approval state and budgets

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix tool approval PR CI failures

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Revert DevUI Aspire README link change

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-11 17:35:44 +00:00
c79f886dc3 .NET: Align Foundry sample environment variables and credentials. (#6422)
* dotnet: refresh Foundry sample guidance

Carry forward the still-relevant sample guidance and Foundry-specific documentation fixes from the old stacked sample migration work, adapted to the current repo layout and policy.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* dotnet: rename Foundry sample env vars

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* dotnet: remove persistent provider sample

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* dotnet: drop SAMPLE_GUIDELINES.md from this PR

Defer the guidelines doc and its cross-link to a follow-on PR to avoid broken-link failures in CI.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* dotnet: add DefaultAzureCredential warning to remaining samples

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* dotnet: address PR review feedback

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-11 17:26:00 +00:00
c9e2a490be Fix AzureFunctions integration tests — set FUNCTIONS_WORKER_RUNTIME (#6425)
Azure Functions Core Tools v4 can no longer auto-detect the worker
runtime when local.settings.json is absent. Add the required
FUNCTIONS_WORKER_RUNTIME=dotnet-isolated environment variable to
both StartFunctionApp helpers and re-enable the skipped tests.

Fixes: https://github.com/microsoft/agent-framework/issues/6402

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-11 15:09:55 +00:00
westeyandGitHub 12ce099165 .NET: Add LoopAgent capability for Harnesses (#6384)
* Add LoopAgent capability for Harnesses

* Address PR comments.

* Add support for returning user messages and response aggregation

* Support fresh context per iteration with input sessions via cloning

* Add ability to receive newly created sessions via callback

* Address PR comments

* Add judge criteria

* Address PR comments
2026-06-11 15:00:01 +00:00
8e1998ddcb .NET: Adds Valkey to chat message history - issue 5445 (#5542)
* Adds Valkey to chat message history

* Address review: switch to Valkey.Glide, add options class, remove context provider

- Switch from StackExchange.Redis to Valkey.Glide 1.1.0 (official Valkey .NET client)
- Extract optional params into ValkeyChatHistoryProviderOptions
- Add JsonSerializerOptions support, remove [RequiresUnreferencedCode]
- Make MaxMessages/MaxMessagesToRetrieve readonly via options
- Remove ValkeyContextProvider (overlaps with ChatHistoryMemoryProvider + MEVD)
- Remove ValkeyProviderScope (only used by context provider)
- Remove connection string constructors (caller manages IConnectionMultiplexer)
- Update samples to use new API and gpt-5.4-mini

* Use type-safe JsonSerializer overloads, remove suppress attributes

Use JsonSerializerOptions.GetTypeInfo() for Serialize/Deserialize calls
to enable NativeAOT/trimming compatibility without suppress attributes.
Default to AgentAbstractionsJsonUtilities.DefaultOptions when no options provided.

Signed-off-by: Matthias Howell <matthias.howell@improving.com>

* Update READMEs: remove context provider references

Remove ValkeyContextProvider and long-term memory references from sample
READMEs since the context provider was removed from this PR. Simplify
Valkey server requirements (no search module needed for chat history).

Signed-off-by: Matthias Howell <matthias.howell@improving.com>

* Apply suggestion from @westey-m

* Fix formatting (dotnet format)

Signed-off-by: Matthias Howell <matthias.howell@improving.com>

* Update dotnet/src/Microsoft.Agents.AI.Valkey/Microsoft.Agents.AI.Valkey.csproj

Co-authored-by: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>

---------

Signed-off-by: Matthias Howell <matthias.howell@improving.com>
Co-authored-by: Matthias Howell <matthias.howell@yoppworks.com>
Co-authored-by: westey <164392973+westey-m@users.noreply.github.com>
Co-authored-by: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
2026-06-11 13:18:00 +00:00
4149f24791 Python: [Generated by SRE Agent] Fix MCP allowed_tools empty list handling (#6296)
* Fix MCP allowed_tools empty list handling

When allowed_tools is set to an empty list [], the falsy check
'if not self.allowed_tools' incorrectly treats it as unconfigured
(same as None), causing all tools to be exposed. Change to an
explicit 'is None' check so that an empty list correctly results
in no tools being allowed.

Co-authored-by: Azure SRE Agent <noreply@microsoft.com>

* Clarify allowed_tools docstring: None vs [] semantics

Per Eduard's review on PR #6296: explicitly document that None exposes all tools and [] exposes none, across all four MCPTool / MCPStdioTool / MCPStreamableHTTPTool / MCPWebsocketTool docstrings.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* allowed_tools docstring: recommend load_tools=False for full disable

Per Eduard's follow-up on PR #6296: `load_tools=False` is the cleaner idiom when you don't want to expose any tools. Reframe `allowed_tools=[]` in the docstring as a runtime guard / inspection-only path and cross-reference `load_tools`.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Azure SRE Agent <noreply@microsoft.com>
Co-authored-by: Giles Odigwe <79032838+giles17@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-11 06:46:46 +00:00
Tao Chen 6534a739d0 Add create checkpoint to workflow interface 2026-06-10 22:20:10 -07:00
Tao Chen 0e3831192a Fix checkpoint ancestry bug 2026-06-10 16:36:15 -07:00
Peter IbekweGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
3753d938f5 .NET: Bug fixes for declarative workflows (#6427)
* declarative workflow approval flow fix

* Update mcp handler cache construction

* fix method argument.

* Update dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeFunctionToolExecutor.cs

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Fix identation

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-10 18:08:32 +00:00
60cc5ee4e4 .NET: Make GitHub.Copilot.SDK build targets reach transitive consumers (#6455) (#6457)
* .NET: Make GitHub.Copilot.SDK build targets reach transitive consumers (#6455)

Microsoft.Agents.AI.GitHub.Copilot now ships a buildTransitive/ bridge so
consumers who only reference this package (the normal use case) get the
GitHub.Copilot.SDK's CLI binary-download MSBuild targets executed at build
time. Without this, the SDK shipped its targets under build/ which NuGet
only auto-imports for projects with a direct PackageReference to the SDK,
so consumers of the adapter package got only the managed .dll, no
copilot.exe in their output, and a runtime InvalidOperationException on
the first RunAsync.

The bridge consists of two files under buildTransitive/:

* Microsoft.Agents.AI.GitHub.Copilot.props is generated at this package's
  pack time and pins the SDK version (from PackageVersion items in
  Directory.Packages.props) into _MicrosoftAgentsAICopilotSdkVersion.

* Microsoft.Agents.AI.GitHub.Copilot.targets is static and imports the
  SDK's own build/GitHub.Copilot.SDK.targets from the NuGet cache using
  the pinned version. The version-pin condition no-ops gracefully if the
  resolved SDK differs from what was baked in (e.g. consumer overrides
  the SDK version directly), so this is purely additive.

Verified by packing locally, restoring from a flat local feed, and
building a transitive-only consumer (PackageReference to MAF only, no
direct SDK ref). copilot.exe lands at bin/{cfg}/{tfm}/runtimes/{rid}/
native/copilot.exe as expected, matching the path the SDK's runtime
CopilotClient looks at.

Fixes #6455

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address Copilot review feedback (#6457)

- buildTransitive/.targets: compute the full SDK targets path with a single
  Path.Combine call into one property (_MicrosoftAgentsAICopilotSdkTargetsPath),
  used in both Project= and Exists() — no more split between Path.Combine for
  the directory and inline / separator for the file name.

- Split the version-defaulting Condition between the two files: the generated
  .props now just bakes the packaged SDK version into a dedicated property
  (_MicrosoftAgentsAICopilotSdkPackagedVersion), and the static .targets file
  is the single place that defaults _MicrosoftAgentsAICopilotSdkVersion to it.
  Removes the need for any MSBuild escape gymnastics in the pack-time string
  construction, and keeps the consumer override path the same.

- _GenerateBuildTransitiveProps now hangs off public BeforeTargets (Build, Pack)
  in addition to _GetPackageFiles, so the file is generated even without a
  full pack, and we're not solely dependent on an underscore-prefixed internal
  target. The <None Pack=true /> items live in a top-level ItemGroup so they
  are collected at evaluation time instead of being added from inside the
  Target.

End-to-end retested with a transitive-only consumer (PackageReference to MAF
only, no direct GitHub.Copilot.SDK ref): copilot.exe lands at
bin/Debug/net10.0/runtimes/win-x64/native/copilot.exe (141.8 MB) as before.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Tamir Dresher <tamirdresher@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-10 18:07:18 +00:00
dd29f9aa65 .NET: Hosted Agent Sample - Toolbox with various Auth (#5777) (#6018)
* .NET: Add Hosted-Toolbox-AuthPaths sample and auto-map /readiness with toolbox health gating (#5777)

Add a new hosted agent sample demonstrating five MCP tool authentication paths
(API key, agent MI, project MI, custom OAuth, literal token) via a Foundry Toolbox.

Package changes (Microsoft.Agents.AI.Foundry.Hosting):
- MapFoundryResponses now auto-maps GET /readiness via MapHealthChecks, idempotent
  across Tier 1/2 (AgentHost, already mapped) and Tier 3 (WebApplication, gap filled).
- AddFoundryResponses registers AddHealthChecks() so the pipeline is available.
- AddFoundryToolboxes registers FoundryToolboxHealthCheck on the /readiness aggregate,
  gating readiness on pre-registered toolbox startup outcome (per spec section 3.1).
- FoundryToolboxService now exposes StartupStatus and FailedToolboxNames properties.

New types:
- FoundryToolboxStartupStatus (public enum): Pending, Healthy, Failed, NoEndpoint.
- FoundryToolboxHealthCheck (internal IHealthCheck): adapts startup status to the
  AspNetCore HealthChecks pipeline with failed toolbox names in result data.

Tests:
- 3 new tests for /readiness auto-mapping (Tier 3 default, pre-mapped skip, idempotent).
- 4 new tests for FoundryToolboxHealthCheck (Pending, NoEndpoint, Failed, Healthy).
- 3 enhanced FoundryToolboxServiceTests with StartupStatus assertions.

* .NET: Align FoundryToolboxService with tools-integration-spec (#5777 Part A)

Bring Microsoft.Agents.AI.Foundry.Hosting's toolbox path into compliance with
tools-integration-spec.md sections 2-4, 6.3, and 9. Empirically validated
against tao-foundry-prj: the previous code (reading FOUNDRY_AGENT_TOOLSET_ENDPOINT,
which the platform never injects) silently registered zero tools in production.

Package changes (Microsoft.Agents.AI.Foundry.Hosting):

- FoundryToolboxService.StartAsync now derives the toolbox proxy base URL from
  the platform-injected FOUNDRY_PROJECT_ENDPOINT and constructs the per-toolbox
  URL as {FOUNDRY_PROJECT_ENDPOINT}/toolboxes/{name}/mcp?api-version={ApiVersion}
  per spec sections 2-3. The legacy FOUNDRY_AGENT_TOOLSET_ENDPOINT env var is
  removed outright (preview package, no production consumers).
- FoundryToolboxOptions.ApiVersion default flipped to 'v1' to match spec example.
- FoundryToolboxBearerTokenHandler always sends the mandatory
  Foundry-Features: Toolboxes=V1Preview header per spec section 2, merging any
  additional flags supplied via the FOUNDRY_AGENT_TOOLSET_FEATURES env var.
- FoundryToolboxBearerTokenHandler token scope changed from
  https://cognitiveservices.azure.com/.default to https://ai.azure.com/.default
  per spec section 4.
- FoundryToolboxBearerTokenHandler propagates W3C trace context (traceparent,
  tracestate, baggage) from Activity.Current per spec section 6.3.

Sample changes:

- Hosted-Toolbox-AuthPaths and Hosted-Toolbox Program.cs, README.md, and
  .env.example corrected to describe the actual env-var contract
  (FOUNDRY_PROJECT_ENDPOINT auto-injected; AZURE_AI_PROJECT_ENDPOINT as the
  local-dev fallback). Removes the misleading 'auto-injected by Foundry runtime'
  claims for FOUNDRY_AGENT_TOOLSET_ENDPOINT.
- Hosted-Toolbox-AuthPaths/agent.manifest.yaml declares the toolbox and model
  dependencies under resources[] per the AgentManifest schema so azd ai agent
  init users get them provisioned automatically.

Tests:

- 4 new FoundryToolboxServiceTests covering env-var derivation, EndpointOverride
  precedence, trailing-slash normalization, and the existing NoEndpoint behavior
  under the new env var name.
- 4 new FoundryToolboxBearerTokenHandlerTests covering token scope, mandatory
  feature header always present, header merging with override, no duplicate
  mandatory flag, trace context propagation from Activity.Current, and no
  override of caller-set traceparent.
- New FoundryProjectEndpointEnvFixture xUnit collection definition serializes
  env-var-mutating tests across FoundryToolboxServiceTests and
  FoundryToolboxHealthCheckTests, preventing parallel-execution races.
- FoundryToolboxHealthCheckTests adjusted for the new env var name.

* .NET: Drop ACA prereq from Hosted-Toolbox-AuthPaths README (#5777 Part B)

Empirically verified that any Azure Cognitive Services MCP endpoint already in
the Foundry project (e.g., a Language service MCP) accepts Entra tokens and can
serve Paths 2 and 3 without deploying a separate Azure MCP Server to ACA.

README updates:
- Step 0 rewritten: 'Identify an Entra-authenticated MCP target in your project'
  instead of 'Deploy Azure MCP Server to Azure Container Apps' (the original
  azmcp-foundry-aca-mi setup is now optional, not required).
- Auth-paths matrix updated to describe AAD-based connections targeting a
  Cognitive Services MCP URL (e.g., Language service) instead of an ACA URL.
- Step 2 connections table updated: the Entra ID category is now a single 'AAD'
  authType. The original 'Agent Identity' vs 'Project Managed Identity' as
  selectable connection sub-types is NOT exposed via the ARM control plane
  today; the platform selects the calling principal contextually. Both
  connections in the walkthrough share the same shape and target.
- Added an explicit RBAC note: the agent identity AND project MI must hold the
  required role (typically Cognitive Services User) on the target resource;
  without it the MCP server returns HTTP 401 even though the connection wiring
  is correct.
- Toolbox tool entries renamed lang_entra_agent / lang_entra_project to
  match the new connection names.

Empirical validation supporting these changes is captured in the session
plan.md (Part B addendum).

* .NET: Document correct connection shape for Hosted-Toolbox-AuthPaths Paths 2/3 (#5777)

Updates the sample README with the verified connection shape and RBAC procedure
for Microsoft Entra agent-identity and project-managed-identity MCP authentication:

- Connection authType values: AgenticIdentityToken (agent identity) and
  ProjectManagedIdentity (project MI), both with category=RemoteTool.
- Top-level audience property required; for Cognitive Services targets the value
  is https://cognitiveservices.azure.com.
- Connections created via ARM REST (the Foundry portal wizard does not yet
  expose these authTypes).
- RBAC grants target the project's shared agent identity blueprint principal
  (project.properties.agentIdentity.agentIdentityId) for Path 2 and the
  project's system-assigned MI (project.identity.principalId) for Path 3.
- Troubleshooting table updated with the audience-mismatch symptom and the
  startup-cache behavior of FoundryToolboxService.

* .NET: Drop Path 3 (project MI) and align with new agent model in Hosted-Toolbox-AuthPaths (#5777)

Updates the sample to use only the new Foundry agent object model and removes
the project managed identity path:

- Auth-path matrix reduced to four paths: key, Entra agent identity, custom
  OAuth, inline authorization. Project managed identity is moved into a note
  describing when it applies (multiple agents sharing access) rather than as
  a documented sample path.
- RBAC instructions reference the agent's own instance_identity.principal_id
  from the agent ARM resource (new agent object model) instead of the
  project's shared agent identity blueprint (legacy model).
- Step 2 (connections) creates only the AgenticIdentityToken connection.
- Step 3 (toolbox tools) lists four tool entries instead of five.
- Sample prompts and troubleshooting table updated to match.

* .NET: Restore Path 3 (project MI) to Hosted-Toolbox-AuthPaths matrix (#5777)

The sample's purpose is to enumerate every authentication path a Foundry toolbox
can drive, not to pick one. Path 3 belongs alongside the other four with
explicit guidance for when each path is the right choice.

- Path 3 (project managed identity, authType=ProjectManagedIdentity) restored
  to the matrix with a 'When to pick this' column.
- Step 2 (connections) provisions both lang-mcp-agent-id and lang-mcp-project-mi
  via ARM REST.
- Step 3 (toolbox) lists five tool entries (one per path).
- RBAC instructions cover both the agent's instance identity (Path 2) and the
  project's system-assigned MI (Path 3).
- Sample prompts include all five paths.
- Troubleshooting table updated accordingly.

* .NET: Fix duplicate line in Hosted-Toolbox-AuthPaths README (#5777)

* .NET: Fix broken markdown link to ToolCallingApprovalHostedAgentFixture (#5777)

* .NET: Fix relative path depth in markdown link (#5777)

* .NET: Address Copilot review feedback for #5777

- FoundryToolboxHealthCheck description: rename FOUNDRY_AGENT_TOOLSET_ENDPOINT
  → FOUNDRY_PROJECT_ENDPOINT (stale reference; operator-facing in /readiness body).
- FoundryToolboxStartupStatus.NoEndpoint XML doc: same rename.
- ServiceCollectionExtensions XML docs: same rename + URL shape update.
- Foundry.Hosting.IntegrationTests.TestContainer: remove explicit
  app.MapGet('/readiness') — now redundant + would conflict with the
  auto-mapped readiness route from MapFoundryResponses.
- Hosted-Toolbox-AuthPaths agent.manifest.yaml: parameterize TOOLBOX_NAME via
  {{TOOLBOX_NAME}} template substitution and declare it under parameters with a
  default of 'auth-paths-toolbox' so the README's 'use any name' guidance
  actually works for hosted deployments.

* .NET: Address Copilot review round 2 — fallback env + dedup + naming (#5777)

- FoundryToolboxService.StartAsync: fall back to AZURE_AI_PROJECT_ENDPOINT when
  FOUNDRY_PROJECT_ENDPOINT is absent. Matches the local-dev convention used by
  the samples and resolves the doc/code mismatch flagged in review.
- FoundryToolboxHealthCheck description updated for the fallback.
- AddFoundryToolboxes: guard against duplicate health-check registration via an
  explicit name-uniqueness check on HealthCheckServiceOptions.Registrations.
  AddCheck<T>(name, ...) does not dedupe by name, so repeated AddFoundryToolboxes
  calls would have registered multiple instances.
- FoundryToolboxOptions.EndpointOverride doc: clarify URL becomes
  {EndpointOverride}/toolboxes/{name}/mcp (was missing /toolboxes/ segment).
- Hosted-Toolbox sample (Program.cs + README): switch FOUNDRY_TOOLBOX_NAME to
  TOOLBOX_NAME (the FOUNDRY_* prefix is reserved by the platform), default
  changed from 'my-toolset' to 'my-toolbox', terminology updated from 'Toolset'
  to 'Toolbox'.
- FoundryToolboxServiceTests: 2 test renames to reflect what they actually
  assert (StartupStatus + FailedToolboxNames, not URL shape directly).
- Tests adjusted to clear both env vars in NoEndpoint scenarios.

* .NET: Fix stale NoEndpoint XML doc and misleading test comment (#5777)

Update FoundryToolboxStartupStatus.NoEndpoint XML doc to mention both
FOUNDRY_PROJECT_ENDPOINT and AZURE_AI_PROJECT_ENDPOINT (the service
checks both since the fallback was added).

Fix test comment that claimed URL derivation validation when the test
only asserts on StartupStatus and FailedToolboxNames.

* Remove OAuth consent path from AuthPaths sample, keep four working auth paths

The interactive OAuth identity passthrough path needs a protocol gap closed in the
hosting package (the proprietary oauth_consent_request item is not representable
through the OpenAI/MEAI abstractions), so it is deferred to a separate spike branch.

This strips the OAuth path from the AuthPaths sample, the companion REPL client, the
agent manifest, and the docs, then renumbers the inline Authorization path so the
sample teaches four contiguous paths: API key via connection, Entra agent identity,
Entra project managed identity, and inline Authorization (anti-pattern).

Package code is unchanged; the consent infrastructure already present in main stays
as baseline. Both samples build with --warnaserror and all 246 hosting unit tests pass.

* .NET: Drop project MI auth path and dedicated client from Hosted-Toolbox-AuthPaths (#5777)

Live validation against tao-foundry-prj showed the ProjectManagedIdentity
path failing with an unresolved token audience 401, so the sample now ships
three working auth paths instead of four: connection key, agent managed
identity, and inline Authorization.

Changes:
- Remove the project managed identity path from the AuthPaths sample matrix,
  prerequisites, connections, toolbox table, prompts, Program.cs instructions
  and agent.manifest.yaml.
- Delete the near duplicate Hosted-Toolbox-AuthPaths-Client project and remove
  it from the solution. The README now drives the agent with the shared
  SimpleAgent REPL via AsAIAgent(agentEndpoint).
- Correct the troubleshooting note: the Foundry toolbox tools/list is all or
  nothing, so one bad source returns -32007, fails startup, and returns 424
  for every path. Add the allowed_tools caveat that names must match the
  upstream server.
- Mark the toolbox startup status and health check experimental under
  AgentsAIExperiments (MAAI001) instead of AIOpenAIResponses, and update the
  package NoWarn set accordingly.

* .NET: Address PR review nits for Hosted-Toolbox-AuthPaths (#5777)

- Remove duplicated NU1903 comment in Foundry.Hosting csproj.

- Fix stale 'four-tool' cross-links in Hosted-Toolbox and Hosted-McpTools READMEs to describe the three-path toolbox driven by the shared SimpleAgent REPL.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* .NET: Address toolbox startup-status review feedback (#5777)

- Rename FoundryToolboxStartupStatus.Failed to Unhealthy so it is the proper opposite of Healthy, and clarify the doc comment covers the partial-failure case.

- Raise the missing-endpoint toolbox log from Information to Warning, since enabling toolboxes is an explicit opt-in and a silently disabled toolbox warrants a higher-severity signal.

- Update unit tests and the AuthPaths README troubleshooting row accordingly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* .NET: Reword toolbox-wiring comment to avoid hosting-layer internals (#5777)

Address PR review feedback: explain how a Foundry Toolbox is attached using the public API (AddFoundryToolboxes vs the CreateHostedMcpToolbox marker) and observable behavior, instead of naming the internal AgentFrameworkResponseHandler type and FoundryToolboxService.Tools property.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-10 16:49:48 +00:00
a5f4e0078e .NET: Fix .NET Copilot integration tests for SDK v1.0.0 (#6424)
* Fix .NET Copilot integration tests for SDK v1.0.0

- Remove hard-skip in favor of runtime Assert.Skip when COPILOT_GITHUB_TOKEN is not set
- Add [Trait("Category", "Integration")] for CI filtering
- Fix FunctionTool test: use explicit SessionConfig with Tools, OnPermissionRequest, and SystemMessage
- Mark RemoteMcp test as IntegrationDisabled (requires OAuth flow)
- Create explicit sessions in all tests and delete after each (cleanup)
- Remove unused System.Diagnostics import
- Simplify SkipIfCopilotNotConfigured to only check env var

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address review: use try/finally for session cleanup, IsNullOrWhiteSpace

- Wrap act/assert in try/finally so sessions are always deleted even on failure
- Use IsNullOrWhiteSpace instead of IsNullOrEmpty for token check

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add COPILOT_GITHUB_TOKEN to .NET integration test workflow

The Copilot SDK runtime reads this env var directly for authentication.
No Node.js/npm install needed - the SDK downloads the CLI binary at build time.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-10 15:41:48 +00:00
westeyandGitHub 3c0c12cd46 .NET: Update release version for 2026-06-10 release and switch GH.CP Agent to RC (#6454)
* Update release version for 2026-06-10 release

* Switch GitHub.Copilot to RC
2026-06-10 15:26:23 +00:00
8dde9ef627 Python: HarnessAgent: Disable compaction when max tokens not provided (#6410)
* HarnessAgent: Disable compaction when max tokens not provided

* Fix regression.

* Address PR comments

* Require max_output_tokens to be positive

Reject max_output_tokens=0 (must be positive), mirroring
max_context_window_tokens. Addresses PR review feedback.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-10 13:57:23 +00:00
93cbf6b3f0 Python: Parse MCP CallToolResult.structuredContent field to prevent tool results returning None (#6421)
* Parse structuredContent from MCP CallToolResult (#3313)

The _parse_tool_result_from_mcp method only iterated over the content
field from CallToolResult, ignoring the structuredContent field entirely.
MCP servers that return JSON data via structuredContent (e.g., Power BI
MCP) appeared to return None.

Add handling for structuredContent: when present, serialize it as JSON
text and append it to the result list. This preserves the data for the
LLM while maintaining backward compatibility with existing behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: Parse MCP CallToolResult.structuredContent field to prevent tool results returning None

Fixes #3313

* Address review feedback: add default=str to json.dumps and remove .checkpoints/

- Add default=str to json.dumps for structuredContent serialization so
  non-JSON-serializable values (e.g. bytes) degrade gracefully instead
  of raising TypeError
- Remove all .checkpoints/ runtime artifacts from the repository
- Add **/.checkpoints/ to .gitignore to prevent future accidental commits
- Add test for non-serializable structuredContent values

Fixes #3313

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address review feedback for #3313: Python: MCP CallToolResult.structuredContent field is not parsed, causing tool results to return None

---------

Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-10 12:51:09 +00:00
9a56bc9f16 Python: [BREAKING] Add sampling guardrails to MCP tools (#6413)
* Add sampling guardrails to MCP tools

Add approval, token, and request-count controls to the MCP sampling
callback used when an MCPTool is configured with a chat client.

- Add `sampling_approval_callback`, `sampling_max_tokens`, and
  `sampling_max_requests` parameters to `MCPTool` and its
  `MCPStdioTool`, `MCPStreamableHTTPTool`, and `MCPWebsocketTool`
  subclasses, positioned directly after `client`.
- Gate each server-initiated `sampling/createMessage` request behind the
  approval callback, which denies by default when no callback is provided.
- Clamp the requested `maxTokens` to `sampling_max_tokens` and enforce a
  per-session request count via `sampling_max_requests`.
- Log incoming sampling requests at WARNING level (counts only).
- Export `SamplingApprovalCallback` from the public API.
- Add tests, a sample, and documentation updates.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Make sampling denial message context-aware

Distinguish the deny-by-default case (no approval callback configured)
from an explicit denial by a configured `sampling_approval_callback`, so
the returned ErrorData message is accurate for callback-driven denials
and exceptions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-10 10:17:36 +00:00
CopilotGitHubcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>Roger Barreto
cea83bd8d5 .NET: Bump Microsoft.Extensions.AI packages to 10.6.0, align transitive dependency floor, and update Merge Gatekeeper ignores (#6148)
* Bump Microsoft.Extensions.AI packages to 10.6.0

* Align transitive package versions for Microsoft.Extensions.AI 10.6.0

* Ignore external review check in Merge Gatekeeper

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
2026-06-10 10:02:22 +00:00
7ae73a68d6 Remove broken Atomic Agents docs link (#6442)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-10 09:07:51 +00:00
CopilotGitHubcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
3daed114ee Python: bump package versions for 1.8.1 release (#6420)
* Python: bump package versions for 1.8.1 release

* Python: bump agent-framework-foundry-hosting for 1.8.1 release

* Python: bump ag-ui and azurefunctions for 1.8.1 release

* Remove incorrect agent-framework-foundry changelog entry for #6259

* Add [1.8.1] changelog compare link and update [Unreleased] base

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-06-09 21:27:42 +00:00
5e097276a0 .NET: Add Foundry Deployment docs to HA sample READMEs (#6365)
* Add 'Deploying to Foundry (azd spec)' sections to all Foundry hosted agent samples

This commit adds comprehensive deployment documentation to all 13 .NET Foundry hosted agent samples that were missing it. Each sample now includes:

- Instructions to initialize an azd project from the sample's agent.manifest.yaml
- Steps to deploy using 'azd deploy'
- Example environment variable overrides for customization
- Link to the official Foundry deployment guide

Samples updated:
- Hosted-LocalTools
- Hosted-Files
- Hosted-FoundryAgent
- Hosted-McpTools
- Hosted-Observability
- Hosted-MemoryAgent
- Hosted-TextRag
- Hosted-ToolboxMcpSkills
- Hosted-AzureSearchRag
- Hosted-AgentSkills
- Hosted-Workflow-Handoff
- Hosted-Workflow-Simple
- Hosted-Invocations-EchoAgent

Each section includes the correct agent name from the sample's manifest and points to the correct GitHub URL for initializing the azd project.

Fixes: https://github.com/microsoft/agent-framework/issues/6308

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* docs(samples): fix Foundry hosted README consistency

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* docs(samples): address PR 6365 README review comments

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Ben Thomas <25218250+alliscode@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-09 19:40:36 +00:00
Tao Chen 47012f1dcf Address comments 2026-06-09 11:02:17 -07:00
383d551b86 Purview: Parallelize PSPC cold-cache scope refresh (#5832)
* Parallelize Purview PSPC cold cache path

* Cache Purview payment-required state for scope refresh

* Cache Purview payment-required state for scope refresh

* Align Purview policy action dedupe and 402 caching

 Deduplicate combined policy actions by action and restriction action so restriction-only actions are preserved
without duplicating identical entries. Cache tenant-level payment-required state from background scope refresh so
subsequent calls short-circuit consistently.

* .NET: Implement best-effort caching for background job scope retrieval and add unit tests for cache write failures

* Purview - feat: Enhance ScopedContentProcessor to queue ContentActivityJob when no applicable scopes are found and update related tests

* docs: Update purview package README and AGENTS documentation to reflect caching optimizations and policy enforcement scenarios

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-09 18:01:21 +00:00
Hasan GhomiandGitHub 2a345e5d3b .NET: Fix Magentic to share agent replies across team (#6222)
* Fix Magentic to share agent replies across team

The per-round instruction was sent untargeted (fan-out delivered it to
every participant) and replies were never relayed, so a later speaker saw
the prior speaker's instruction but not its response - inverted from
GroupChatHost and the Python reference.

- Target the instruction at the selected speaker only.
- Broadcast each reply to the other participants (buffered, no TurnToken),
  excluding the responder via _currentSpeakerExecutorId, mirroring
  GroupChatHost.
- Persist _currentSpeakerExecutorId across checkpoints.
- Add a regression test.

* Address review feedback: null-guard, explicit checkpoint key, drop vacuous assertion

* Address review feedback: centralize checkpoint keys, clear current speaker

- Move CurrentSpeakerStateKey into MagenticConstants as
  nameof(CurrentSpeakerStateKey)
- Clear _currentSpeakerExecutorId in ResetAndReplanAsync and
  PrepareFinalAnswerAsync so a checkpoint taken in those windows does not
  persist a stale speaker
- Add UTF-8 BOM to RecordingEchoAgent.cs to satisfy the format check.
2026-06-09 17:00:42 +00:00
632f67b92e Python: [Generated by SRE Agent] docs: clarify checkpoint storage security model and deserialization trust boundaries (#6295)
* docs: clarify checkpoint storage security model and deserialization trust boundaries

Add Security Model documentation sections to the checkpoint encoding and
Azure Functions serialization modules explaining:
- Checkpoint storage is a trusted data source requiring access controls
- The RestrictedUnpickler allowlist is defense-in-depth, not a security boundary
- Developer responsibilities for securing storage backends
- Guidance on using allowed_types and strip_pickle_markers

Co-authored-by: Azure SRE Agent <noreply@microsoft.com>

* Apply suggestions from code review

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Azure SRE Agent <noreply@microsoft.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-09 16:53:48 +00:00
Shawn HenryandGitHub 5e6eb6f121 New logo in banner (#6380) 2026-06-09 16:41:28 +00:00
Tao Chen 4e623d561f Merge branch 'main' into local-branch-python-add-reset-to-workflow 2026-06-09 09:38:45 -07:00
Tao Chen 6eb85477fc Remove lifecycle flag 2026-06-09 09:27:21 -07:00
Shawn HenryandGitHub dbfacbfc4a New Microsoft Agent Framework logos (#6378) 2026-06-09 15:56:56 +00:00
29cec0d27b Python: fix: use getattr for non-OpenAI provider response compatibility (#6270)
* fix: use getattr for non-OpenAI provider response compatibility

Fixes #6234
Fixes #6235

Use getattr with None fallback for system_fingerprint and output
attributes to prevent AttributeError when non-OpenAI providers
return response objects without these fields.

* fix: use typed variable for response output to satisfy pyright

Fixes #6235

Use getattr with None fallback for the output attribute, and assign
to a typed list variable before the match statement to help pyright
narrow the response item types correctly.

* fix: rename response_outputs to avoid name collision with case-block variable

Fixes #6235

Rename outputs to response_outputs on line 1974 to avoid mypy error
about conflicting variable names in the match statement's case blocks.
Also use list[Any] for explicit generic type annotation.

* fix: use cast(list[Any]) for response output to satisfy pyright

Fixes #6235

The getattr() call returns Unknown type which pyright cannot narrow
in the match statement. Use an explicit cast to list[Any].

* fix: use hasattr guard instead of getattr for response.output

Fixes #6235

Using hasattr(response, 'output') and then accessing response.output
directly gives pyright enough type information to verify the match
statement exhaustiveness. This avoids the cast(list[Any]) approach
which pyright still flagged as partially unknown.

* fix: use ternary operator for response_outputs assignment

Replace if-else block with ternary expression to satisfy ruff SIM108 lint rule.
This fixes the Package Checks (3.11) CI failure.

* fix: use ternary with cast for ruff SIM108 and pyright type safety

Replace if-else block with ternary expression using cast(list[Any], ...)
to satisfy:
- ruff SIM108 (use ternary instead of if-else)
- ruff E501 (line length < 120)
- pyright type narrowing (cast preserves type info lost in ternary)

All local checks pass: ruff check, ruff format, pyright, 298 tests.

* fix: replace hasattr+cast with try/except to preserve pyright types

---------

Co-authored-by: Tao Chen <taochen@microsoft.com>
2026-06-09 15:17:39 +00:00
96d242fa7f .NET: Remove required token params from HarnessAgent, make compaction opt-in (#6409)
* Move token params from HarnessAgent constructor to options

Remove the required maxContextWindowTokens and maxOutputTokens
constructor parameters from HarnessAgent and AsHarnessAgent, replacing
them with optional MaxContextWindowTokens and MaxOutputTokens properties
on HarnessAgentOptions.

When both values are provided, compaction is enabled as before (in-loop
CompactionProvider and chat reducer on the default InMemoryChatHistory
Provider). When either is null, compaction is disabled entirely, making
it opt-in.

New constructor: HarnessAgent(IChatClient, HarnessAgentOptions?,
ILoggerFactory?, IServiceProvider?)

Closes #6333

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Improving comments.

* feat: Add custom CompactionStrategy and DisableCompaction to HarnessAgentOptions

Allow users to provide their own CompactionStrategy via options, with
a clear priority system:
1. DisableCompaction=true: no compaction regardless of other settings
2. Custom CompactionStrategy provided: use it (token params ignored)
3. Both MaxContextWindowTokens and MaxOutputTokens set: default strategy
4. Otherwise: no compaction

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: Address PR review comments on compaction opt-in

- Update chatClient param XML doc to reflect compaction is opt-in
- Strengthen compaction tests to assert ChatReducer is null/not-null
  rather than just asserting construction succeeds

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-09 13:06:00 +00:00
9486c76ef8 .NET: Add Reasoning to ChatClientAgent ChatOptions merging (#5463)
* Add reasoning option to request chat options in ChatClientAgent

* Add tests for ChatOptions reasoning merging in ChatClientAgent

---------

Co-authored-by: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
2026-06-09 11:25:31 +00:00
caa75f7cdd Python: Add Foundry Toolbox MCP skills hosted agent sample (#6363)
* Add 12_foundry_toolbox_mcp_skills hosted agent sample

Demonstrates using MCPSkillsSource with a Foundry Toolbox MCP endpoint
to discover and serve skills via SkillsProvider (progressive disclosure).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix env var reference in README and reuse local var in main.py

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Require AZURE_AI_MODEL_DEPLOYMENT_NAME and use placeholder in .env.example

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Document Toolbox MCP skills vs Foundry Skills in sample README

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Reference 12_foundry_toolbox_mcp_skills in parent README

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: SergeyMenshykh <SergeMenshikh@outlook.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-09 08:38:19 +00:00
cfb033e5d4 Python: Filter MCP tool kwargs to declared params via allowlist (#6399)
* Filter MCP tool kwargs to declared params via allowlist

Previously MCPTool combined framework runtime kwargs (from
FunctionInvocationContext.kwargs) with the LLM-supplied arguments and
stripped only a hardcoded denylist of known framework keys before
forwarding to the MCP server. Any new framework-injected kwarg leaked to
the server unless the denylist was updated.

Switch to an allowlist built from each tool's declared parameters
(inputSchema.properties). Only declared params are forwarded; everything
else is stripped. Add an `additional_tool_argument_names` constructor
argument so users can opt extra names back in, globally (Sequence[str])
and/or per remote tool name (Mapping with reserved "*" global key). The
existing denylist is kept as a safety net for framework-named params a
server declares in its schema; explicitly opted-in extras always win. The
reserved _meta handling is unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address MCP allowlist review comments and fix reload arg loss

- Fix pyright reportUnknownArgumentType in _load_tools (cast schema properties).
- Register declared param names before the existing-tool skip guard so that
  tool-list reloads preserve the allowlist for already-loaded tools (previously
  unchanged tools silently dropped all declared args after a background reload).
- Handle bare-string values in an additional_tool_argument_names mapping instead
  of iterating their characters.
- Clarify the framework denylist comment: explicit extras override the denylist.
- Make the extras-override-denylist test unambiguous (opt in a denylisted name).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-09 07:37:11 +00:00
Yufeng HeandGitHub d222079df9 .NET: fix: preserve AG-UI session history (#5904)
* fix: preserve AG-UI session history

* refactor: use static AG-UI provider check
2026-06-09 07:06:13 +00:00
e89e745bc0 Python: feat(claude): bump claude-agent-sdk to 0.2.87 (#6248)
* feat(claude): bump claude-agent-sdk to 0.2.87

Upgrade claude-agent-sdk dependency from >=0.1.36,<0.1.49 to >=0.2.87,<0.3.

Changes:
- Bump version pin in pyproject.toml
- Add 'xhigh' effort level to ClaudeAgentOptions (Opus 4.7 specific)
- Expose new upstream SDK options: skills, session_id, task_budget,
  include_hook_events, strict_mcp_config, continue_conversation,
  fork_session
- Add TaskBudget type import
- Update uv.lock

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: lower claude-agent-sdk floor to >=0.1.36

Keep the lower bound at 0.1.36 since the 0.1→0.2 transition was additive
and our code works on older versions as long as new options aren't used.
This avoids forcing unnecessary upgrades on existing users.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: replace TaskBudget import with inline type for SDK compat

TaskBudget was added in claude-agent-sdk 0.2.93 but does not exist in
0.2.87. Use dict[str, int] inline type instead so type checking passes
against 0.2.87. Lock file pinned to 0.2.87.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-09 06:01:55 +00:00
bad05a2bdc Python: Harness console for python (#6312)
* Add initial harness console for python

* Add textual to project

* Add planning and approval flows with list selector

* Address PR comments

* Fix list selection bug

* Fix PR #6312 round 2 review comments

- Escape untrusted agent text with rich.markup.escape() in observers
  (text_output, planning_output, reasoning_display) to prevent markup injection
- Remove non-functional 'Always approve' choices from tool_approval.py
  (framework lacks CreateAlwaysApproveToolResponse support)
- Remove textual from root pyproject.toml dev deps (sample-specific)
- Add PEP 723 inline script metadata to harness_research.py
- Narrow except Exception to except NoMatches in list_selection.py

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix build error

* Fix build errors

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-09 05:48:35 +00:00
7e0767a0a0 Python: Fix per-service-call history persistence with server-storing clients (#6310)
* Fix per-service-call history persistence with server-storing clients

When an Agent set require_per_service_call_history_persistence=True together
with a HistoryProvider, and the chat client stored history server-side by
default (e.g. OpenAIChatClient, STORES_BY_DEFAULT=True), the external history
provider was silently never persisted.

Unify persistence on the per-service-call middleware: when the flag is set and
a HistoryProvider exists, the middleware is always installed and owns
persistence. service_stores_history now only selects middleware behavior:
- service does not store: load providers and drive the function loop with a
  local sentinel conversation id, or
- service stores: skip loading (the service owns history) and persist each
  service call while the real conversation id flows through.

Also rationalize chat-options handling in _prepare_run_context:
- _merge_options now skips None overrides and strips remaining None values, so
  an unset `store` is never forwarded and the service decides its own default.
- Resolve `store` and `conversation_id` once from a single combined view
  (effective_options) instead of probing both default and runtime dicts; the
  auto-injection and per-service-call resolution now agree on conversation_id.

Fixes #5798

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Correct as_agent() docstring: persistence is per service call, not once per run

Address PR review: when the client stores history server-side, the
per-service-call middleware still persists after each model call; only
provider loading is skipped. The previous "persist once per run()" wording
contradicted the implementation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address PR review: docs, missing-conversation-id warning, and tests

- Clarify that require_per_service_call_history_persistence is a no-op when no
  HistoryProvider is present (docstrings in _agents.py and _clients.py).
- Warn on every service call when the client stores history server-side but
  returns no conversation_id, so the (uncommon) loss of cross-turn resumability
  cannot fail silently.
- Add tests: storing client + existing conversation_id does not raise and the id
  propagates; two runs on the same session keep persisting with a stable
  service_session_id and no provider loading; storing-without-conversation-id
  warns per call.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-09 05:47:57 +00:00
Tao Chen 568afdd293 Fix tests and address comments 2026-06-08 16:30:59 -07:00
Tao Chen 598ad231ba Add sample 2026-06-08 16:07:26 -07:00
af772997af .NET: [BREAKING] Migrate .NET GitHub Copilot SDK to v1.0.0 (#6381)
* Migrate .NET GitHub Copilot SDK from 1.0.0-beta.2 to 1.0.0

- Update namespace from GitHub.Copilot.SDK to GitHub.Copilot
- Replace PermissionRequestResult/PermissionRequestResultKind with PermissionDecision
- Remove ConnectionState check (StartAsync is now idempotent)
- Rename ConfigDir to ConfigDirectory
- Use SessionConfig.Clone() for CopySessionConfig
- Update Tools type from List<AIFunction> to List<AIFunctionDeclaration>
- Rename UserMessageAttachmentFile to AttachmentFile
- Update usage data types (CacheWriteTokens: long, Duration: TimeSpan)
- Add GHCP001 NoWarn for experimental SDK APIs (matches framework convention)
- Specify type argument on CopilotSession.On<SessionEvent>()

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix formatting: remove unused using directive

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Skip AzureFunctions SamplesValidation tests pending func tools fix

Azure Functions Core Tools v4 can no longer auto-detect the worker
runtime in CI (local.settings.json is gitignored). All 7 active
SamplesValidation tests fail with 'Worker runtime cannot be None'.

Tracked by: https://github.com/microsoft/agent-framework/issues/6402

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Skip additional failing integration tests in CI

WorkflowSamplesValidation (5 tests): same func tools issue as #6402.
WorkflowConsoleAppSamplesValidation (4 tests): KeyNotFoundException
during workflow execution, tracked by #6404.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-08 22:34:05 +00:00
Tao Chen 5910a6f869 Add reset to hosted workflow 2026-06-08 14:57:01 -07:00
Tao Chen 65522bdbee Add reset to workflow 2026-06-08 13:42:42 -07:00
westeyandGitHub b343625c1f .NET: Add approval bypassing to harness as the default (#6387)
* Add approval bypassing to harness as a default

* Add tests

* Address PR comments.
2026-06-08 17:50:41 +00:00
Evan MattsonandGitHub 9bc7b27813 Match AG-UI approval responses to requested arguments (#6376) 2026-06-08 16:33:16 +00:00
westeyandGitHub 6a2efeae7c .NET: [BREAKING] Fix hosting bugs (#6388)
* Fix hosting bugs

* Address PR comments
2026-06-08 16:17:54 +00:00
Vedant SonaniandGitHub 6169df04cb Python: fix(mem0): isolate entity retrieval and correct app_id payload (#6242)
* fix(mem0): parallel memory retrieval logic and strict type compliance

* fix(mem0): align parallel retrieval types for pyright and mypy

* fix(mem0): handle asyncio.CancelledError in search response and update test description

* fix(mem0): improve error handling for asyncio.CancelledError and update test names for clarity

* fix(mem0): improve retrieval response handling
2026-06-08 13:50:23 +00:00
Peter IbekweandGitHub 331201294b .NET: Fix single-column value unwrap in declarative workflow (#6367)
* Fix single-column value unwrap in declarative workflow

* Added more tests
2026-06-08 11:37:12 +00:00
Tao Chen c5e6a7797f Move runner state management out of Workflow 2026-06-05 16:29:19 -07:00
Yufeng HeandGitHub fa9e086576 fix: preserve foreach record values (#6208) 2026-06-05 22:01:59 +00:00
dcc218dbac Python: feat(python): Add MCP client OTel spans per GenAI semantic conventions (#6349)
* feat(python): Add MCP client OTel spans per GenAI semantic conventions

Implement MCP client spans per the OTel GenAI Semantic Conventions for MCP
(https://opentelemetry.io/docs/specs/semconv/gen-ai/mcp/#client).

Operations instrumented:
- initialize: CLIENT span capturing MCP session setup
- tools/list: CLIENT span for tool listing (per-page)
- prompts/list: CLIENT span for prompt listing (per-page)
- tools/call: CLIENT span (nested under execute_tool when called via FunctionTool)
- prompts/get: CLIENT span

Span attributes follow the MCP semantic conventions:
- Required: mcp.method.name
- Conditional: error.type, gen_ai.tool.name, gen_ai.prompt.name
- Recommended: gen_ai.operation.name, mcp.protocol.version, mcp.session.id,
  network.transport, server.address, server.port

Transport-specific attributes per subclass:
- MCPStdioTool: network.transport=pipe
- MCPStreamableHTTPTool: network.transport=tcp, network.protocol.name=http
- MCPWebsocketTool: network.transport=tcp, network.protocol.name=websocket

All span creation gated behind OBSERVABILITY_SETTINGS.ENABLED.

Closes #3624
Closes #4697

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor: simplify MCP spans — remove enrichment logic and protocol version caching

- Always create nested CLIENT spans for tools/call instead of enriching
  the parent execute_tool span
- Remove _ACTIVE_TOOL_EXECUTION_SPAN contextvar (no longer needed)
- Remove enrich_span_with_mcp_attributes() helper
- Remove _otel_error_type preservation in FunctionTool.invoke()
- Remove _mcp_protocol_version instance variable; protocol version is
  only set on the initialize span where it is available

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Refine copilot solution

* fix: enable automatic exception recording on MCP spans

Remove record_exception=False and set_status_on_exception=False from
create_mcp_client_span. Let OTel handle exception recording and status
setting automatically. The manual set_mcp_span_error calls for tools/call
still correctly set error.type (which OTel's automatic handling doesn't
touch), so tool_error is preserved.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Reduce number of lines

* Add comment to sample

* test: address PR review comments on MCP observability tests

- Fix initialize test to call mocked session.initialize() and read
  protocolVersion from the result instead of hardcoding it
- Add tools/call McpError error-path test
- Add prompts/get McpError error-path test

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix export error

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-05 19:23:01 +00:00
6bd2cfec03 .NET: [BREAKING] Add auto-approval rules (heuristics) to ToolApprovalAgent (#6335)
* Add support for approving tools via heuristic rules

* Address PR comments

* Address PR comments

* Apply suggestion from @SergeyMenshykh

Co-authored-by: SergeyMenshykh <68852919+SergeyMenshykh@users.noreply.github.com>

---------

Co-authored-by: SergeyMenshykh <68852919+SergeyMenshykh@users.noreply.github.com>
2026-06-05 18:43:07 +01:00
westeyandGitHub ab8ba8fc61 .NET: Allow storage of auto-approved functions (#4950)
* Allow storage of auto-approved functions

* Address PR comments
2026-06-05 18:42:21 +01:00
Tao ChenandGitHub 9cafd7e58b Python: Refactor workflow as agent pending request handling (#6259)
* WIP: Refactor Workflow as agent pending request handling

* WIP: debugging empty message bug

* Working: Workflow as agent with function approval

* Address Copilot comments

* Fix mypy

* Address comments and fix pipeline

* Request info non function approval now becomes function call

* Revert uv.lock

* Fix mypy

* Bump min version of azure-ai-project

* Remove RequestInfoFunctionArgs

* fix tests

* Fix failing tests

* Fix sample
2026-06-05 17:23:19 +00:00
d5335fbeae Python (fix:gemini): make Gemini honor declarative outputSchema, not just JSON mode (#5893)
* fix(gemini): preserve schema response_format

* fix(gemini): satisfy pyright strict in response schema extraction

Cast Any-narrowed mappings to Mapping[str, Any] in the structured-output
schema helpers so pyright strict no longer reports partially-unknown
member, argument, and variable types. Pass response_format["format"]
straight into the recursive extractor, which already guards non-mapping
inputs. No behavior change.

* fix(gemini): use Sequence[object] cast to satisfy both mypy and pyright

The Sequence[Any] cast pyright strict needs to know the loop element type
is reported as a redundant-cast by mypy, which already narrows the
isinstance branch to Sequence[Any]. Cast to Sequence[object] instead:
pyright gets a fully known element type and mypy no longer sees an
identical-type cast. No behavior change.

---------

Co-authored-by: Evan Mattson <evan.mattson@microsoft.com>
2026-06-05 15:17:51 +00:00
bf4ad48cf2 Python: MCP long-running task support in Python (#6319)
* MCP long-running task support in Python

* Fix pyupgrade and AGENTS.md reconnect description

- pyupgrade: drop forward-reference string annotations in _mcp.py (Python 3.10+ resolves them natively now that MCPTaskOptions is defined before use).

- AGENTS.md: align reconnect description with current behavior. Phase 1 (initial tools/call) does NOT retry on connection loss; raises 'connection lost; task state unknown' instead, so a server that accepted the request but lost the response cannot start the operation twice. Phase 2 (tasks/get / tasks/result) still reconnects once against the same task_id.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix bandit nosec marker for CI pipeline

* Address PR feedbacks

* Clarifiied comments and addressed more PR feedbacks.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-05 00:04:55 +00:00
01fc518b29 Python: bump package versions for 1.8.0 release (#6351)
- Released cohort (core, openai, foundry, root): 1.7.0 -> 1.8.0
- agent-framework-github-copilot: promote to RC (1.0.0rc1)
- agent-framework-orchestrations: rc2 -> rc3 (bug fix)
- Beta/alpha packages with changes: a2a, anthropic, azurefunctions, bedrock,
  foundry-hosting, mistral bumped to new date stamp (260604)
- Inter-package dependency bounds updated for changed packages
- CHANGELOG.md and PACKAGE_STATUS.md updated

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-04 23:03:24 +00:00
f3c3efed43 Python: Add GitHub Copilot integration tests to CI workflows (#6346)
Add a dedicated integration test job for the github_copilot package to both
python-integration-tests.yml and python-merge-tests.yml.

The job:
- Runs 6 integration tests marked with @pytest.mark.integration
- Uses COPILOT_GITHUB_TOKEN secret from the integration environment
- Follows the same pattern as other provider integration jobs
- Includes path filtering in merge-tests (github_copilot package + core changes)
- Added to needs lists in report and check jobs

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-04 22:06:26 +00:00
bbccb7c28c .NET: Bump ModelContextProtocol from 1.1.0 to 1.2.0 (#3956) (#6239)
Co-authored-by: Neeraj Karamchandani <neerajkaramchandani@mac.mynetworksettings.com>
2026-06-04 21:51:15 +01:00
Tao ChenandGitHub dbc312a78a Python: Fix toolbox consent flow in hosted agent (#6249)
* Fix toolbox consent flow in hosted agent

* Resolve conflict

* Make unused tool as comment

* Fix tests
2026-06-04 20:28:59 +00:00
bb9ed63a34 .NET: Restructure skill script schemas XML and remove resources from body (#6343)
* Restore UTF-8 BOMs and fix BuildScriptSchemasBlock doc comment

- Restore UTF-8 BOM on all changed files to match repo convention
- Fix XML doc: <schema name=...> -> <schema script=...> to match emitted output

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address PR review comments: fix doc remarks and rename tests

- Update script doc remarks to clarify only parameter schemas are included
- Fix grammar: 'arguments format' -> 'argument format'
- Rename misleading test methods to match actual assertions
- Clarify comment about removed wrapper element

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: SergeyMenshykh <SergeMenshikh@outlook.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-04 21:15:29 +01:00
6b94315161 Python: Add timeout parameter to FoundryAgent to fix ConnectTimeout on multi-turn conversations (#6263)
* Python: fix ConnectTimeout on multi-turn FoundryAgent conversations (#6241)

Expose a `timeout` parameter on `RawFoundryAgentChatClient`,
`_FoundryAgentChatClient`, `RawFoundryAgent`, `FoundryAgent`, and
`RawOpenAIChatClient` so callers can override the HTTP timeout used by
the underlying AsyncOpenAI client.

Root cause: `RawFoundryAgentChatClient.__init__` called
`project_client.get_openai_client()` without configuring any timeout,
inheriting the OpenAI SDK default of `httpx.Timeout(connect=5.0)`.
When connections are recycled between turns under load, the 5 s connect
timeout fires and surfaces as `openai.APITimeoutError`.

Fix:
- `load_openai_service_settings` (`_shared.py`): accept `timeout` and
  include it in `client_args` for all three `AsyncOpenAI`/
  `AsyncAzureOpenAI` construction paths.
- `RawOpenAIChatClient.__init__` (`_chat_client.py`): accept `timeout`
  and forward to `load_openai_service_settings`.
- `RawFoundryAgentChatClient.__init__` (`_agent.py`): accept `timeout`
  and set `openai_client.timeout = timeout` on the client returned by
  `get_openai_client()` before passing it to the base class.
- `_FoundryAgentChatClient`, `RawFoundryAgent`, `FoundryAgent`: accept
  and propagate `timeout` through the construction chain.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add timeout parameter to FoundryAgent and RawOpenAIChatClient

Expose a timeout parameter on RawFoundryAgentChatClient,
_FoundryAgentChatClient, RawFoundryAgent, FoundryAgent, and
RawOpenAIChatClient. When provided, the value is applied to the
underlying AsyncOpenAI client so that connect timeouts under load
or after connection recycling can be tuned by callers.

Previously, get_openai_client() was called without any timeout
override, so the SDK default of httpx.Timeout(connect=5.0) was
inherited and could fire on multi-turn conversations where the
underlying connection is recycled between turns.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: Add `timeout` parameter to `FoundryAgent` to fix `ConnectTimeout` on multi-turn conversations

Fixes #6241

* fix(foundry): use with_options to avoid mutating shared OpenAI client timeout (#6241)

Replace direct assignment  with
 in
RawFoundryAgentChatClient.__init__.

The Azure AI Projects SDK caches and returns a shared AsyncOpenAI client
per AIProjectClient. Mutating its .timeout attribute leaked the override
to all other code paths sharing that client (other agents, user code).
with_options() returns a new client instance with the override applied,
leaving the original shared client untouched.

Update tests to assert with_options is called with the correct timeout
and that the original shared client's timeout attribute is not mutated.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* test(foundry): assert with_options return value flows to instance.client (#6241)

The four timeout propagation tests verified that with_options was called
but did not confirm that the returned (timeout-configured) client was
actually stored on the instance. A silent discard of the return value
would have left the tests green while the timeout had no effect.

Each test now captures the constructed instance and asserts:
  assert <instance>.client is openai_client_mock.with_options.return_value

Affected tests:
- test_raw_foundry_agent_chat_client_init_applies_timeout_to_openai_client
- test_raw_foundry_agent_chat_client_init_applies_timeout_with_preview_enabled
- test_foundry_agent_chat_client_init_propagates_timeout
- test_foundry_agent_init_propagates_timeout_to_openai_client

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>
2026-06-04 18:25:18 +00:00
Yufeng HeandGitHub bc0e65d716 fix: drop hosted MCP calls when reasoning is stripped (#6210) 2026-06-04 18:11:24 +00:00
4268080c20 Python: Fix spurious Magentic custom manager warning (#6261)
* Fix magentic manager warning

* Use typing_extensions.Sentinel for _MISSING sentinel value

Replace the bare object() sentinel with typing_extensions.Sentinel per
PEP 661 (now final). Sentinel provides a proper name and repr
('<_MISSING>') and is the idiomatic approach going forward.

Refs #4306

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: correct Sentinel type annotation for max_stall_count param (#6261)

Use int | Sentinel for max_stall_count parameter type annotation instead
of int with cast(Any, _MISSING) to properly express that the parameter
can hold either an int or the _MISSING sentinel value. This fixes the
pyright reportUnnecessaryComparison errors caused by the types int and
Sentinel having no overlap.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Rename _MISSING sentinel to UNSET in orchestrations

The sentinel is user-visible as a default in public init signatures, so
use UNSET (no leading underscore) instead of the private _MISSING name.
Drop the now-unnecessary reportPrivateUsage ignores on the UNSET imports.

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>
2026-06-04 08:59:04 +00:00
fe08574a7c Python: [BREAKING] Upgrade github-copilot-sdk to v1.0.0 (stable) (#6292)
* Python: Upgrade github-copilot-sdk to v1.0.0 (stable)

Upgrade agent-framework-github-copilot from github-copilot-sdk 1.0.0b2 to the
stable 1.0.0 release, adapting to all breaking API changes.

Source changes (_agent.py):
- SubprocessConfig removed: use RuntimeConnection.for_stdio(path=...) +
  CopilotClient kwargs (connection, log_level, base_directory)
- Import paths: copilot.generated.session_events -> copilot.session_events
- Settings: copilot_home -> base_directory (env GITHUB_COPILOT_BASE_DIRECTORY)
- Default deny handler: PermissionDecisionUserNotAvailable() (from
  copilot.generated.rpc)

Test changes:
- Updated imports and client-construction assertions (kwargs-based)
- Permission handler tests use concrete decision types
  (PermissionDecisionApproveOnce, PermissionDecisionDeniedInteractivelyByUser)

Sample changes:
- Permission handlers use PermissionHandler.approve_all or sync
  approve_and_log pattern (v1.0.0 protocol v3 dispatch is incompatible
  with blocking input() in permission handlers)
- Function approval sample uses asyncio.to_thread for interactive prompts
- Simplified imports across all samples

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address PR review: scope permission handlers, widen type, add test

- Shell sample: only approve kind='shell', deny others
- URL sample: only approve kind='url', deny others
- Use getattr() for kind-specific attributes to satisfy pyright
- Widen PermissionHandlerType to accept async handlers (matches SDK)
- Add test for _deny_all_permissions return value

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix validation script and strengthen test assertion

- Update scripts/sample_validation/create_dynamic_workflow_executor.py to
  use copilot.session_events imports and PermissionHandler.approve_all
- Assert isinstance(result, PermissionDecisionUserNotAvailable) instead of
  stringly-typed kind check

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add integration tests for GitHubCopilotAgent

Add 6 integration tests mirroring .NET coverage:
- Basic non-streaming response
- Streaming response
- Function tool invocation
- Session context (multi-turn)
- Session resume by ID
- Shell command execution

Tests require COPILOT_GITHUB_TOKEN env var (skipped otherwise).
Each test cleans up its Copilot session via delete_session.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-04 08:42:35 +00:00
f970a699d8 Python: Fix compaction message-id collisions and tool-loop summary persistence (#6299)
* Fix compaction message-id collisions and tool-loop summary persistence

Fixes two bugs in the compaction strategies:

- #5237: incremental group annotation assigned message ids by position
  within the re-annotated slice, so moving the re-annotation start back to
  a previous group start restarted ids at 0 and produced collisions
  (e.g. a user message reusing an assistant message's id), merging groups
  and causing tool-result compaction to wrongly exclude messages.
  group_messages/_ensure_message_ids now take an id_offset and guard
  against existing-id collisions; annotate_message_groups threads the
  slice start index through as the offset.

- #4991: the function-invocation loop copied the message list each
  iteration, so summaries inserted by compaction landed in a throwaway
  copy and were lost across tool-loop iterations (only the persistent
  excluded flags survived). _prepare_messages_for_model_call now compacts
  the list in place when messages is a list, so inserted summaries persist.

Adds regression tests (incremental id uniqueness, existing-id collision
avoidance, idempotency, and tool-loop summary persistence including
streaming and conversation-id modes).

Also adds a summarization.py sample demonstrating SummarizationStrategy
directly with a real client, and reworks advanced.py with tool-call
groups and a real summarizer.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Guard incremental message-id assignment against prefix-id collisions

Addresses PR review on #5237: _ensure_message_ids only guarded against
collisions within the re-annotated slice. A preexisting (e.g. user-supplied)
id in the preserved prefix could still be reassigned in the suffix when the
id was numerically out of position, merging groups across the re-annotation
boundary again.

group_messages/_ensure_message_ids now accept reserved_ids, and
annotate_message_groups passes the preserved prefix's ids so auto-assigned
suffix ids never collide across the full list. Adds a regression test
reproducing the out-of-position prefix-id collision.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-04 08:37:59 +00:00
Yufeng HeandGitHub f29bae8fbc Python: run sync tools off the event loop (#5773)
* fix: run sync tools off event loop

* chore: silence harness tool marker type check
2026-06-04 04:42:08 +00:00
Peter IbekweandGitHub c3901a4ddd Fix Observability/WorkflowAsAnAgent sampl (#6316) 2026-06-03 23:52:50 +00:00
Evan MattsonandGitHub ba617fc3b5 Don't count dependabot prs as part of the limit (#6317) 2026-06-04 08:31:36 +09:00
afa7834e2e Updating dotnet package versions for 1.9 release (#6314)
Co-authored-by: Ben Thomas <25218250+alliscode@users.noreply.github.com>
2026-06-03 20:03:21 +00:00
c6951c21f6 Python: Add MCP-based skills discovery (McpSkillsSource) (#6169)
* Add MCP-based skills discovery (McpSkill, McpSkillsSource, McpSkillResource)

Implement Agent Skills discovery over MCP following the SEP-2640 convention:
- McpSkillsSource: reads skill://index.json to discover skills served by an MCP server
- McpSkill: lazily fetches SKILL.md content via resources/read on demand
- McpSkillResource: wraps MCP resource results (text and binary)
- Path traversal protection in get_resource for defense in depth
- Samples for Foundry Toolbox and standalone MCP skills server
- Comprehensive unit tests (514 lines)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address PR review comments: rename to MCP* convention, fix error handling and samples

- Rename McpSkill/McpSkillResource/McpSkillsSource to MCPSkill/MCPSkillResource/MCPSkillsSource
- Add data-URI prefix stripping for blob resource decoding
- Let non-McpError exceptions propagate from get_resource()
- Fix contradictory test comment
- Use interactive input() in mcp_based_skill sample
- Remove misleading sample output block

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Restore debug logging for McpError in get_resource()

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Use AzureCliCredential in Foundry toolbox skills sample for consistency

Replace DefaultAzureCredential with AzureCliCredential to match the
credential convention used in all other samples.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Use MCPStreamableHTTPTool in MCP skills sample

Replace raw mcp library imports (ClientSession, streamable_http_client)
with the framework's MCPStreamableHTTPTool to keep MCP server connections
consistent regardless of whether skills are enabled.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Branch on McpError.error.code so only not-found errors return empty

Previously _try_read_index() and get_resource() swallowed every McpError
as 'no skills available', making auth failures, server crashes, and
connection drops indistinguishable from a server that simply has no
skills.

Now only two codes are treated as not-found:
- -32002 (MCP-spec Resource not found)
- -32601 (METHOD_NOT_FOUND — server lacks resources/read)

All other McpError codes and non-McpError exceptions propagate with a
warning log, surfacing real failures visibly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add tests for non-McpError and non-not-found error propagation in MCP skills

Cover the re-raise branch in MCPSkill.get_resource for plain
ConnectionError/TimeoutError, the generic McpError (code 0) propagation
on get_resource, and TimeoutError propagation in _try_read_index.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Revert "Use MCPStreamableHTTPTool in MCP skills sample"

This reverts commit f31ed0ded9.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Introduce MCP_SKILLS experimental feature for MCP skill classes

Add a separate MCP_SKILLS feature ID to ExperimentalFeature enum and
use it for MCPSkillResource, MCPSkill, and MCPSkillsSource, since their
promotion timeline is partly outside of our control.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-03 18:09:50 +00:00
westeyandGitHub a982428916 .NET: Bug fixes for AGUI hosting and workflows (#6311)
* Add mcp tool execution fix

* Apply IsolationKeyScopedAgentSessionStore to MapAGUI by default if not yet set and improve comments in samples

* Address PR comments

* Fix formatting
2026-06-03 17:45:58 +00:00
90a3e5de47 .NET: Add ILoggerFactory and IServiceProvider to HarnessAgent constructor (#6273)
* Add ILoggerFactory and IServiceProvider to HarnessAgent constructor

Add optional ILoggerFactory and IServiceProvider parameters to the
HarnessAgent constructor and AsHarnessAgent extension method, passing
them to all downstream components that accept them:

- FunctionInvokingChatClient (via UseFunctionInvocation)
- CompactionProvider
- AgentSkillsProvider
- ChatClientAgent (via BuildAIAgent)
- AIAgentBuilder.Build()

Closes #6103

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Improve tests to verify ILoggerFactory and IServiceProvider propagation

- Add test verifying ILoggerFactory.CreateLogger() is called by
  downstream components (CompactionProvider, AgentSkillsProvider)
- Add test verifying IServiceProvider is queried during pipeline build

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-03 09:09:39 +00:00
49a6e433a3 Python: progressive tool exposure via FunctionInvocationContext (#6233)
* Python: progressive tool exposure via FunctionInvocationContext

Add first-class progressive tool exposure to the Python core function-calling
loop. Tools can now add or remove real FunctionTool schemas at runtime via the
injected FunctionInvocationContext, taking effect on the next iteration of the
loop.

- FunctionInvocationContext gains a live `tools` list plus experimental
  `add_tools()` / `remove_tools()` helpers (feature: PROGRESSIVE_TOOLS).
- The function-calling loop establishes a run-local, normalized tools list and
  threads it into the context at both invocation paths so mutations propagate.
- Add a sample (dynamic_tool_exposure.py) and a tools samples README, including
  a note that CodeAct providers (Monty/Hyperlight) use their own provider-level
  tool management instead.

Supersedes #3877.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Validate non-negative input in dynamic_tool_exposure sample tools

Address review feedback: factorial and fibonacci now return an error
message for negative n instead of producing incorrect results.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Make add_tools atomic and surface swallowed function errors

Address review feedback on progressive tool exposure:

- add_tools now validates the full batch against a throwaway copy before
  committing, so a duplicate-name clash partway through a sequence leaves
  the live tool list unchanged (all-or-nothing).
- _auto_invoke_function now logs a warning (with traceback) when a tool
  raises, so contract errors such as a duplicate-name ValueError from
  add_tools are debuggable without enabling include_detailed_errors.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Avoid retaining tracebacks when logging swallowed function errors

Logging with exc_info=exc fed the exception traceback to the logging
machinery, whose frame references created reference cycles collected
lazily by the cyclic GC. On Windows that could drop a hyperlight
WasmSandbox on a non-owning thread ("unsendable, dropped on another
thread"), crashing the xdist worker. Log a pre-formatted message with
the exception repr instead, so no traceback object is retained.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* added missing decorator

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-03 09:01:07 +00:00
Peter IbekweandGitHub 6086a74302 Python: Promote agent-framework-declarative package to RC (#6256)
* Promote agent-framework-declarative package to RC

* Update missed package status file.
2026-06-02 19:30:05 +00:00
fa8cfb7567 Python: Fix FoundryAgent stripping model from PromptAgent requests (#5526)
* Fix FoundryAgent stripping model from PromptAgent requests

Move run_options.pop('model', None) inside the _uses_foundry_agent_session()
conditional so that model is only stripped for hosted agent sessions (where
the server manages the model) and preserved for PromptAgent requests that
require it in the Responses API call.

Fixes #5525

* test: add coverage for resp_* continuation preserving model

Adds test_raw_foundry_agent_chat_client_prepare_options_preserves_model_for_resp_continuation
to explicitly verify that HostedAgent v1 / v2-no-session paths (where conversation_id
starts with resp_) preserve model and previous_response_id without triggering the
hosted-session gate.

---------

Co-authored-by: Benke Qu <bequ@microsoft.com>
Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com>
2026-06-02 18:30:04 +00:00
6de4c24fdd .NET: Promote Workflows.Declarative packages to stable versions (#6254)
* Promote Workflows.Declarative packages to stable versions

* Address PR feedback: enable package validation on GA declarative packages

Both Workflows.Declarative and Workflows.Declarative.Mcp set IsReleased=true

but were disabling package validation, bypassing the repo's GA convention

(see dotnet/nuget/nuget-package.props which auto-enables validation when

IsReleased=true).

Re-enable validation by removing the local EnablePackageValidation=false

overrides and pointing PackageValidationBaselineVersion at 1.8.0-rc1 (the

latest published version of each package). This catches accidental breaking

changes between RC and the first GA. Future GAs should bump the baseline to

the previous GA version.

Verified locally: dotnet build -c Release on both projects runs

RunPackageValidation -> APICompat ran successfully without finding any

breaking changes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Update statement for the baseline validation.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-02 15:10:02 +00:00
Dineshsuriya DandGitHub a5f355e04a Python: Fix OTLP HTTP base-endpoint losing /v1/{signal} auto-append (#5913)
* Python: Fix OTLP HTTP base-endpoint losing /v1/{signal} auto-append

Per the OTel spec, OTEL_EXPORTER_OTLP_ENDPOINT is a *base* URL for HTTP —
the SDK auto-appends /v1/traces, /v1/metrics, /v1/logs when it reads the
env var directly. Signal-specific endpoint env vars are *full* URLs used
verbatim.

_get_exporters_from_env read the base endpoint and forwarded it as the
constructor ``endpoint=`` argument, which the SDK always treats as a full
signal URL. As a result, with OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
and HTTP protocol, the exporter sent to http://localhost:4318 instead of
http://localhost:4318/v1/traces (and likewise for metrics/logs).

Replicate the spec's auto-append here when falling back to the base
endpoint under HTTP. gRPC behavior is unchanged.

* Python: Fix mypy type errors in OTLP endpoint assignment

Pre-declare traces_endpoint, metrics_endpoint, logs_endpoint as
str | None before the if/else block. Mypy inferred str from the
if-branch f-string assignments and then rejected the str | None
expressions in the else-branch as incompatible.
2026-06-02 09:59:50 +00:00
0cf48923cd .NET: Add Hosted-ToolboxMcpSkills sample (#6175)
* .NET: Add Hosted-ToolboxMcpSkills sample

Adds a hosted Foundry Responses sample that discovers MCP-based skills from a Foundry Toolbox and makes them available to the agent via AgentSkillsProvider.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Align README and Program.cs default model to gpt-5

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Clarify MCP skills provider log to avoid implying eager discovery

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Drop redundant skills provider configured log

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add Foundry Toolbox Skills tag to manifest

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Simplify BearerTokenHandler by deriving from HttpClientHandler

Removes the need for an explicit InnerHandler. Enables CheckCertificateRevocationList to satisfy CA5399.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-02 08:41:21 +00:00
cdc4809b8a ci: harden Python test coverage workflow (#5982)
Improve input handling and token management in the Python test coverage
workflows.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-02 07:43:08 +00:00
Hameed KunkanoorandGitHub 043208241a Python: Persist hosted MCP call/results as canonical mcp_call output (#6070)
* Persist hosted MCP call/results as canonical mcp_call output

- Preserve hosted MCP call/result pairs as canonical mcp_call output items

- Coalesce MCP call + result in non-streaming conversion path

- Keep call-id alignment for MCP tool call tracking and output mapping

- Update tests and package metadata

* Fix missing Mapping import in hosted responses adapter

* Fix pyright unknown type in MCP output stringification

* Fix typing for MCP output sequence iteration

* Improve MCP output robustness and avoid eager flattening

* Bump foundry_hosting to b7 and update responses dependency to b7

* Restore foundry_hosting package version to 1.0.0a260521

* Refactor hosted MCP output parsing
2026-06-02 07:30:36 +00:00
Yufeng HeandGitHub 05ebb966cf fix: skip orphan anthropic thinking signatures (#5784) 2026-06-02 00:48:42 +00:00
Evan MattsonandGitHub c83a944e85 Fix open pr count check (#6255) 2026-06-02 09:09:36 +09:00
Thota Sai KarthikandGitHub 5d98beddf5 Python: feat(bedrock): implement native structured output support via Converse API (#6052)
* feat(bedrock): add structured output support via Converse API (Fixes #5966)

* fix(bedrock): improve unsupported model exception handling and schema parsing

* refactor(bedrock): use generic traversal for strict schema enforcement

* address Copilot review comments on structured output

* refine bedrock structured output: guard additionalProperties, TypeError check, docs + test

* fix(bedrock): widen response_format to Mapping and add missing test coverage
2026-06-01 23:30:19 +00:00
e0d0ad16a0 Python: feat(evals): Foundry Adaptive Evals integration (rubric-generation) (#6101)
* Python: feat(evals): RubricScore type + EvalScoreResult.dimensions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: feat(foundry-evals): RubricDimension + GeneratedEvaluatorRef + accept in evaluators=

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: feat(evals): parse rubric_scores from output items + assertion helpers

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: feat(evals): BaseAgent.as_eval_source / Workflow.as_eval_source

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: feat(foundry-evals): EvalGenerationSource + generate_rubric helper

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: feat(foundry-evals): YAML config loader + sample

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: fix(evals): address PR review feedback

Addresses 4 Copilot review comments on PR #6101:

1. assert_dimension_score_at_least: drop the (not evaluator or found_any) guard so require_applicable=True correctly raises when the named evaluator produces no entries for the dimension. Adds TestRubricAssertions covering the regression.

2. GeneratedEvaluatorRef docstring: reword to describe actual behaviour (pinning recommended, not required) so it matches the dataclass default and FoundryEvals warning path.

3. _poll_generation_job: switch from asyncio.get_event_loop() to get_running_loop() and bound the per-iteration sleep by remaining time, matching _poll_eval_run.

4. generate_rubric: type category as Literal['quality','safety'] and validate at the entry point with a ValueError; drop the silent 'invalid -> quality' rewrite in _generation_job_to_ref. Adds a regression test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: feat(foundry-evals): hosted-agent-aware rubric generation

* Auto-detect hosted Foundry agents in agent_as_eval_source: when the
  agent's chat_client exposes a string agent_name (the convention used
  by RawFoundryAgentChatClient for PromptAgents/HostedAgents), emit a
  type='agent' EvalGenerationSource so the service fetches instructions
  and tools from the agent registry instead of relying on the local
  wrapper (which holds neither for hosted agents).
* Add hosted_agent_version kwarg and a new agent_version field on
  EvalGenerationSource so PromptAgent runs can pin to a specific hosted
  version for reproducible rubric generation.
* Add force_prompt_source escape hatch to bypass auto-detection and
  always emit a rendered prompt dossier - useful when the local wrapper
  carries overrides the service-side agent doesnt see.
* Fix _to_sdk_source for dataset sources: SDK ctor takes name=/version=,
  not dataset_name=/dataset_version=. The mismatch would raise TypeError
  against the real azure-ai-projects 2.3.0a* SDK; only unmocked
  integration paths were affected.

Tests cover: auto-detection happy path, versionless hosted agent,
explicit hosted_agent_version forwarding, force_prompt_source override,
non-string chat_client attrs (MagicMock test doubles) not mis-detected,
agent_version forwarded through _to_sdk_source, and the corrected
dataset SDK kwarg names.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(foundry-evals): accept canonical dimension_scores key per docs

The published Foundry rubric-evaluator output (Microsoft Learn 'Rubric evaluators' reference) places per-dimension breakdowns under properties.dimension_scores, not properties.rubric_scores. The parser now tries dimension_scores first and falls back to rubric_scores for preview-build compatibility, and tolerates non-list payloads (e.g. MagicMock auto-attrs) by trying the next candidate when parsing yields zero entries.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(foundry-evals): add manual create_rubric_evaluator

Adds FoundryEvals.create_rubric_evaluator as the agent-framework surface over project_client.beta.evaluators.create_version. This is the manual counterpart to generate_rubric: callers supply RubricDimension instances (authored locally, ported from another framework, or hand-tuned) and we POST a RubricBasedEvaluatorDefinition. The service auto-attaches the non-editable residual dimension (general_quality for quality, general_policy_compliance for safety).

Per the Microsoft Learn 'Rubric evaluators' reference, the auto-generation path (create_generation_job) is primarily a portal/UI feature; external SDK clients with rich local agent context are better served by manual create_version. This keeps generate_rubric for users who want to round-trip through a Foundry-registered agent.

Validation up front: weight must be in [1,10], ids unique, descriptions non-empty, pass_threshold in [0,1]. The returned GeneratedEvaluatorRef is identical in shape to one obtained from generate_rubric, so downstream evaluators= lists work unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* samples(foundry-evals): manual rubric sample + namespace re-exports

Adds evaluate_with_manual_rubric_sample.py demonstrating the end-to-end dev scenario for FoundryEvals.create_rubric_evaluator: hand-author a list of RubricDimension, register via create_rubric_evaluator, then use the pinned GeneratedEvaluatorRef alongside built-in evaluators in an agent regression run.

Also re-exports RubricDimension, GeneratedEvaluatorRef, build_sources, and load_evals_config from agent_framework.foundry (both the lazy runtime shim and the type stub) so the rubric samples can import everything from a single namespace; the auto-generate sample was previously broken because the shim was missing build_sources / load_evals_config.

Updates the foundry-evals README with a chooser entry for the two rubric paths.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(foundry-evals): remove rubric creation flows; keep consumption only

Reframes agent-framework as a pure consumer of Foundry rubric evaluators: scoring against rubrics that already exist (authored in the Foundry portal or via the dedicated SDK / REST surface) instead of creating them from the SDK.

Removed creation surface area:

- FoundryEvals.generate_rubric (auto-generate path) and create_rubric_evaluator (manual path), plus all _GenerationSdkTypes / _ManualRubricSdkTypes / _to_sdk_dimensions / _coalesce_generation_sources / _to_sdk_source / _poll_generation_job / _generation_job_to_ref / _evaluator_version_to_ref / _get_beta_evaluators / _import_*_sdk_types helpers.

- EvalGenerationSource (the input source discriminator), RubricDimension (the input dimension type), agent_as_eval_source / workflow_as_eval_source / _detect_hosted_foundry_agent helpers, and the YAML-config loader (_evals_config.py with RubricGenerationSpec / RubricSourceSpec / parse_evals_config / load_evals_config / build_sources).

- BaseAgent.as_eval_source / Workflow.as_eval_source plus the _render_agent_dossier / _render_workflow_dossier helpers in core. These existed only to feed the now-removed generation pipeline.

- Samples evaluate_with_generated_rubric_sample.py, evaluate_with_manual_rubric_sample.py, and evaluators.yaml. Replaced with a short README section showing how to reference an existing rubric evaluator via GeneratedEvaluatorRef.

Kept (consumption surface):

- GeneratedEvaluatorRef, slimmed to (name, version, display_name). Still accepted alongside built-in evaluator strings in FoundryEvals(evaluators=[...]). Versionless refs still warn.

- RubricScore on EvalScoreResult.dimensions plus EvalResults.assert_dimension_score_at_least for per-dimension CI gates.

- _parse_dimension_entries / _extract_rubric_scores output parsing (both canonical dimension_scores and the legacy rubric_scores key).

Tests: 160/160 foundry unit tests and 71/71 core local-eval tests pass; pyright is clean across changed files. The pre-existing tests/core/test_telemetry.py::test_detect_hosted_fallback_import_error failure is unrelated and reproduces on the prior commit.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* samples(foundry-evals): add evaluate_with_rubric_sample

Adds a runnable end-to-end sample showing how to consume a pre-existing rubric evaluator created in Foundry: reference it with GeneratedEvaluatorRef(name, version), mix it with built-in evaluators in FoundryEvals, and gate CI with assert_dimension_score_at_least on a specific dimension.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(foundry-evals): satisfy mypy on _fetch_output_items

mypy infers OutputItemListResponse.sample as dict[str, object] | None while pyright correctly infers the typed Sample model. Cast to Any so both type checkers accept the attribute access pattern, rename the local to avoid shadowing the inner-loop sample binding, and drop the now-stale pyright suppressions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* docs(foundry-evals): drop unpublished rubric-evaluators learn.microsoft.com link

The Adaptive Evals authoring docs are not yet published on Microsoft Learn, so the link 404s. Keep the descriptive text without the broken hyperlink; we can re-add it once the docs ship.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* test(foundry-evals): hoist repeated local imports to module top

Per code review feedback (eavanvalkenburg): the test file repeated 'from agent_framework_foundry._foundry_evals import ...' inside 22 test bodies and 'from agent_framework_foundry import GeneratedEvaluatorRef' inside 8 more. Move all of them to the existing top-level imports; the symbols are the same across tests and the local imports were redundant.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Ben Thomas <25218250+alliscode@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-01 23:01:56 +00:00
f36096ce1a Python: Fix core observability unsafe serialization of function-call arguments containing dataclass/framework objects (#6026)
* fix: safely serialize function-call arguments in core observability

Apply make_json_safe() to content.arguments in _to_otel_part() before
building the otel message dict, so that dataclass/framework payloads
(e.g. workflow request_info events) do not cause a TypeError when
_capture_messages() calls json.dumps().

Lift make_json_safe() into agent_framework._serialization (no new
external deps — dataclasses/datetime only) so the core observability
path can use it without a dependency on the ag-ui adapter.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(core): safely serialize workflow request_info payloads in observability (#5733)

- Add make_json_safe() helper to recursively convert non-serializable objects
- Use make_json_safe() in _to_otel_part() for function_call arguments
- Fix CustomPayload test class to use @dataclass (resolves B903 lint error)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(serialization): guard callability and normalize dict keys in make_json_safe (#5733)

- Use callable(getattr(obj, method, None)) instead of hasattr() so that
  non-callable attributes named model_dump/to_dict/dict do not raise
  TypeError at runtime.
- Wrap each call in try/except TypeError to handle callables with
  mandatory arguments gracefully.
- Convert dict keys to str() so that non-string keys (e.g. datetime,
  int) cannot cause json.dumps to raise TypeError.
- Add regression tests for both scenarios.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address observability serialization review feedback

---------

Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-01 21:41:52 +00:00
03e14ca187 .NET: Update hosted agents (#6243)
* Updating to latest Foundry hosting packages.

* Re-applying .gitignore.

* Adding empty line at end of .gitignore

---------

Co-authored-by: Ben Thomas <25218250+alliscode@users.noreply.github.com>
2026-06-01 21:27:29 +00:00
b298113d15 .NET - Fix missing id on function_call_output in Foundry Hosting (#6246)
* Fix missing id on function_call_output in Foundry Hosting

The Foundry storage layer was rejecting responses with
"ID cannot be null or empty (Parameter 'id')" because
function_call_output items emitted by OutputConverter had no id on
the wire.

OutputItemFunctionToolCallOutput's public ctor only sets CallId and
Output; Id is read-only and only the SDK's internal ctor populates
it. OutputItemBuilder<T>.ApplyAutoStamps fills ResponseId and
AgentReference but not Id, so the itemId passed to
AddOutputItem<T>(itemId) was used only for event sequencing and the
serialized item went out with id=null.

Switch to stream.OutputItemFunctionCallOutput(callId, output), the
SDK convenience method that uses the internal ctor and stamps the
id. Add a regression test asserting the added/done events carry a
non-empty matching Id.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* ci: free disk space and relocate NuGet cache on ubuntu runners

The ubuntu-latest dotnet-build/test jobs were hitting No space left on device because the runner image only ships ~14 GB free on /. The full multi-TFM build plus the dotnet pack + console-app install-check exhausts that easily.

Add a reusable composite action .github/actions/free-runner-disk-space that runs on Linux runners only and:

* removes pre-installed toolchains we never use here (Android SDK, GHC/Haskell, CodeQL, PyPy, Ruby, Go, boost, vcpkg, etc.), prunes docker images, and disables swap (reclaims ~25-30 GB on /)

* relocates the NuGet package cache to /mnt/nuget via NUGET_PACKAGES env, since /mnt has ~75 GB free on hosted runners

Wire the action into the four ubuntu-touching jobs in dotnet-build-and-test.yml (dotnet-build, dotnet-test, dotnet-foundry-hosted-it, dotnet-test-functions). The action self-guards with runner.os == 'Linux' so the matrix legs that run on windows are unaffected.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: alliscode <25218250+alliscode@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-01 18:43:45 +00:00
8091d052d8 Python: refresh dev dependencies and validate runtime bounds (#6238)
Updates third-party dev dependencies across the Python workspace and
validates that all runtime dependency bounds still hold at both ends.

Dev dependency bumps (root, lab, declarative, durabletask):
- uv 0.11.6 -> 0.11.17, ruff 0.15.8 -> 0.15.15,
  pytest-asyncio 1.3.0 -> 1.4.0, mcp 1.27.0 -> 1.27.2,
  azure-monitor-opentelemetry 1.8.7 -> 1.8.8,
  poethepoet 0.42.1 -> 0.46.0, prek 0.3.9 -> 0.4.3,
  types-python-dateutil and types-PyYaml stub bumps.
- Transitive Dependabot items swept via lock: idna 3.11 -> 3.17,
  pip 26.0.1 -> 26.1.2.

Deliberately excluded:
- opentelemetry-sdk stays 1.40.0: azure-monitor-opentelemetry (incl.
  1.8.8) hard-pins opentelemetry-sdk==1.40.
- mypy stays 1.20.0 and pyright stays 1.1.408: the 2.1.0 / 1.1.409
  bumps introduce new diagnostics that fail type checking and need
  dedicated PRs.
- rich kept as a range: agentlightning (lab[lightning]) forces
  rich==13.9.4.

Code/formatting changes driven by the ruff upgrade:
- devui lifespan now uses try/finally so shutdown cleanup always runs
  (ruff RUF075).
- Removed unused TYPE_CHECKING imports in core and foundry flagged by
  ruff 0.15.15.
- Reapplied ruff 0.15.15 formatting to the files it changed.

Validation: validate-dependency-bounds-test "*" passes (31/31 lower +
31/31 upper); typing 62/62; lint 31/31; devui tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-01 17:53:56 +00:00
westeyandGitHub 52a8045bb6 Python: Add background agent support to harness agent (#6155)
* Add background agent support to harness agent

* Address PR comments
2026-06-01 17:20:39 +00:00
Yufeng HeandGitHub 78d175a1e2 Python: coalesce code interpreter history chunks (#5801)
* fix: coalesce code interpreter history chunks

* fix: narrow content item list types

* fix: remove redundant content list casts
2026-06-01 13:26:20 +00:00
CopilotGitHublarohracopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>Laveesh Rohra
b59a854fcd Fix integration test worker crashes in Azure Functions on Py3.13 (#4260)
* Initial plan

* Fix integration test worker crashes on Python 3.13

Three changes to prevent pytest-xdist workers from crashing during
Azure Functions integration tests:

1. Add `start_new_session=True` to subprocess on Linux so signals
   (e.g. from test-timeout) cannot propagate between the func host
   and the xdist worker process.

2. Add an overall 100-second budget to the fixture setup loop so
   the retry logic never exceeds the 120-second test timeout. When
   pytest-timeout's thread method fires during fixture setup and the
   thread doesn't respond, it calls os._exit() which kills the
   xdist worker – this is the root cause of the "Not properly
   terminated" crashes.

3. Remove the `UV_PYTHON: "3.10"` workaround from both workflow
   files so integration tests actually run on Python 3.13.

Co-authored-by: larohra <41490930+larohra@users.noreply.github.com>

* Validate integration tests on Python 3.13

Co-authored-by: larohra <41490930+larohra@users.noreply.github.com>

* Revert unintentional uv.lock dependency bumps

Co-authored-by: larohra <41490930+larohra@users.noreply.github.com>

* Use time.monotonic() instead of time.time() for fixture budget timing

Addresses review feedback: monotonic clock is immune to NTP/clock
adjustments that could skew the budget enforcement.

Co-authored-by: larohra <41490930+larohra@users.noreply.github.com>

* Fix func worker segfault on Python 3.13 by redirecting worker to Python 3.12

The Azure Functions Python worker crashes with SIGSEGV (exit code 139)
on Python 3.13 due to protobuf C extension (google._upb) compatibility
issues.  When the test runner uses Python >=3.13, the conftest now
automatically finds a compatible Python 3.10-3.12 and sets
languageWorkers__python__defaultExecutablePath so the func host uses
it for the worker process.

The CI setup action also ensures Python 3.12 is available on the
runner, falling back to uv python install if the system doesn't have
it.

Co-authored-by: larohra <41490930+larohra@users.noreply.github.com>

* Address code review: add path validation, clarify version range and config key format

Co-authored-by: larohra <41490930+larohra@users.noreply.github.com>

* Run func worker natively on Python 3.13 by disabling dependency isolation

Replace the Python 3.12 redirect workaround with the proper fix:
set PYTHON_ISOLATE_WORKER_DEPENDENCIES=0 on Python >=3.13.

The segfault (exit code 139) is caused by the Azure Functions worker's
module isolation mechanism conflicting with protobuf's C extensions
(google._upb) on Python 3.13.  Disabling isolation lets the worker
load dependencies from the app's own environment, which avoids the
crash while keeping everything running on Python 3.13.

See: https://github.com/Azure/azure-functions-python-worker/issues/1797

Co-authored-by: larohra <41490930+larohra@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: larohra <41490930+larohra@users.noreply.github.com>
Co-authored-by: Laveesh Rohra <larohra@microsoft.com>
2026-06-01 09:18:26 +00:00
Evan MattsonandGitHub 8b0db48d33 Add community PR limit workflow (#6229)
* Add community PR limit workflow

* Address PR limit workflow review feedback
2026-06-01 18:12:31 +09:00
5affc9c333 Python: Reorganize A2A samples and use package A2AExecutor (#6165)
* Reorganize A2A samples: client demos in 02-agents, use package A2AExecutor

- Move client samples (agent_with_a2a, a2a_agent_as_function_tools) to samples/02-agents/a2a/
- Add new concept samples: polling, stream reconnection, protocol selection
- Replace sample agent_executor.py with package-level A2AExecutor (stream=True)
- Update 04-hosting/a2a to focus on server-side, point to 02-agents for clients
- Add README.md for the new 02-agents/a2a/ sample collection

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix streaming artifact coalescing and address PR review feedback

A2AExecutor fix:
- Generate a stable artifact_id per stream in _run_stream so all streaming
  chunks share the same ID, enabling proper append=True coalescing per the
  A2A spec (TaskArtifactUpdateEvent with same artifactId).
- Previously, item.message_id was None for OpenAI/Foundry streaming updates,
  causing the SDK to generate a new random UUID per token (100+ separate
  artifacts instead of 1 appended artifact).

Sample improvements:
- Replace join workaround with response.text now that coalescing works
- Add background=True to stream reconnection resume call (required for
  continuation token emission on in-progress tasks)
- Fix type ignore specificity in polling sample

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-01 07:09:11 +00:00
edcc786651 .NET: Preserve and propagate CreatedAt through workflows (#3930)
* Preserve per-message CreatedAt attribute if it's available

* Add unit test

---------

Co-authored-by: Sam Chang <changsam@microsoft.com>
Co-authored-by: samchang-msft <samchang.msft@gmail.com>
2026-05-29 21:41:40 +00:00
07a1e83492 .NET: Forward Magentic participant replies to manager (#6156)
MagenticOrchestrator.TakeTurnAsync dropped the `messages` parameter
on subsequent turns, so participant replies never reached the manager's
ChatHistory. The manager kept re-dispatching the same speaker every
round until MaxRounds.

Append the incoming messages to taskContext.ChatHistory before running
the coordination round (matches Python's _handle_response).

Adds RecordingReplayAgent + regression test that asserts the worker's
reply reaches round-2's progress-ledger call.

Co-authored-by: Jacob Alber <jaalber@microsoft.com>
2026-05-29 21:41:25 +00:00
Roger BarretoandGitHub fa2a6af443 Bump Azure.AI.AgentServer.* packages and align Azure.Core/System.ClientModel (#6178)
* Bump Azure.AI.AgentServer.* package versions

* Align Azure.Core/System.ClientModel to AgentServer transitive deps

Bump Azure.Core 1.55->1.56 and System.ClientModel 1.11->1.12 to match Azure.AI.AgentServer.* requirements, and add explicit references in transitive-pinning-off Foundry consumers to avoid CS1705/MSB3277 version conflicts.
2026-05-29 19:42:07 +00:00
Peter IbekweandGitHub 11c8d89ab2 .NET: Fix InvokeMcpTool approval path for declarative workflows (#6177)
* Fix InvokeMcpTool approval path for declarative workflows

* Added more test for coverage.
2026-05-29 19:07:48 +00:00
CopilotGitHubcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>Roger Barreto
6510d6e3c8 .NET: Quarantine flaky DevUI test (#6159)
* Bump Microsoft.Extensions.AI packages to 10.6.0

* Align transitive package versions for Microsoft.Extensions.AI 10.6.0

* Initial plan

* Temporarily skip flaky DevUI keyed/default workflow test

* Revert Microsoft.Extensions.AI package bumps, keep only flaky test quarantine

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
2026-05-29 17:09:12 +00:00
dd9a4b6321 Python: [A2A] Set message_id on AgentResponseUpdate for message-bearing paths (#6163)
Map A2A protocol message_id to AgentResponseUpdate.message_id in two paths
where it was previously omitted, aligning with .NET behavior:

1. Standalone A2AMessage: set message_id=msg.message_id (matches .NET
   ConvertToAgentResponseUpdate(Message) which sets both ResponseId and
   MessageId to message.MessageId)

2. TaskStatusUpdateEvent (terminal/input_required): set
   message_id=message.message_id (matches .NET which sets
   MessageId=statusUpdateEvent.Status.Message?.MessageId)

Fixes #5949

Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-29 08:11:13 +00:00
e8ff541ebf Python: consolidate MCP reliability fixes (#6145)
* Python: consolidate MCP reliability fixes

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix MCP cleanup and metadata typing

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Satisfy MCP metadata mypy typing

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix Pyright metadata mapping type

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-29 07:21:14 +00:00
Daria KorenievaandGitHub d2d5384f28 Python: Add Mistral AI embedding client package (#5480)
* Python: Add Mistral AI embedding client package

Signed-off-by: Daria Korenieva <daric2612@gmail.com>

* Address review feedback: fix dimensions check, sort embeddings by index, align docs

Signed-off-by: Daria Korenieva <daric2612@gmail.com>

* Address review feedback: downgrade to alpha, remove integration tests - Change version to 1.0.0a260505 (alpha) - Update classifier to Development Status :: 3 - Alpha - Update PACKAGE_STATUS.md to alpha - Remove Mistral from integration test workflows (no API keys yet)

Signed-off-by: Daria Korenieva <daric2612@gmail.com>

* Add samples directory for alpha package compliance Per python-package-management skill: alpha packages must include samples inside the package directory.

Signed-off-by: Daria Korenieva <daric2612@gmail.com>

* Fix ruff formatting in sample file

Signed-off-by: Daria Korenieva <daric2612@gmail.com>

---------

Signed-off-by: Daria Korenieva <daric2612@gmail.com>
2026-05-29 07:20:56 +00:00
Jacob AlberandGitHub 1fccf16f11 feat: Remove [Experimental] tag from .NET Orchestrations (#6164) 2026-05-29 00:03:19 +00:00
8ed2159c4b .NET: Workflow Outputs Overhaul: Support Tagging, Filtering Agent Outputs (#6045)
* test: reshuffle .NET Workflow tests in preparation for Outputs overhaul

Phase 1 of the .NET Workflows outputs overhaul (see
working/implementation-plan.md). Pure moves/renames in
dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests; no production code
changes, no new test cases. The split keeps each orchestration mode in
its own source file so the upcoming tag-aware and orchestration-default
test additions land on clean diffs.

Renames:
* WorkflowBuilderSmokeTests.cs -> WorkflowBuilderTests.cs (with class
  rename to match). The scope is no longer "smoke"-only once subsequent
  phases add tag-aware builder tests.
* InputWaiterAndOutputFilterTests.cs -> InputWaiterTests.cs +
  OutputFilterTests.cs. The file already declared the two test classes
  separately; this split simply gives each its own file so the
  output-filter cases have a dedicated home for tag-aware additions.

Split of AgentWorkflowBuilderTests.cs:
* AgentWorkflowBuilderTests.cs is now the outer
  `public static partial class AgentWorkflowBuilderTests` holding the
  shared test helpers (DoubleEchoAgent + session + WithBarrier variant,
  WorkflowRunResult, RunWorkflow* methods) bumped from `private` to
  `internal` so the new top-level GroupChatWorkflowBuilderTests in the
  same assembly can reach them.
* AgentWorkflowBuilder.SequentialTests.cs (nested SequentialTests):
  BuildSequential_InvalidArguments_Throws,
  BuildSequential_AgentsRunInOrderAsync.
* AgentWorkflowBuilder.ConcurrentTests.cs (nested ConcurrentTests):
  BuildConcurrent_InvalidArguments_Throws,
  BuildConcurrent_AgentsRunInParallelAsync.

Sequential and Concurrent are kept as nested classes because they're
modes of the same `AgentWorkflowBuilder` static factory and do not
produce dedicated builder types.

New file:
* GroupChatWorkflowBuilderTests.cs (top-level): the existing
  BuildGroupChat_* and GroupChatManager_* cases moved out of the old
  AgentWorkflowBuilderTests file. They exercise the
  `GroupChatWorkflowBuilder` type (returned by
  `AgentWorkflowBuilder.CreateGroupChatBuilderWith`), so a dedicated
  top-level test class - matching the convention reserved by the plan
  for HandoffWorkflowBuilderTests / MagenticWorkflowBuilderTests - is
  the right home. Cross-class helper references qualify with
  `AgentWorkflowBuilderTests.DoubleEchoAgent` and
  `AgentWorkflowBuilderTests.RunWorkflowAsync`.

The outer partial class is `static` (and nested classes carry the
instance test methods) because the outer holds only static helpers;
this satisfies CA1052 without suppressions and is invisible to xUnit
discovery, which finds tests on the nested classes as
`AgentWorkflowBuilderTests.SequentialTests.*` etc.

Validation: `dotnet build` clean on both target frameworks; all 547
tests in Microsoft.Agents.AI.Workflows.UnitTests pass on net10.0.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat: introduce OutputTag, Futures, and tag-aware WorkflowBuilder API

Phase 2 of the .NET Workflows outputs overhaul. Additive code change
only - no observable runtime behavior change. The runner still uses the
legacy bypass for AgentResponse / AgentResponseUpdate payloads, and the
new `Futures.EnableAgentResponseOutputTaggingAndFiltering` flag defaults
to false. Phase 3 will wire the flag into the runner; this commit only
introduces the types and the builder API.

New public surface:
* `OutputTag` (readonly struct): wraps a string Value with ordinal
  equality (IEquatable, GetHashCode, == / !=) so it can participate as a
  HashSet element. Internal ctor closes the set. One public singleton:
  `OutputTag.Intermediate`. Terminal / regular outputs carry no tag
  (empty Tags set). JSON-serialized as a bare string via
  [JsonConverter(typeof(OutputTagJsonConverter))], with the converter
  rehydrating to the well-known singleton on read.
* `Futures` (static class): hosts opt-in pre-GA behavior switches.
  First flag is `EnableAgentResponseOutputTaggingAndFiltering`; XML doc
  captures the v2.0.0 obsoletion / v3.0.0 removal lifecycle.
* `WorkflowOutputEvent.Tags`: `HashSet<OutputTag>` exposed directly
  (concrete collection, matches the JSON-serialization convention used
  for `WorkflowInfo.OutputExecutorIds`). Never null; empty for legacy /
  terminal events. New ctors take a single `OutputTag` or
  `IEnumerable<OutputTag>?`; the existing (data, executorId) ctor
  remains and produces an untagged event. `HasTag(OutputTag)` helper.
  `AgentResponseEvent` and `AgentResponseUpdateEvent` gain matching
  tag-accepting ctors forwarding to the base.
* `WorkflowOutputEventExtensions.IsIntermediate(this WorkflowOutputEvent)`:
  extension method returning `evt.HasTag(OutputTag.Intermediate)`. The
  preferred way to ask "is this an intermediate output?" without
  reaching into the Tags set.
* `WorkflowBuilder.WithOutputFrom(IEnumerable<ExecutorBinding>, OutputTag)`
  and `WorkflowBuilder.WithOutputFrom(ExecutorBinding, OutputTag)`:
  forward-looking tagged overloads. The IEnumerable form is the primary
  tagged surface; the single-executor form is a convenience for the
  common one-executor case. Currently usable for the
  `OutputTag.Intermediate` singleton; will become the primary surface
  once the `OutputTag` constructor is opened to user-defined tags in
  a future release. Callers in this release should prefer the
  intent-specific `WithIntermediateOutputFrom` extension for the
  intermediate case. Tags accumulate across repeated calls; same tag
  repeated dedupes via the HashSet.
* `WorkflowBuilderExtensions.WithIntermediateOutputFrom(this WorkflowBuilder, IEnumerable<ExecutorBinding>)`:
  helper that forwards to `WithOutputFrom(executors, OutputTag.Intermediate)`.
  Takes an IEnumerable (matching the tagged WithOutputFrom shape) -
  callers pass collection literals: `builder.WithIntermediateOutputFrom([a, b])`.
  XML doc remarks call out the Futures-flag interaction and the
  AIAgent-payload forwarding contract.

Internal shape changes:
* `WorkflowBuilder._outputExecutors`: HashSet<string> -> Dictionary<
  string, HashSet<OutputTag>>. The value set is empty for executors
  designated only via the untagged WithOutputFrom; contains Intermediate
  (and possibly future tags) otherwise.
* `Workflow.OutputExecutors`: HashSet<string> -> Dictionary<string,
  HashSet<OutputTag>>.
* `OutputFilter.CanOutput`: `Contains(id)` -> `ContainsKey(id)`.
* `WorkflowInfo.OutputExecutorIds`: HashSet<string> -> Dictionary<
  string, HashSet<OutputTag>>, with a custom JsonConverter that reads
  both the new map shape (`{id: ["intermediate", ...]}`) and the legacy
  array shape (`[id1, id2]`, where each id is treated as an untagged
  output). Always writes the map shape. IsMatch updated to compare
  per-id tag sets.

Tests landing in this commit (per the test-with-feature principle):
* `OutputTagTests.cs` (6 tests): KnownValues, EqualityIsOrdinalOnValue,
  DefaultStructValueIsDistinct (default(OutputTag) does not collide
  with the Intermediate singleton in a HashSet),
  GetHashCodeMatchesEquals, JsonConverter_RoundtripsValueAsString,
  ConstructorIsInternal (reflection-based assertion that the (string)
  ctor is `internal`).
* `WorkflowBuilderTests.cs` adds 7 new tests pinning the builder
  API contract: RegistersWithEmptyTagSet, AddsIntermediateTag,
  MultipleExecutorsAllUntagged, ThenIntermediate_AccumulatesTags,
  RepeatedDedupes, OnlyRegistersWithoutPriorWithOutputFrom,
  TracksExecutorBinding.
* `BackwardsCompatibility/JsonCheckpointSerializationTests.cs`
  (new folder + file, 5 tests): event-level ctor contract tests
  (single-tag, no-tag, multi-tag — the last with a custom tag);
  IsIntermediate() asserted; load-bearing JSON BC tests for
  `WorkflowInfo.OutputExecutorIds` -
  `WorkflowOutputExecutorsReadsLegacyArrayShape` (legacy ids map to
  empty tag sets) and `WorkflowOutputExecutorsWritesMapShape`.

The plan's three JSON round-trip tests for `WorkflowOutputEvent.Tags`
were dropped: `WorkflowEvent` is not currently a serialized checkpoint
shape (see the comment in WorkflowsJsonUtilities.cs about events not
being persisted), so there is no real back-compat surface to pin
through JSON. They are substituted with in-process ctor/property
round-trip tests that exercise the `Tags` / `HasTag` / `IsIntermediate`
contract.

Validation: full `Microsoft.Agents.AI.Workflows.UnitTests` suite runs
green on net10.0 (565 passing, 0 failing). Core library builds clean
on net472, netstandard2.0, net8.0, net9.0, and net10.0. Test project
builds clean on net472 + net10.0.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat: route AgentResponse(Update) through the output filter under a Futures flag

`InProcessRunnerContext.YieldOutputAsync` historically special-cased AgentResponse and
AgentResponseUpdate payloads: it built the typed event subclass and emitted it directly,
bypassing the output filter. Rewrites the method so that:

- When `Futures.EnableAgentResponseOutputTaggingAndFiltering` is `false` (the current
  default), AgentResponse(Update) keep the legacy bypass — emitted as
  AgentResponseEvent / AgentResponseUpdateEvent with no tags. Existing callers see no
  behavior change.
- When the flag is `true`, AIAgent payloads flow through the output filter just like
  every other payload type: undesignated sources are dropped, and the emitted event
  carries the source's tag set (empty for terminal `WithOutputFrom`, `{Intermediate}`
  for `WithIntermediateOutputFrom`, the set union when both designations apply).

Non-AIAgent (POCO) outputs also now carry the source's tag set on the emitted
WorkflowOutputEvent unconditionally — additive, since no existing assertion inspected
Tags. Subclass events (`AgentResponseEvent` / `AgentResponseUpdateEvent`) continue to
be emitted under both modes so `switch (evt) { case AgentResponseEvent: ... }`
consumer code keeps matching.

Adds `OutputFilter.TryGetTags` as the tag-aware lookup used by the runner.
`OutputFilter.CanOutput` is kept (still used by the existing sync tests in
`OutputFilterTests.cs`).

Tests
-----
- `Futures/Futures.AgentResponseOutputFilteringAndTaggingTests.cs` (new): the F1–F13
  matrix from the plan, covering every combination of `(flag on/off) Ă— (designation)
  Ă— (payload shape)`. Uses a `FuturesScope` IDisposable + a `FuturesSerial` xUnit
  collection (DisableParallelization = true) to keep the process-global flag from
  leaking across parallel tests.
- `OutputFilterTests.cs`: four new `Test_OutputFilter_…` cases for the `TryGetTags`
  surface (empty-tag-set for terminal designation, `{Intermediate}` for intermediate
  designation, union for accumulated designation, `false` for unregistered).

582/582 unit tests pass on net10.0 (565 baseline + 17 new).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat: tag-aware defaults and designation API on orchestration builders

Aligns the .NET orchestration builders with Python's output / intermediate-output
distinction. Each builder either applies a Python-aligned default designation set or
replays the user's explicit `WithOutputFrom` / `WithIntermediateOutputFrom` calls,
never both.

Static `AgentWorkflowBuilder.BuildSequential` / `BuildConcurrent` apply defaults
unconditionally (no user-facing fluent surface to take control through):

- Sequential: terminal `end` + every agent designated intermediate.
- Concurrent: terminal `end` + every agent and per-agent accumulator designated
  intermediate.

The three fluent instance builders memoize agent-typed designation calls in a
`Dictionary<AIAgent, HashSet<OutputTag>>` (empty set = terminal-only, non-empty =
intermediate tag(s)) so repeated calls dedupe naturally. They replay the entries
at `Build()` time, suppressing defaults when any call has been made:

- `HandoffWorkflowBuilder` / `HandoffWorkflowBuilderCore<TBuilder>` (also picked up
  by the obsolete `HandoffsWorkflowBuilder` via inheritance).
  Default: terminal `HandoffEnd` + every handoff agent intermediate.
  (Bug fix: legacy code relied on `WithOutputFrom(end)` to bind `HandoffEnd`. The
  new explicit-designation path bypasses that, so `Build()` now calls
  `BindExecutor(end)` unconditionally to keep validation happy.)
- `GroupChatWorkflowBuilder` — default: terminal host + every participant intermediate.
- `MagenticWorkflowBuilder` — default: terminal orchestrator + every team member
  intermediate.

Designating a non-participant agent throws `InvalidOperationException`.

The bare `WorkflowBuilder` default is unchanged — only the orchestration-style
builders gain implicit defaults, matching the plan's non-goal.

Tests
-----
- `AgentWorkflowBuilder.SequentialTests` / `.ConcurrentTests`: one default-spec
  assertion each.
- `GroupChatWorkflowBuilderTests`: defaults-match-spec, explicit-replaces-defaults,
  non-participant throws.
- `HandoffWorkflowBuilderTests` (new file): same three.
- `MagenticWorkflowBuilderTests` (new file): same three.

593/593 unit tests pass on net10.0 (582 baseline + 11 new).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat: WorkflowHostAgent forwards AgentResponseEvent unconditionally under Futures-on

Aligns the .NET Workflow-as-Agent surface with Python `as_agent`. Under
`Futures.EnableAgentResponseOutputTaggingAndFiltering = true`,
`WorkflowSession.InvokeStageAsync` now forwards `AgentResponseEvent`
unconditionally — joining `AgentResponseUpdateEvent` in ignoring the host's
`includeWorkflowOutputsInResponse` switch. That switch keeps governing the
generic `WorkflowOutputEvent` path for non-AIAgent payloads, where it is
further short-circuited by an `IsIntermediate()` check (tagged intermediate
outputs always surface).

Under Futures-off the legacy asymmetry is preserved: `AgentResponseUpdateEvent`
always forwarded, `AgentResponseEvent` gated by `includeWorkflowOutputsInResponse`.

Back-compat: with `Futures.EnableAgentResponseOutputTaggingAndFiltering` left at
its default `false`, observable behavior is identical to before.

`Futures` documentation gains a remark explaining the `Workflow.AsAIAgent()`
interaction in both flag states.

Runner fix
----------
`InProcessRunnerContext.YieldOutputAsync` now skips `Executor.CanOutput` for
AgentResponse-shaped payloads under both Futures branches. `AIAgentHostExecutor`
doesn't declare AgentResponse(Update) in its `Yields` set, so the historical
legacy bypass had silently skipped the check; Phase 3's Futures-on path was
running it and would reject AIAgent payloads. AIAgent-shaped payloads are now
always a valid output shape, matching the legacy bypass semantics.

Phase 4 follow-on
-----------------
Switched the three orchestration-builder designation-replay loops to iterate
`Dictionary.Keys` with a value lookup instead of constructing/destructuring
`KeyValuePair<,>`. Cleaner shape and avoids the netstandard2.0 / net472
`KeyValuePair<,>.Deconstruct` unavailability that surfaced when this branch
multi-TFM-built.

Tests
-----
`WorkflowHostSmokeTests.IntermediateForwarding` (new nested class, 6 tests):
- intermediate AgentResponse forwarded past the include-outputs gate (Futures on)
- terminal AgentResponse forwarded unconditionally (Futures on)
- terminal AgentResponse gated by include flag (Futures off, legacy)
- undesignated AIAgent executor emits no AgentResponseEvent under Futures-on
- legacy bypass still emits AgentResponseEvent under Futures-off
- intermediate tag is observable via `update.RawRepresentation`

The class joins the `FuturesSerial` xUnit collection so the process-global flag
is serialized against other Futures-toggling tests.

599/599 unit tests pass on net10.0 (593 baseline + 6 new).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat: SequentialWorkflowBuilder and ConcurrentWorkflowBuilder, OrchestrationBuilderBase

Promotes the Sequential and Concurrent orchestration shapes to first-class fluent
builder classes, matching Handoff / GroupChat / Magentic. Users can call
`WithOutputFrom(agents)` / `WithIntermediateOutputFrom(agents)` to control which
agents are designated output / intermediate sources; when no designation call is
made, the Python-aligned defaults apply (terminal aggregator output + every agent
intermediate; Concurrent also tags per-agent accumulators).

`AgentWorkflowBuilder.BuildSequential(...)` and `BuildConcurrent(...)` are kept
and now delegate to the new builders; observable behavior unchanged. Five static
factories now mirror each other:

- `AgentWorkflowBuilder.CreateSequentialBuilderWith(params IEnumerable<AIAgent>)`
- `AgentWorkflowBuilder.CreateConcurrentBuilderWith(params IEnumerable<AIAgent>)`
- `AgentWorkflowBuilder.CreateHandoffBuilderWith(AIAgent)`        (already existed)
- `AgentWorkflowBuilder.CreateGroupChatBuilderWith(Func<...>)`    (already existed)
- `AgentWorkflowBuilder.CreateMagenticBuilderWith(AIAgent)`       (new)

OrchestrationBuilderBase
------------------------
New abstract `OrchestrationBuilderBase<TBuilder>` unifies the shared fluent
surface across all five orchestration builders: `WithName`, `WithDescription`,
`WithOutputFrom`, `WithIntermediateOutputFrom`, and the
`ApplyOutputDesignations(builder, agentMap, kind, applyDefaults)` helper that
either replays the user's designations or invokes the orchestration-specific
defaults.

Removes ~150 LOC of duplicated designation-management code from the four
non-Handoff builders, plus the equivalent from `HandoffWorkflowBuilderCore`.

Tests
-----
- New `SequentialWorkflowBuilderTests.cs` / `ConcurrentWorkflowBuilderTests.cs`
  (replace the old `AgentWorkflowBuilder.{Sequential,Concurrent}Tests.cs`
  nested-class files). Method names normalized to
  `Test_<BuilderType>_<Scenario>[Async]`.
- Shared helpers (`DoubleEchoAgent`, `DoubleEchoAgentWithBarrier`,
  `WorkflowRunResult`, `RunWorkflow*`) moved from the old
  `AgentWorkflowBuilderTests` partial class into a new
  `OrchestrationTestHelpers` static class in `OrchestrationTestHelpers.cs`.
  Downstream test files (Group Chat, Handoff, Sequential, Concurrent) updated
  to qualify with `OrchestrationTestHelpers.*`.
- A new `AgentWorkflowBuilderTests.cs` covers the static surface directly:
  `BuildSequential` / `BuildConcurrent` invariants and aggregator wiring, plus
  null-rejection + round-trip checks for every `Create*BuilderWith` factory.
- New AsAgent intermediate-suppression tests on a nested `AsAgentForwarding`
  class for each of Sequential and Concurrent: build with only the terminal
  agent designated via `WithOutputFrom`, run via `AsAIAgent(...)`, assert via
  `AgentResponseUpdate.AuthorName` that intermediate agents do not surface.
  Both join the `FuturesSerial` collection.
- New `Test_<Builder>_WithDescriptionPropagatesToWorkflow` smoke tests on
  Sequential and Concurrent (newly available via the base class).

625/625 unit tests pass on net10.0.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: dotnet format

* fixup: encoding

* fixup: charset

* fixup: Updates for PR feedback

* fixup: format

* fixup: merge issue

* Fix intermediate filtering on .AsAgent()

* fix filter logic

* fix: Revert logic change and add comments

---------

Co-authored-by: Jacob Alber <jalber@lokitoth.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-28 21:26:31 +00:00
b000a2cf51 Python: Adding AgentFileStore and FileAccessProvider to support file access operations. (#6099)
* Adding AgentFileStore and FileAccessProvider to support file ased operations for agents.

* Address PR review feedback on FileAccessProvider

- Probe symlinks on the unresolved candidate path so in-root symlinks
  cannot silently pass and out-of-root symlinks surface the correct
  error message.
- Validate matching_lines elements in FileSearchResult.from_dict and
  raise a clean ValueError for non-mapping entries.
- Cap search regex pattern length (256 chars) via a new
  _compile_search_regex helper to mitigate ReDoS, and surface the cap
  in the file_access_search_files tool description.
- Skip non-UTF-8 files during filesystem search instead of aborting
  the entire directory walk.
- Replace the module-scope trailing string in the data-processing
  sample with comments to avoid Ruff B018.
- Remove the checked-in working/region_totals.md sample artifact so
  the save flow works from a clean checkout.
- Expand the Windows stdout reconfiguration comment in task_runner.py
  for clarity.
- Add tests for invalid/oversize regex, non-UTF-8 file search, and
  in-root symlink rejection.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix mypy redundant-cast in FileSearchResult.from_dict

Use cast(list[object], ...) instead of cast(list[Any], ...) so the
cast represents a real type change (lists are invariant) and is no
longer flagged by mypy as redundant, while still satisfying pyright's
reportUnknownVariableType. Matches the existing pattern in _memory.py.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Tighten path normalization and directory resolution in FileAccess

- _normalize_relative_path now strips surrounding whitespace up front
  so leading/trailing spaces never leak into file segments, and
  rejects trailing path separators for file paths so 'foo/' is no
  longer silently coerced to 'foo'.
- FileSystemAgentFileStore._resolve_safe_directory_path normalizes
  with is_directory=True and maps an empty normalized result to the
  root. This matches InMemoryAgentFileStore so whitespace-only
  directory inputs resolve to the root instead of raising.
- Added tests for whitespace stripping, trailing-separator rejection,
  and whitespace-only directory listing on the filesystem store.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Harden FileAccess search and atomic save in store API

- Add wall-clock timeout (10s) around regex scans so a pathological pattern (e.g. `(a+)+`) below the length cap cannot stall the event loop.
- Offload the InMemoryAgentFileStore regex scan to a worker thread, matching the filesystem store.
- Fail closed when `Path.is_symlink` raises during the safe-path probe so a permission error cannot silently bypass the symlink/reparse-point rejection.
- Add `overwrite: bool = True` to `AgentFileStore.write_file`; the in-memory store performs the check under the existing lock and the filesystem store uses `open(mode='x')` so concurrent callers cannot race past `overwrite=False`.
- `file_access_save_file` now relies on the atomic store call instead of a separate `file_exists` round-trip.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix Python 3.10 timeout handling and add directory arg to list/search tools

- Catch asyncio.TimeoutError in _run_search_with_timeout. In Python 3.10
  asyncio.wait_for raises asyncio.exceptions.TimeoutError, which is
  distinct from the builtin TimeoutError (the two were unified in 3.11).
  Catching the asyncio alias works on every supported version.
- Add an optional directory parameter to file_access_list_files and
  file_access_search_files so agents can enumerate / scope searches to
  nested folders, not just the store root.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address FileAccess review feedback: case, errors, signal, TOCTOU

- InMemoryAgentFileStore now stores (display_name, content) so list_files
  and search_files return the original-case names callers wrote, matching
  the behaviour of FileSystemAgentFileStore on case-preserving filesystems
  and removing the silent in-memory vs. on-disk contract divergence.
- FileSystemAgentFileStore.read_file raises ValueError instead of letting
  UnicodeDecodeError bubble for binary / non-UTF-8 input, restoring
  symmetry with search_files (which still skips) and giving the tool
  layer a recoverable type to translate.
- Tool wrappers now catch ValueError and OSError around every operation
  and surface them as readable strings, so 'you used ..' and 'the file
  already exists' are both reported to the model the same way instead of
  the former crashing out as an unhandled exception.
- _search_files_sync logs per skipped non-UTF-8 file at WARNING and an
  aggregate INFO summary so operators can distinguish 'no matches' from
  'half the corpus was unreadable'.
- FileSystemAgentFileStore softens its docstrings to acknowledge the
  inherent probe-then-open TOCTOU window. On POSIX both read and write
  now pass O_NOFOLLOW so the kernel refuses if the leaf segment becomes
  a symlink between the probe and the open. Windows has no equivalent
  flag; the limitation is documented.
- Tests cover: case preservation on list/search, ValueError on non-UTF-8
  read at the store and tool layer, tool-layer string responses for
  path-traversal and oversized-regex inputs, search-skip log output,
  symlink rejection on delete/search/list, and symlinked intermediate
  directory rejection.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address FileAccess nit comments: docstrings, enumerate, opt-in delete approval

- Expand FileSearchMatch/FileSearchResult.to_dict docstrings to explain why
  the override is needed (__slots__ defeats the mixin's __dict__ iteration)
  and why exclude/exclude_none are accepted-but-ignored (mixin signature
  compatibility for callers like to_json).
- Use enumerate(lines, start=1) in _search_file_content so the +1 below is
  no longer needed; rename loop variable to line_number for clarity.
- Add opt-in require_delete_approval: bool = False on FileAccessProvider.
  When True, file_access_delete_file is registered with approval_mode
  'always_require' so the host must approve every delete. Default False
  preserves current behaviour and matches the .NET reference, but
  deployments that want a safer-by-default posture can enable it.
- Add tests covering both delete approval modes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* FileAccess: require delete approval by default

Flip the default for FileAccessProvider(require_delete_approval=...) from
False to True so destructive deletes are gated by host approval out of the
box. Callers that want the previous autonomous behaviour (which matches the
.NET reference) can pass require_delete_approval=False.

Tests updated accordingly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fixing linkinspector by installing Chrome for puppeteer first.

---------

Co-authored-by: Ben Thomas <25218250+alliscode@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-28 20:09:50 +00:00
Tao ChenandGitHub 0578f4c910 Backfill chat span request model if it's unknown and response model is avaliable (#6160) 2026-05-28 20:03:46 +00:00
e9a606344a Python A2A: Expose supported_protocol_bindings as configurable parameter (#6098)
* Expose supported_protocol_bindings as configurable parameter on A2AAgent

Add supported_protocol_bindings parameter to A2AAgent.__init__() allowing
users to configure which A2A protocol bindings (JSONRPC, GRPC, HTTP+JSON)
the client prefers when connecting to remote agents.

- Defaults to ["JSONRPC"] matching current behavior
- Passes through to ClientConfig for transport negotiation
- Replaces 4 hardcoded references with the configurable value

Closes #6057

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix empty list falsy trap and add fallback path test coverage

- Use 'is not None' check instead of 'or' to preserve explicit empty list
- Add test verifying empty list is not silently replaced with defaults
- Add test verifying fallback path uses custom bindings

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Document known protocol binding values in docstring

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Use Literal union for protocol binding type hint

Provides IDE autocomplete for known values while keeping the type
open for custom bindings (Literal is str at runtime).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-28 19:05:13 +00:00
d2f79930d5 .NET: feat: Update GroupChatManager semantics to match other Orchestration patterns (#6140)
* Refactor group chat workflow to prevent message echoing and enhance checkpointing

- Updated GroupChatWorkflowBuilder to disable forwarding incoming messages to prevent duplicates.
- Enhanced RoundRobinGroupChatManager with checkpointing support to preserve state across executions.
- Modified GroupChatHost to maintain a history of messages and track the current speaker for message broadcasting.
- Implemented broadcasting logic to ensure participants receive messages from others while excluding their own responses.
- Added comprehensive unit tests for group chat orchestration, including scenarios for tool approval and function calls.
- Introduced a new ApprovalHarness for testing tool invocation and approval workflows.

* fixup: format

* Add JSON serialization support for GroupChatManagerState and RoundRobinGroupChatManagerState

---------

Co-authored-by: Jacob Alber <jalber@lokitoth.com>
2026-05-28 18:40:48 +00:00
Peter IbekweandGitHub b1e9efee7e Update package version (#6161) 2026-05-28 18:37:19 +00:00
3ee1bb4f9f .NET: [Breaking] Refactor AgentFileSkillsSource for depth-based discovery and predicate filters (#6109)
* Refactor AgentFileSkillsSource to use filter predicates and add AgentFileSkillFilterContext

- Replace hardcoded script/resource directory lists with configurable ScriptFilter and ResourceFilter predicates
- Add AgentFileSkillFilterContext class to provide contextual file information to filter predicates
- Replace MaxSearchDepth constant with configurable SearchDepth option
- Update AgentFileSkillsSourceOptions with new filter and search depth properties
- Update tests to reflect the new filtering approach

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Log '(none)' instead of empty string for missing file extensions in debug output

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-28 18:14:57 +00:00
945647a065 .NET: feat: Bring Handoff Orchestration to parity with Python (#6138)
* feat: implement autonomous mode and termination conditions in handoff workflow

* fixup: format

* feat: enhance autonomous mode with per-agent configurations and add unit tests

* fixup: remove empty file

---------

Co-authored-by: Jacob Alber <jalber@lokitoth.com>
2026-05-28 18:04:15 +00:00
Jacob AlberGitHublokitothcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
401a552735 .NET: Support ClaimsIdentity-based scoping of agent sessions (#5696)
* feat: Add DelegatingAgentSessionStore

Add helper for decorator pattern for AgentSessionStore

* feat: Add UserIdentityScopedSessionStore

Add support for using the ASP.Net Core ambient `ClaimsIdentity` User, along with a user-specified claim type to scope the session store based on authenticated identity.

* fix: Harden scope mapping

* fix: Add UserIdentityScopeSessionStoreOptions to avoid future breaking changes

* Split UserIdentityScopedSessionStore into a separate IsolationKeyProvider and IsolationKeyScopedSessionStore

* Add GetService<>() capabilities to interrogate AgentSessionStore delegation chain

* Harden default for A2A hosting by using an IsolationKeyScopedAgentSessionStore when no store is available.

* Pipe isolation through Hosting helper extension methods

* Add comment to samples about adding SessionIsolationKeyProvider

* Fix isolation key provider nullability semantics

* fix A2A defaults

* fixup

* remove unneeded keyProvider requirement test

* Add trust-model XML docs to AgentSessionStore, InMemoryAgentSessionStore, MapAGUI, A2A entry points

Agent-Logs-Url: https://github.com/microsoft/agent-framework/sessions/e466c53a-faad-40a8-8b5f-83cf0dce0b1d

Co-authored-by: lokitoth <6936551+lokitoth@users.noreply.github.com>

* fix: Switch ClaimsBasedIsolationKeyProvider to be Singleton

   * matches HttpContextAccessor and related MAF services

* release: Ensure new project is in the release filter

* fixup: Integraitaon tests

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: lokitoth <6936551+lokitoth@users.noreply.github.com>
2026-05-28 17:43:18 +00:00
Peter IbekweandGitHub 718a1f14fd Add missing projects to solution for release (#6157) 2026-05-28 16:47:31 +00:00
f7c5b8d108 Python: [Breaking] Refactor Skill API to async resource and script lookup (#6135)
Port of .NET commit 08541ee5a9.

Replace property-based Skill.content/resources/scripts with async
by-name lookup methods:
- content property -> async get_content() -> str
- resources property -> async get_resource(name) -> SkillResource | None
- scripts property -> async get_script(name) -> SkillScript | None

SkillsProvider now always includes all three tools (load_skill,
read_skill_resource, run_skill_script) and both instruction blocks
regardless of whether any skills have resources or scripts.

ClassSkill retains resources/scripts properties as overridable hooks
for subclass reflection-based discovery.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-28 15:54:20 +00:00
westeyandGitHub e6762ea876 .NET: Fix render dupe and text input clear bugs, and improve guardrail error messaging (#6136)
* Fix render dupe and text input clear bugs

* Fix another text rendering issue and improve guardrails messaging

* Address PR comments

* Improve guardrail rendering and json error handling

* Another tweak for input box render issue

* Address PR comments
2026-05-28 16:38:04 +01:00
08abe9e704 .NET: Add Foundry Toolbox MCP skills discovery sample (#6134)
* feat: add Agent_Step26_FoundryToolboxMcpSkills sample

Add a new sample demonstrating MCP-based skills discovery from a Foundry
Toolbox endpoint using AgentSkillsProviderBuilder and AIContextProviders.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: address PR review comments for Step26 sample

- Add Foundry-Features: Toolboxes=V1Preview header to MCP transport
  options, matching the Step25 pattern
- Document skill://index.json prerequisite in README

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Update dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step26_FoundryToolboxMcpSkills/Program.cs

Co-authored-by: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
2026-05-28 12:41:33 +00:00
764 changed files with 56706 additions and 6978 deletions
+4 -1
View File
@@ -20,7 +20,10 @@ ignorePatterns:
- pattern: "https://your-resource.openai.azure.com/"
- pattern: "http://host.docker.internal"
- pattern: "https://openai.github.io/openai-agents-js/openai/agents/classes/"
- pattern: "https:\/\/dotnet.microsoft.com\/download"
# dotnet.microsoft.com bot-blocks CI link checkers with intermittent 403s on any
# path (including localized variants like /en-us/download/...), so ignore the
# whole domain rather than just /download.
- pattern: "https:\/\/dotnet.microsoft.com"
- pattern: "https://github.com/Rel1cx/eslint-react"
# excludedDirs:
# Folders which include links to localhost, since it's not ignored with regular expressions
@@ -0,0 +1,64 @@
name: Free runner disk space
description: |
Reclaims disk space on GitHub-hosted Ubuntu runners by removing
pre-installed toolchains we do not use (Android SDK, GHC/Haskell,
CodeQL bundle), Docker images, and swap. Also relocates the
NuGet package cache to /mnt (which has ~75 GB free vs ~14 GB
on /). No-op on non-Linux runners.
runs:
using: composite
steps:
- name: Free disk space (Linux only)
if: runner.os == 'Linux'
shell: bash
run: |
set -euo pipefail
echo "::group::Disk usage before cleanup"
df -h /
echo "::endgroup::"
# Remove pre-installed toolchains we never use on this repo's
# dotnet/python jobs. These reclaim ~25-30 GB on ubuntu-latest.
sudo rm -rf \
/usr/local/lib/android \
/usr/share/dotnet/sdk/NuGetFallbackFolder \
/opt/ghc \
/usr/local/.ghcup \
/opt/hostedtoolcache/CodeQL \
/opt/hostedtoolcache/PyPy \
/opt/hostedtoolcache/Ruby \
/opt/hostedtoolcache/go \
/usr/local/share/boost \
/usr/local/share/powershell \
/usr/local/share/chromium \
/usr/local/share/vcpkg \
/usr/local/lib/heroku \
"${AGENT_TOOLSDIRECTORY:-/opt/hostedtoolcache}/PyPy" \
"${AGENT_TOOLSDIRECTORY:-/opt/hostedtoolcache}/Ruby" \
"${AGENT_TOOLSDIRECTORY:-/opt/hostedtoolcache}/go" || true
# Drop docker images shipped on the runner; jobs that need
# docker pull what they need fresh.
if command -v docker >/dev/null 2>&1; then
sudo docker image prune --all --force >/dev/null 2>&1 || true
fi
# Disable swap to free its backing file.
sudo swapoff -a || true
sudo rm -f /mnt/swapfile /swapfile || true
echo "::group::Disk usage after cleanup"
df -h /
echo "::endgroup::"
- name: Relocate NuGet package cache to /mnt (Linux only)
if: runner.os == 'Linux'
shell: bash
run: |
set -euo pipefail
sudo mkdir -p /mnt/nuget
sudo chown -R "$USER":"$USER" /mnt/nuget
echo "NUGET_PACKAGES=/mnt/nuget" >> "$GITHUB_ENV"
echo "Relocated NuGet package cache to /mnt/nuget"
df -h /mnt || true
+25 -5
View File
@@ -1,23 +1,43 @@
### Motivation and Context
### Motivation & Context
<!-- Thank you for your contribution to the Agent Framework repo!
Please help reviewers and future users, providing the following information:
1. Why is this change required?
2. What problem does it solve?
3. What scenario does it contribute to?
4. If it fixes an open issue, please link to the issue here.
4. If it fixes an open issue, please link to the issue below.
-->
### Description
### Description & Review Guide
<!-- Describe your changes, the overall approach, the underlying design.
Highlight what you want the reviewers to focus on.
These notes will help understanding how your code works. Thanks! -->
- **What are the major changes?**
- **What is the impact of these changes?**
- **What do you want reviewers to focus on?**
<!-- NOTE FOR AUTOMATED/AI REVIEWERS: the "What do you want reviewers to focus on?"
item above is intended for human reviewers only. Automated/AI reviewers should
ignore it and review the entire change rather than narrowing scope to it. -->
### Related Issue
<!-- Which issue does this PR fix? Link it using a GitHub closing keyword so it is
closed automatically when this PR is merged, e.g. "Fixes #123" or "Closes #123".
PRs that are not linked to an issue may be closed, no matter how valid the change is.
Also check whether an open PR already exists for this issue; if so,
explain how this PR is different. -->
Fixes #
### Contribution Checklist
<!-- Before submitting this PR, please make sure: -->
- [ ] The code builds clean without any errors or warnings
- [ ] The PR follows the [Contribution Guidelines](https://github.com/microsoft/agent-framework/blob/main/CONTRIBUTING.md)
- [ ] All unit tests pass, and I have added new tests where possible
- [ ] **Is this a breaking change?** If yes, add "[BREAKING]" prefix to the title of the PR.
- [ ] The PR follows the [Contribution Guidelines](https://github.com/microsoft/agent-framework/blob/main/CONTRIBUTING.md)
- [ ] This PR is linked to an issue and there is no other open PR for this issue (see Related Issue above).
- [x] **This is not a breaking change.** If it _is_ a breaking change, add the `breaking change` label (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.
+181
View File
@@ -0,0 +1,181 @@
// Copyright (c) Microsoft. All rights reserved.
function getPullRequest(context) {
const pullRequest = context.payload.pull_request;
if (!pullRequest?.number || !pullRequest.user?.login) {
throw new Error('This script must be run from a pull_request_target event.');
}
return {
author: pullRequest.user.login,
authorType: pullRequest.user.type,
labels: pullRequest.labels?.map((label) => label.name).filter(Boolean) ?? [],
number: pullRequest.number,
};
}
async function ensureLabel({ github, owner, repo, labelName }) {
try {
await github.rest.issues.getLabel({
owner,
repo,
name: labelName,
});
} catch (error) {
if (error.status !== 404) {
throw error;
}
try {
await github.rest.issues.createLabel({
owner,
repo,
name: labelName,
color: 'd93f0b',
description: 'Community author has exceeded the open pull request limit.',
});
} catch (createError) {
if (createError.status !== 422) {
throw createError;
}
}
}
}
function hasLabel(labels, labelName) {
if (!labelName) {
return false;
}
return labels.some((label) => label.toLowerCase() === labelName.toLowerCase());
}
function isDependabotAuthor({ author, authorType }) {
return authorType === 'Bot' && author.toLowerCase() === 'dependabot[bot]';
}
function buildLimitMessage({ author, exemptLabelName, maxOpenPrs, openPrCount }) {
return [
`Thank you for your contribution, @${author}.`,
'',
`To keep the review queue manageable, we currently limit community contributors to ${maxOpenPrs} `
+ `open pull requests at a time. This PR would put you at ${openPrCount} open pull requests, `
+ 'so we are closing it automatically.',
'',
'Please focus on getting your existing PRs reviewed, merged, or closed before opening another one. '
+ `If a maintainer asked you to open this PR, they can apply the \`${exemptLabelName}\` label and reopen it.`,
].join('\n');
}
async function getOpenPrCount({ github, owner, repo, author, pullRequestNumber }) {
const openPullRequests = await github.paginate(github.rest.pulls.list, {
owner,
repo,
state: 'open',
per_page: 100,
});
const authorOpenPullRequestNumbers = openPullRequests
.filter((pullRequest) => pullRequest.user?.login === author)
.map((pullRequest) => pullRequest.number);
const currentPrIsOpen = authorOpenPullRequestNumbers.includes(pullRequestNumber);
const existingOpenPrCount = currentPrIsOpen
? authorOpenPullRequestNumbers.length - 1
: authorOpenPullRequestNumbers.length;
return existingOpenPrCount + 1;
}
async function enforcePrLimit({ github, context, core, exemptLabelName, maxOpenPrs, labelName }) {
const { owner, repo } = context.repo;
const { author, authorType, labels, number } = getPullRequest(context);
if (isDependabotAuthor({ author, authorType })) {
core.info(`Author ${author} is Dependabot; skipping open PR limit enforcement.`);
return {
author,
closed: false,
dependabotExempt: true,
openPrCount: null,
};
}
if (hasLabel(labels, exemptLabelName)) {
core.info(`PR #${number} has the ${exemptLabelName} label; skipping open PR limit enforcement.`);
return {
author,
closed: false,
exempt: true,
openPrCount: null,
};
}
const openPrCount = await getOpenPrCount({
github,
owner,
repo,
author,
pullRequestNumber: number,
});
if (openPrCount <= maxOpenPrs) {
core.info(
`${author} has ${openPrCount} open pull request(s), which is within the limit of ${maxOpenPrs}.`,
);
return {
author,
closed: false,
openPrCount,
};
}
await ensureLabel({
github,
owner,
repo,
labelName,
});
await github.rest.issues.addLabels({
owner,
repo,
issue_number: number,
labels: [labelName],
});
await github.rest.issues.createComment({
owner,
repo,
issue_number: number,
body: buildLimitMessage({
author,
exemptLabelName,
maxOpenPrs,
openPrCount,
}),
});
await github.rest.pulls.update({
owner,
repo,
pull_number: number,
state: 'closed',
});
core.info(
`${author} has ${openPrCount} open pull request(s), which exceeds the limit of ${maxOpenPrs}. `
+ `Closed PR #${number}.`,
);
return {
author,
closed: true,
openPrCount,
};
}
module.exports = {
buildLimitMessage,
enforcePrLimit,
getOpenPrCount,
};
+253
View File
@@ -0,0 +1,253 @@
// Copyright (c) Microsoft. All rights reserved.
const BREAKING_CHANGE_LABEL = 'breaking change';
const BREAKING_PREFIX = '[BREAKING]';
const DEFAULT_PREFIX_LABELS = Object.freeze({
python: 'Python',
'.NET': '.NET',
});
const DEFAULT_BRACKET_PREFIX_LABELS = Object.freeze({
[BREAKING_CHANGE_LABEL]: BREAKING_PREFIX,
});
function escapeRegExp(value) {
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
function getMatchingValueByKey(valuesByKey, keyToFind) {
const matchingKey = Object.keys(valuesByKey).find((key) => key.toLowerCase() === keyToFind.toLowerCase());
return matchingKey === undefined ? null : valuesByKey[matchingKey];
}
function getPrefixPattern(prefixes) {
return prefixes.map(escapeRegExp).join('|');
}
function canonicalizePrefix(prefix, prefixes) {
return prefixes.find((knownPrefix) => knownPrefix.toLowerCase() === prefix.toLowerCase()) ?? prefix;
}
function normalizeLeadingBracketPrefix(title, bracketPrefixes) {
const bracketPattern = getPrefixPattern(bracketPrefixes);
if (!bracketPattern) {
return title;
}
const leadingBracketPrefix = new RegExp(`^(${bracketPattern})(?=\\s|$)`, 'i');
return title.replace(
leadingBracketPrefix,
(bracketPrefix) => canonicalizePrefix(bracketPrefix, bracketPrefixes),
);
}
function parseLeadingTitlePrefix(title, titlePrefixes) {
const titlePrefixPattern = getPrefixPattern(titlePrefixes);
if (!titlePrefixPattern) {
return null;
}
const match = title.match(new RegExp(`^(${titlePrefixPattern}):\\s*`, 'i'));
if (!match) {
return null;
}
return {
prefix: canonicalizePrefix(match[1], titlePrefixes),
rest: title.slice(match[0].length).trimStart(),
};
}
function removeBracketPrefixToken(title, bracketPrefix) {
const bracketPrefixPattern = escapeRegExp(bracketPrefix);
return title
.replace(new RegExp(`(^|\\s+)${bracketPrefixPattern}(?=\\s|$)`, 'ig'), '$1')
.replace(/\s{2,}/g, ' ')
.trim();
}
function addTitlePrefix(title, prefix, bracketPrefixes = Object.values(DEFAULT_BRACKET_PREFIX_LABELS)) {
const bracketPattern = getPrefixPattern(bracketPrefixes);
const prefixPattern = escapeRegExp(prefix);
if (bracketPattern) {
const bracketThenTitlePrefix = new RegExp(`^(${bracketPattern})(\\s+)(${prefixPattern})(?=:)`, 'i');
if (bracketThenTitlePrefix.test(title)) {
return title.replace(
bracketThenTitlePrefix,
(match, bracketPrefix, spacing) => `${canonicalizePrefix(bracketPrefix, bracketPrefixes)}${spacing}${prefix}`,
);
}
title = normalizeLeadingBracketPrefix(title, bracketPrefixes);
}
if (!title.startsWith(`${prefix}: `)) {
const existingTitlePrefix = new RegExp(`^${prefixPattern}:\\s*`, 'i');
if (existingTitlePrefix.test(title)) {
return title.replace(existingTitlePrefix, `${prefix}: `);
}
return `${prefix}: ${title}`;
}
return title;
}
function hasBracketPrefix(title, bracketPrefix, titlePrefixes = Object.values(DEFAULT_PREFIX_LABELS)) {
const bracketPrefixPattern = escapeRegExp(bracketPrefix);
const leadingBracketPrefix = new RegExp(`^${bracketPrefixPattern}(?=\\s|$)`, 'i');
if (leadingBracketPrefix.test(title)) {
return true;
}
const leadingTitlePrefix = parseLeadingTitlePrefix(title, titlePrefixes);
if (!leadingTitlePrefix) {
return false;
}
return leadingBracketPrefix.test(leadingTitlePrefix.rest);
}
function addBracketPrefix(title, bracketPrefix, titlePrefixes = Object.values(DEFAULT_PREFIX_LABELS)) {
const bracketPrefixPattern = escapeRegExp(bracketPrefix);
const leadingBracketPrefix = new RegExp(`^${bracketPrefixPattern}(?=\\s|$)`, 'i');
if (leadingBracketPrefix.test(title)) {
return title.replace(leadingBracketPrefix, bracketPrefix);
}
const leadingTitlePrefix = parseLeadingTitlePrefix(title, titlePrefixes);
if (leadingTitlePrefix) {
if (leadingBracketPrefix.test(leadingTitlePrefix.rest)) {
const normalizedRest = leadingTitlePrefix.rest.replace(leadingBracketPrefix, bracketPrefix);
return `${leadingTitlePrefix.prefix}: ${normalizedRest}`;
}
const titleWithoutBracketPrefix = removeBracketPrefixToken(leadingTitlePrefix.rest, bracketPrefix);
return `${leadingTitlePrefix.prefix}: ${bracketPrefix}`
+ (titleWithoutBracketPrefix ? ` ${titleWithoutBracketPrefix}` : '');
}
const titleWithoutBracketPrefix = removeBracketPrefixToken(title, bracketPrefix);
return `${bracketPrefix}${titleWithoutBracketPrefix ? ` ${titleWithoutBracketPrefix}` : ''}`;
}
function hasLabel(labels, labelName) {
return labels.some((label) => label.toLowerCase() === labelName.toLowerCase());
}
function getCurrentTitle(context) {
switch (context.eventName) {
case 'issues':
return context.payload.issue.title;
case 'pull_request_target':
return context.payload.pull_request.title;
default:
throw new Error(`Unrecognized eventName: ${context.eventName}`);
}
}
async function updateTitleForAddedLabel({
github,
context,
core,
prefixLabels = DEFAULT_PREFIX_LABELS,
bracketPrefixLabels = DEFAULT_BRACKET_PREFIX_LABELS,
}) {
const labelAdded = context.payload.label?.name;
if (!labelAdded) {
throw new Error('This script must be run from a labeled event.');
}
const currentTitle = getCurrentTitle(context);
let newTitle = null;
const titlePrefix = getMatchingValueByKey(prefixLabels, labelAdded);
if (titlePrefix !== null) {
newTitle = addTitlePrefix(currentTitle, titlePrefix, Object.values(bracketPrefixLabels));
}
const bracketPrefix = getMatchingValueByKey(bracketPrefixLabels, labelAdded);
if (bracketPrefix !== null) {
newTitle = addBracketPrefix(currentTitle, bracketPrefix, Object.values(prefixLabels));
}
if (newTitle === null) {
core.info(`No title prefix configured for label "${labelAdded}".`);
return { updated: false, newTitle: currentTitle };
}
if (newTitle === currentTitle) {
core.info(`Title already includes the prefix for label "${labelAdded}".`);
return { updated: false, newTitle };
}
switch (context.eventName) {
case 'issues':
await github.rest.issues.update({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
title: newTitle,
});
break;
case 'pull_request_target':
await github.rest.pulls.update({
pull_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
title: newTitle,
});
break;
default:
throw new Error(`Unrecognized eventName: ${context.eventName}`);
}
return { updated: true, newTitle };
}
async function syncBreakingChangeLabelFromTitle({
github,
context,
core,
labelName = BREAKING_CHANGE_LABEL,
bracketPrefix = BREAKING_PREFIX,
titlePrefixes = Object.values(DEFAULT_PREFIX_LABELS),
}) {
const pullRequest = context.payload.pull_request;
if (!pullRequest) {
throw new Error('This script must be run from a pull_request_target event.');
}
const title = pullRequest.title || '';
if (!hasBracketPrefix(title, bracketPrefix, titlePrefixes)) {
core.info(`Title does not include ${bracketPrefix} in the title prefix.`);
return { added: false };
}
const labels = pullRequest.labels?.map((label) => label.name).filter(Boolean) ?? [];
if (hasLabel(labels, labelName)) {
core.info(`PR already has the "${labelName}" label.`);
return { added: false };
}
await github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: [labelName],
});
return { added: true };
}
module.exports = {
addBracketPrefix,
addTitlePrefix,
hasBracketPrefix,
syncBreakingChangeLabelFromTitle,
updateTitleForAddedLabel,
};
+116
View File
@@ -0,0 +1,116 @@
---
name: pull-requests
description: >
Guidance for creating pull requests and handling PR review comments in the
Agent Framework repository. Use this when writing a PR description (filling out
the PR template) or when responding to and resolving review comments on an
existing PR.
---
# Pull Request Workflow
This skill covers two tasks: (1) writing a high-quality PR description, and
(2) handling review comments on an existing PR.
## 1. Writing the PR description
Always follow the repository PR template at
[`.github/pull_request_template.md`](../../pull_request_template.md). Keep its
exact structure and headings. Fill every section:
### `### Motivation & Context`
Explain *why* the change is needed: the problem it solves and the scenario it
contributes to. Describe the net change relative to `main` — this is implied, so
do **not** spell out "vs main" explicitly.
### `### Description & Review Guide`
Describe the changes, the overall approach, and the design. Answer the three
prompts:
- **What are the major changes?**
- **What is the impact of these changes?**
- **What do you want reviewers to focus on?** — This item is for **human
reviewers only**. Automated/AI reviewers must ignore it and review the entire
change rather than narrowing scope to it.
### `### Related Issue`
Link the issue the PR fixes using a GitHub closing keyword (`Fixes #123` /
`Closes #123`) so it closes automatically on merge. A PR with no linked issue may
be closed regardless of how valid the change is. Before opening, confirm there is
no other open PR for the same issue; if there is, explain how this PR differs.
### `### Contribution Checklist`
Check every item that applies. For the breaking-change item:
- Leave **"This is not a breaking change."** checked for the common case.
- If the change **is** breaking, add the `breaking change` label **or** put
`[BREAKING]` in the title prefix, before or after a language prefix such as
`Python:` or `.NET:` — workflows keep the label and the title prefix in sync
automatically (see `.github/workflows/label-title-prefix.yml` and
`.github/workflows/label-pr.yml`).
### Do not
- Do **not** add ad-hoc sections such as "Validation" or "Tests run"; CI/CD and
the checklist already cover validation status.
- Do **not** remove or reorder the template's headings.
### Creating the PR
Open new PRs as **drafts** until they are ready for review. Example:
```bash
gh pr create --repo microsoft/agent-framework --base main \
--head <your-fork-owner>:<branch> --draft \
--title "<concise title>" --body "<body following the template>"
```
## 2. Handling review comments
When a PR receives review comments, follow this sequence — **do not start editing
code before the user has reviewed the plan**:
1. **Review the comments.** Read every review comment and thread on the PR,
including inline code comments and general review summaries.
2. **Make a plan.** Produce a concrete plan describing how each comment will be
addressed (or why it should not be, with reasoning).
3. **Let the user review the plan.** Present the plan and wait for the user's
approval or adjustments before implementing anything.
4. **Implement.** Make the agreed changes.
5. **Reply to every comment.** Add a reply to **all** comments explaining how it
was addressed (or the agreed outcome) — leave none unanswered.
6. **Resolve resolved threads.** Mark a review thread as resolved only when the
comment has actually been addressed.
### Useful commands
List review comments and threads:
```bash
# Inline review comments
gh api repos/{owner}/{repo}/pulls/{pr}/comments
# Review threads with resolution state (GraphQL)
gh api graphql -f query='
query($owner:String!,$repo:String!,$pr:Int!){
repository(owner:$owner,name:$repo){
pullRequest(number:$pr){
reviewThreads(first:100){
nodes{ id isResolved comments(first:50){ nodes{ id body author{login} } } }
}
}
}
}' -F owner={owner} -F repo={repo} -F pr={pr}
```
Reply to an inline review comment:
```bash
gh api repos/{owner}/{repo}/pulls/{pr}/comments/{comment_id}/replies \
-f body="Addressed in <commit>: <explanation>"
```
Resolve a review thread (needs the thread node id from the GraphQL query above):
```bash
gh api graphql -f query='
mutation($threadId:ID!){
resolveReviewThread(input:{threadId:$threadId}){ thread{ isResolved } }
}' -F threadId={thread_id}
```
+341
View File
@@ -0,0 +1,341 @@
// Copyright (c) Microsoft. All rights reserved.
/**
* Tests for pr_limit_moderation.js.
*
* Run with: node --test .github/tests/test_pr_limit_moderation.js
*/
const { describe, it } = require('node:test');
const assert = require('node:assert/strict');
const { enforcePrLimit } = require('../scripts/pr_limit_moderation.js');
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function createContext({ author = 'community-user', authorType = 'User', labels = [], number = 123 } = {}) {
return {
repo: {
owner: 'microsoft',
repo: 'agent-framework',
},
payload: {
pull_request: {
number,
labels: labels.map((name) => ({ name })),
user: {
login: author,
type: authorType,
},
},
},
};
}
function createCore() {
const messages = [];
return {
messages,
info(message) {
messages.push(message);
},
};
}
function createGithub({
itemNumbers,
labelExists = true,
pullRequests = createPullRequestPage({ numbers: itemNumbers }),
}) {
const calls = [];
return {
calls,
async paginate(method, params) {
calls.push({ api: 'paginate', method, params });
return pullRequests;
},
rest: {
issues: {
async getLabel(params) {
calls.push({ api: 'issues.getLabel', params });
if (!labelExists) {
const error = new Error('Not Found');
error.status = 404;
throw error;
}
return { data: { name: params.name } };
},
async createLabel(params) {
calls.push({ api: 'issues.createLabel', params });
return { data: { name: params.name } };
},
async addLabels(params) {
calls.push({ api: 'issues.addLabels', params });
return { data: [] };
},
async createComment(params) {
calls.push({ api: 'issues.createComment', params });
return { data: { id: 1 } };
},
},
pulls: {
async list(params) {
calls.push({ api: 'pulls.list', params });
return { data: pullRequests };
},
async update(params) {
calls.push({ api: 'pulls.update', params });
return { data: { state: params.state } };
},
},
},
};
}
function createPullRequestPage({ author = 'community-user', numbers }) {
return numbers.map((number) => ({
number,
user: {
login: author,
},
}));
}
// ---------------------------------------------------------------------------
// PR limit enforcement
// ---------------------------------------------------------------------------
describe('PR limit enforcement', () => {
it('does not close the PR when the author is at the open PR limit', async () => {
const github = createGithub({
itemNumbers: [1, 2, 3, 4, 5, 6, 7, 8, 9, 123],
});
const result = await enforcePrLimit({
github,
context: createContext(),
core: createCore(),
exemptLabelName: 'pr-limit-exempt',
maxOpenPrs: 10,
labelName: 'too-many-prs',
});
assert.equal(result.closed, false);
assert.equal(result.openPrCount, 10);
assert.deepEqual(
github.calls.map((call) => call.api),
['paginate'],
);
});
it('counts the new PR when the pull list includes it', async () => {
const github = createGithub({
itemNumbers: [123, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
});
const result = await enforcePrLimit({
github,
context: createContext(),
core: createCore(),
exemptLabelName: 'pr-limit-exempt',
maxOpenPrs: 10,
labelName: 'too-many-prs',
});
assert.equal(result.closed, true);
assert.equal(result.openPrCount, 11);
assert.deepEqual(
github.calls.map((call) => call.api),
[
'paginate',
'issues.getLabel',
'issues.addLabels',
'issues.createComment',
'pulls.update',
],
);
});
it('counts the current PR on top of existing open PRs', async () => {
const github = createGithub({
itemNumbers: [123, ...Array.from({ length: 24 }, (_, index) => index + 1)],
pullRequests: createPullRequestPage({
numbers: [123, ...Array.from({ length: 25 }, (_, index) => index + 1)],
}),
});
const result = await enforcePrLimit({
github,
context: createContext(),
core: createCore(),
exemptLabelName: 'pr-limit-exempt',
maxOpenPrs: 10,
labelName: 'too-many-prs',
});
assert.equal(result.closed, true);
assert.equal(result.openPrCount, 26);
const comment = github.calls.find((call) => call.api === 'issues.createComment').params.body;
assert.match(comment, /This PR would put you at 26 open pull requests/);
});
it('creates the label when it does not already exist', async () => {
const github = createGithub({
itemNumbers: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 123],
labelExists: false,
});
const result = await enforcePrLimit({
github,
context: createContext(),
core: createCore(),
exemptLabelName: 'pr-limit-exempt',
maxOpenPrs: 10,
labelName: 'too-many-prs',
});
assert.equal(result.closed, true);
assert.deepEqual(
github.calls.map((call) => call.api),
[
'paginate',
'issues.getLabel',
'issues.createLabel',
'issues.addLabels',
'issues.createComment',
'pulls.update',
],
);
assert.equal(
github.calls.find((call) => call.api === 'issues.createLabel').params.name,
'too-many-prs',
);
});
it('tolerates a 422 race when creating the label', async () => {
const github = createGithub({
itemNumbers: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 123],
labelExists: false,
});
github.rest.issues.createLabel = async (params) => {
github.calls.push({ api: 'issues.createLabel', params });
const error = new Error('Validation Failed');
error.status = 422;
throw error;
};
const result = await enforcePrLimit({
github,
context: createContext(),
core: createCore(),
exemptLabelName: 'pr-limit-exempt',
maxOpenPrs: 10,
labelName: 'too-many-prs',
});
assert.equal(result.closed, true);
assert.deepEqual(
github.calls.map((call) => call.api),
[
'paginate',
'issues.getLabel',
'issues.createLabel',
'issues.addLabels',
'issues.createComment',
'pulls.update',
],
);
});
it('uses a diplomatic close message with the configured limit', async () => {
const github = createGithub({
itemNumbers: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 123],
pullRequests: createPullRequestPage({
author: 'octo-contributor',
numbers: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 123],
}),
});
await enforcePrLimit({
github,
context: createContext({ author: 'octo-contributor' }),
core: createCore(),
exemptLabelName: 'pr-limit-exempt',
maxOpenPrs: 10,
labelName: 'too-many-prs',
});
const comment = github.calls.find((call) => call.api === 'issues.createComment').params.body;
assert.match(comment, /Thank you for your contribution/);
assert.match(comment, /limit community contributors to 10 open pull requests/);
assert.match(comment, /@octo-contributor/);
assert.match(comment, /`pr-limit-exempt` label and reopen/);
});
it('does not close an exempt PR when it is reopened', async () => {
const github = createGithub({
itemNumbers: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 123],
});
const result = await enforcePrLimit({
github,
context: createContext({ labels: ['PR-LIMIT-EXEMPT'] }),
core: createCore(),
exemptLabelName: 'pr-limit-exempt',
maxOpenPrs: 10,
labelName: 'too-many-prs',
});
assert.equal(result.closed, false);
assert.equal(result.exempt, true);
assert.equal(result.openPrCount, null);
assert.deepEqual(github.calls, []);
});
it('does not close Dependabot PRs', async () => {
const github = createGithub({
itemNumbers: [123, ...Array.from({ length: 25 }, (_, index) => index + 1)],
pullRequests: createPullRequestPage({
author: 'dependabot[bot]',
numbers: [123, ...Array.from({ length: 25 }, (_, index) => index + 1)],
}),
});
const result = await enforcePrLimit({
github,
context: createContext({ author: 'dependabot[bot]', authorType: 'Bot' }),
core: createCore(),
exemptLabelName: 'pr-limit-exempt',
maxOpenPrs: 10,
labelName: 'too-many-prs',
});
assert.equal(result.closed, false);
assert.equal(result.dependabotExempt, true);
assert.equal(result.openPrCount, null);
assert.deepEqual(github.calls, []);
});
it('counts the current PR when the author has more than one page of open PRs', async () => {
const github = createGithub({
itemNumbers: [123, ...Array.from({ length: 100 }, (_, index) => index + 1)],
});
const result = await enforcePrLimit({
github,
context: createContext({ number: 123 }),
core: createCore(),
exemptLabelName: 'pr-limit-exempt',
maxOpenPrs: 10,
labelName: 'too-many-prs',
});
assert.equal(result.closed, true);
assert.equal(result.openPrCount, 101);
});
});
@@ -48,6 +48,10 @@ jobs:
filters: |
dotnet:
- 'dotnet/**'
- '!dotnet/AGENTS.md'
- '!dotnet/**/AGENTS.md'
- '!dotnet/.github/skills/*'
- '!dotnet/.github/skills/**'
cosmosdb:
- 'dotnet/src/Microsoft.Agents.AI.CosmosNoSql/**'
# The Foundry hosted-agent IT is costly (builds a container, pushes to ACR,
@@ -121,6 +125,9 @@ jobs:
python
declarative-agents
- name: Free runner disk space
uses: ./.github/actions/free-runner-disk-space
- name: Setup dotnet
uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0
with:
@@ -191,6 +198,9 @@ jobs:
python
declarative-agents
- name: Free runner disk space
uses: ./.github/actions/free-runner-disk-space
# Start Cosmos DB Emulator for all integration tests and only for unit tests when CosmosDB changes happened)
- name: Start Azure Cosmos DB Emulator
if: ${{ runner.os == 'Windows' && (needs.paths-filter.outputs.cosmosDbChanges == 'true' || (github.event_name != 'pull_request' && matrix.integration-tests)) }}
@@ -365,6 +375,9 @@ jobs:
dotnet
python
- name: Free runner disk space
uses: ./.github/actions/free-runner-disk-space
- name: Setup dotnet
uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0
with:
@@ -452,6 +465,9 @@ jobs:
python
declarative-agents
- name: Free runner disk space
uses: ./.github/actions/free-runner-disk-space
- name: Setup dotnet
uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0
with:
+4
View File
@@ -10,6 +10,10 @@ on:
branches: ["main", "feature*"]
paths:
- dotnet/**
- '!dotnet/AGENTS.md'
- '!dotnet/**/AGENTS.md'
- '!dotnet/.github/skills/*'
- '!dotnet/.github/skills/**'
- '.github/workflows/dotnet-format.yml'
concurrency:
@@ -88,6 +88,7 @@ jobs:
env:
COSMOSDB_ENDPOINT: https://localhost:8081
COSMOSDB_KEY: C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
OpenAI__ApiKey: ${{ secrets.OPENAI__APIKEY }}
OpenAI__ChatModelId: ${{ vars.OPENAI__CHATMODELID }}
OpenAI__ChatReasoningModelId: ${{ vars.OPENAI__CHATREASONINGMODELID }}
+19 -1
View File
@@ -6,16 +6,34 @@
# https://github.com/actions/labeler
name: Label pull request
on: [pull_request_target]
on:
pull_request_target:
types: [opened, synchronize, reopened, edited]
jobs:
add_label:
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
pull-requests: write
steps:
- uses: actions/labeler@f27b608878404679385c85cfa523b85ccb86e213 # v6
with:
repo-token: "${{ secrets.GH_ACTIONS_PR_WRITE }}"
- name: Checkout scripts
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
sparse-checkout: .github/scripts
fetch-depth: 1
persist-credentials: false
- name: "PR: add breaking change label from title"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
github-token: ${{ secrets.GH_ACTIONS_PR_WRITE }}
script: |
const { syncBreakingChangeLabelFromTitle } = require('./.github/scripts/title_prefix.js');
await syncBreakingChangeLabelFromTitle({ github, context, core });
+9 -50
View File
@@ -15,58 +15,17 @@ jobs:
pull-requests: write
steps:
- name: Checkout scripts
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
sparse-checkout: .github/scripts
fetch-depth: 1
persist-credentials: false
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
name: "Issue/PR: update title"
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
let prefixLabels = {
"python": "Python",
".NET": ".NET"
};
function addTitlePrefix(title, prefix)
{
// Update the title based on the label and prefix
// Check if the title starts with the prefix (case-sensitive)
if (!title.startsWith(prefix + ": ")) {
// If not, check if the first word is the label (case-insensitive)
if (title.match(new RegExp(`^${prefix}`, 'i'))) {
// If yes, replace it with the prefix (case-sensitive)
title = title.replace(new RegExp(`^${prefix}`, 'i'), prefix);
} else {
// If not, prepend the prefix to the title
title = prefix + ": " + title;
}
}
return title;
}
labelAdded = context.payload.label.name
// Check if the issue or PR has the label
if (labelAdded in prefixLabels) {
let prefix = prefixLabels[labelAdded];
switch(context.eventName) {
case 'issues':
github.rest.issues.update({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
title: addTitlePrefix(context.payload.issue.title, prefix)
});
break
case 'pull_request_target':
github.rest.pulls.update({
pull_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
title: addTitlePrefix(context.payload.pull_request.title, prefix)
});
break
default:
core.setFailed('Unrecognited eventName: ' + context.eventName);
}
}
const { updateTitleForAddedLabel } = require('./.github/scripts/title_prefix.js');
await updateTitleForAddedLabel({ github, context, core });
+83
View File
@@ -0,0 +1,83 @@
name: Limit community pull requests
on:
pull_request_target:
types: [opened, reopened]
permissions:
contents: read
issues: write
pull-requests: write
concurrency:
group: pr-limit-${{ github.repository }}-${{ github.event.pull_request.user.login }}
cancel-in-progress: false
env:
MAX_OPEN_PULL_REQUESTS: '10'
PR_LIMIT_EXEMPT_LABEL: pr-limit-exempt
TOO_MANY_PRS_LABEL: too-many-prs
jobs:
team_check:
runs-on: ubuntu-latest
outputs:
is_team_member: ${{ steps.check.outputs.is_team_member }}
steps:
- name: Checkout scripts
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
sparse-checkout: .github/scripts
fetch-depth: 1
persist-credentials: false
- name: Check PR author team membership
id: check
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
TEAM_NAME: ${{ secrets.DEVELOPER_TEAM }}
PR_NUMBER: ${{ github.event.pull_request.number }}
with:
github-token: ${{ secrets.GH_ACTIONS_PR_WRITE }}
script: |
const checkTeamMembership = require('./.github/scripts/check_team_membership.js');
const { author, isTeamMember } = await checkTeamMembership({
github,
context,
core,
teamSlug: process.env.TEAM_NAME,
issueNumber: process.env.PR_NUMBER,
});
core.setOutput('is_team_member', isTeamMember ? 'true' : 'false');
if (isTeamMember) {
core.info(`Author ${author} is a team member; skipping open PR limit.`);
} else {
core.info(`Author ${author} is not a team member; checking open PR limit.`);
}
limit_open_prs:
runs-on: ubuntu-latest
needs: team_check
if: ${{ needs.team_check.outputs.is_team_member == 'false' }}
steps:
- name: Checkout scripts
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
sparse-checkout: .github/scripts
fetch-depth: 1
persist-credentials: false
- name: Enforce open PR limit
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
github-token: ${{ secrets.GH_ACTIONS_PR_WRITE }}
script: |
const { enforcePrLimit } = require('./.github/scripts/pr_limit_moderation.js');
await enforcePrLimit({
github,
context,
core,
exemptLabelName: process.env.PR_LIMIT_EXEMPT_LABEL,
maxOpenPrs: Number.parseInt(process.env.MAX_OPEN_PULL_REQUESTS, 10),
labelName: process.env.TOO_MANY_PRS_LABEL,
});
@@ -23,6 +23,14 @@ jobs:
with:
persist-credentials: false
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install Chrome for Puppeteer
run: npx puppeteer browsers install chrome
# Checks the status of hyperlinks in all files
- name: Run linkspector
uses: umbrelladocs/action-linkspector@963b6264d7de32c904942a70b488d3407453049e # v1
+1 -1
View File
@@ -27,7 +27,7 @@ jobs:
# "Cleanup artifacts", "Agent", "Prepare", and "Upload results" are check runs
# created by an org-level GitHub App (MSDO), not by any workflow in this repo.
# They are outside our control and their transient failures should not block merges.
IGNORED_NAMES: "CodeQL,CodeQL analysis (csharp),Cleanup artifacts,Agent,Prepare,Upload results"
IGNORED_NAMES: "CodeQL,CodeQL analysis (csharp),Cleanup artifacts,Agent,Prepare,Upload results,review"
with:
script: |
const timeoutSeconds = Number(process.env.TIMEOUT_SECONDS);
@@ -6,6 +6,10 @@ on:
branches: ["main"]
paths:
- "python/**"
- "!python/AGENTS.md"
- "!python/**/AGENTS.md"
- "!python/.github/skills/*"
- "!python/.github/skills/**"
env:
# Configure a constant location for the uv cache
+42 -1
View File
@@ -474,6 +474,45 @@ jobs:
path: ./python/pytest.xml
if-no-files-found: ignore
# GitHub Copilot integration tests
python-tests-github-copilot:
name: Python Integration Tests - GitHub Copilot
runs-on: ubuntu-latest
environment: integration
timeout-minutes: 60
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
GITHUB_COPILOT_TIMEOUT: "120"
defaults:
run:
working-directory: python
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
ref: ${{ inputs.checkout-ref }}
persist-credentials: false
- name: Set up python and install the project
id: python-setup
uses: ./.github/actions/python-setup
with:
python-version: ${{ env.UV_PYTHON }}
os: ${{ runner.os }}
- name: Test with pytest (GitHub Copilot integration)
run: >
uv run pytest --import-mode=importlib
packages/github_copilot/tests
-m integration
--timeout=120 --session-timeout=900 --timeout_method thread
--retries 2 --retry-delay 5
--junitxml=pytest.xml
- name: Upload test results
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: test-results-github-copilot
path: ./python/pytest.xml
if-no-files-found: ignore
# Integration test trend report (aggregates per-job JUnit XML results)
python-integration-test-report:
name: Integration Test Report
@@ -490,6 +529,7 @@ jobs:
python-tests-foundry,
python-tests-foundry-hosting,
python-tests-cosmos,
python-tests-github-copilot,
]
runs-on: ubuntu-latest
defaults:
@@ -553,7 +593,8 @@ jobs:
python-tests-functions,
python-tests-foundry,
python-tests-foundry-hosting,
python-tests-cosmos
python-tests-cosmos,
python-tests-github-copilot
]
steps:
- name: Fail workflow if tests failed
+4
View File
@@ -31,6 +31,10 @@ jobs:
filters: |
python:
- 'python/**'
- '!python/AGENTS.md'
- '!python/**/AGENTS.md'
- '!python/.github/skills/*'
- '!python/.github/skills/**'
# run only if 'python' files were changed
- name: python tests
if: steps.filter.outputs.python == 'true'
+61
View File
@@ -40,6 +40,7 @@ jobs:
foundryChanged: ${{ steps.filter.outputs.foundry }}
foundryHostingChanged: ${{ steps.filter.outputs.foundry_hosting }}
cosmosChanged: ${{ steps.filter.outputs.cosmos }}
githubCopilotChanged: ${{ steps.filter.outputs.github_copilot }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: dorny/paths-filter@d1c1ffe0248fe513906c8e24db8ea791d46f8590 # v3
@@ -48,6 +49,10 @@ jobs:
filters: |
python:
- 'python/**'
- '!python/AGENTS.md'
- '!python/**/AGENTS.md'
- '!python/.github/skills/*'
- '!python/.github/skills/**'
- '.github/actions/setup-local-mcp-server/**'
- '.github/workflows/python-merge-tests.yml'
- '.github/workflows/python-integration-tests.yml'
@@ -85,6 +90,8 @@ jobs:
- 'python/packages/foundry_hosting/**'
cosmos:
- 'python/packages/azure-cosmos/**'
github_copilot:
- 'python/packages/github_copilot/**'
# run only if 'python' files were changed
- name: python tests
if: steps.filter.outputs.python == 'true'
@@ -658,6 +665,58 @@ jobs:
path: ./python/pytest.xml
if-no-files-found: ignore
# GitHub Copilot integration tests
python-tests-github-copilot:
name: Python Tests - GitHub Copilot Integration
needs: paths-filter
if: >
github.event_name != 'pull_request' &&
needs.paths-filter.outputs.pythonChanges == 'true' &&
(github.event_name != 'merge_group' ||
needs.paths-filter.outputs.githubCopilotChanged == 'true' ||
needs.paths-filter.outputs.coreChanged == 'true')
runs-on: ubuntu-latest
environment: integration
timeout-minutes: 60
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
GITHUB_COPILOT_TIMEOUT: "120"
defaults:
run:
working-directory: python
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Set up python and install the project
id: python-setup
uses: ./.github/actions/python-setup
with:
python-version: ${{ env.UV_PYTHON }}
os: ${{ runner.os }}
- name: Test with pytest (GitHub Copilot integration)
run: >
uv run pytest --import-mode=importlib
packages/github_copilot/tests
-m integration
--timeout=120 --session-timeout=900 --timeout_method thread
--retries 2 --retry-delay 5
--junitxml=pytest.xml
- name: Surface failing tests
if: always()
uses: pmeier/pytest-results-action@20b595761ba9bf89e115e875f8bc863f913bc8ad # v0.7.2
with:
path: ./python/pytest.xml
summary: true
display-options: fEX
fail-on-empty: false
title: GitHub Copilot integration test results
- name: Upload test results
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: test-results-github-copilot
path: ./python/pytest.xml
if-no-files-found: ignore
# Integration test trend report (aggregates per-job JUnit XML results)
python-integration-test-report:
name: Integration Test Report
@@ -674,6 +733,7 @@ jobs:
python-tests-foundry,
python-tests-foundry-hosting,
python-tests-cosmos,
python-tests-github-copilot,
]
runs-on: ubuntu-latest
defaults:
@@ -735,6 +795,7 @@ jobs:
python-tests-foundry,
python-tests-foundry-hosting,
python-tests-cosmos,
python-tests-github-copilot,
]
steps:
- name: Fail workflow if tests failed
@@ -8,6 +8,7 @@ on:
permissions:
contents: read
actions: read
pull-requests: write
jobs:
@@ -23,7 +24,7 @@ jobs:
- name: Download coverage report
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
github-token: ${{ secrets.GH_ACTIONS_PR_WRITE }}
github-token: ${{ github.token }}
run-id: ${{ github.event.workflow_run.id }}
path: ./python
merge-multiple: true
@@ -38,9 +39,9 @@ jobs:
echo "PR number file 'pr_number' is missing or empty"
exit 1
fi
PR_NUMBER=$(head -1 pr_number | tr -dc '0-9')
if [ -z "$PR_NUMBER" ]; then
echo "PR number file 'pr_number' does not contain a valid PR number"
PR_NUMBER=$(cat pr_number)
if ! [[ "$PR_NUMBER" =~ ^[0-9]+$ ]]; then
echo "::error::PR number file contains invalid content"
exit 1
fi
echo "PR_NUMBER=$PR_NUMBER" >> "$GITHUB_ENV"
@@ -48,7 +49,7 @@ jobs:
id: coverageComment
uses: MishaKav/pytest-coverage-comment@26f986d2599c288bb62f623d29c2da98609e9cd4 # v1.6.0
with:
github-token: ${{ secrets.GH_ACTIONS_PR_WRITE }}
github-token: ${{ github.token }}
issue-number: ${{ env.PR_NUMBER }}
pytest-xml-coverage-path: python/python-coverage.xml
title: "Python Test Coverage Report"
+4
View File
@@ -5,6 +5,10 @@ on:
branches: ["main", "feature*"]
paths:
- "python/**"
- "!python/AGENTS.md"
- "!python/**/AGENTS.md"
- "!python/.github/skills/*"
- "!python/.github/skills/**"
env:
# Configure a constant location for the uv cache
UV_CACHE_DIR: /tmp/.uv-cache
+3
View File
@@ -206,6 +206,7 @@ temp*/
.temp/
# AI
**/.checkpoints/
.claude/
.omc/
.omx/
@@ -213,6 +214,7 @@ WARP.md
**/memory-bank/
**/projectBrief.md
**/tmpclaude*
.kiro/
# Dependency-bound validation reports
python/scripts/dependency-*-results.json
python/scripts/dependencies/dependency-*-results.json
@@ -248,3 +250,4 @@ dotnet/filtered-*.slnx
.omx/
**/issues/
.test_*
+17 -17
View File
@@ -1,17 +1,17 @@
# Support
## How to file issues and get help
This project uses GitHub Issues to track bugs and feature requests. Please search the existing
issues before filing new issues to avoid duplicates. For new issues, file your bug or
feature request as a new Issue.
For help and questions about using this project, please create a GitHub issue.
AI Support team will support Microsoft Agent Framework issues for customers under a **Unified support agreement when the issue arises from usage of Azure AI services** (Foundry Models, Foundry Agents etc.) in conjunction with the SDK. Conversely, if customer has any other / non unified support agreement and/or Agent Framework SDK is used in a way **not involving an Azure service**, it is treated as a purely open-source tool – Microsoft’s support organization will not handle it, and users should use GitHub or forums for assistance
For Copilot Studio SDK implementation issues, customers should use GitHub Issues for assistance, as outlined above. Conversely, for prerequisites managed within the Copilot Studio portal, customers can rely on the standard Microsoft Copilot Studio support channels.
## Microsoft Support Policy
Support for this **PROJECT or PRODUCT** is limited to the resources listed above.
# Support
## How to file issues and get help
This project uses GitHub Issues to track bugs and feature requests. Please search the existing
issues before filing new issues to avoid duplicates. For new issues, file your bug or
feature request as a new Issue.
For help and questions about using this project, please create a GitHub issue.
AI Support team will support Microsoft Agent Framework issues for customers under a **Unified support agreement when the issue arises from usage of Azure AI services** (Foundry Models, Foundry Agents etc.) in conjunction with the SDK. Conversely, if customer has any other / non unified support agreement and/or Agent Framework SDK is used in a way **not involving an Azure service**, it is treated as a purely open-source tool – Microsoft’s support organization will not handle it, and users should use GitHub or forums for assistance
For Copilot Studio SDK implementation issues, customers should use GitHub Issues for assistance, as outlined above. Conversely, for prerequisites managed within the Copilot Studio portal, customers can rely on the standard Microsoft Copilot Studio support channels.
## Microsoft Support Policy
Support for this **PROJECT or PRODUCT** is limited to the resources listed above.
Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

@@ -0,0 +1,55 @@
<svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="256" height="256" transform="matrix(-1 0 0 1 256 0)" fill="white"/>
<path d="M163.006 36.0262C155.129 31.4785 145.424 31.4759 137.533 36.0284L104.002 55.3877C107.838 53.2763 112.07 52.2274 116.296 52.2248C120.633 52.2231 124.976 53.3247 128.871 55.5411L174.091 81.6479C175.11 82.2362 175.738 83.3236 175.738 84.5004V151.263C175.738 152.716 177.313 153.621 178.568 152.895L196.967 142.28C204.845 137.732 209.704 129.326 209.704 120.221V77.7299C209.704 68.6342 204.855 60.227 196.967 55.6697L190.983 52.2141C190.65 52.0008 190.313 51.7921 189.969 51.5933L163.006 36.0262Z" fill="url(#paint0_linear_481_4810)"/>
<path d="M163.006 36.0262C155.129 31.4785 145.424 31.4759 137.533 36.0284L104.002 55.3877C107.838 53.2763 112.07 52.2274 116.296 52.2248C120.633 52.2231 124.976 53.3247 128.871 55.5411L174.091 81.6479C175.11 82.2362 175.738 83.3236 175.738 84.5004V151.263C175.738 152.716 177.313 153.621 178.568 152.895L196.967 142.28C204.845 137.732 209.704 129.326 209.704 120.221V77.7299C209.704 68.6342 204.855 60.227 196.967 55.6697L190.983 52.2141C190.65 52.0008 190.313 51.7921 189.969 51.5933L163.006 36.0262Z" fill="url(#paint1_linear_481_4810)"/>
<path d="M103.548 55.6397L103.557 55.6451L104.002 55.3877C103.851 55.471 103.698 55.5531 103.548 55.6397Z" fill="url(#paint2_linear_481_4810)"/>
<path d="M103.548 55.6397L103.557 55.6451L104.002 55.3877C103.851 55.471 103.698 55.5531 103.548 55.6397Z" fill="url(#paint3_linear_481_4810)"/>
<path d="M116.308 52.2209C111.903 52.2271 107.507 53.3498 103.561 55.6366C95.6702 60.1891 90.8239 68.6019 90.8231 77.6986L90.8223 167.846C90.8222 169.786 92.871 171.041 94.599 170.16L103.523 165.611C116.573 158.959 124.788 145.549 124.788 130.902V62.9894C124.778 58.6476 129.49 55.9209 133.25 58.0698L128.879 55.5453C124.976 53.3242 120.645 52.2192 116.308 52.2209Z" fill="url(#paint4_linear_481_4810)"/>
<path d="M95.3068 221.682C103.184 226.23 112.889 226.232 120.78 221.68L154.311 202.32C150.475 204.432 146.243 205.481 142.018 205.483C137.68 205.485 133.337 204.383 129.442 202.167L84.2226 176.06C83.2035 175.472 82.5757 174.384 82.5757 173.208L82.5757 106.445C82.5757 104.992 81 104.087 79.7451 104.813L61.3465 115.428C53.4682 119.976 48.6091 128.382 48.6089 137.487L48.6089 179.978C48.6089 189.074 53.4585 197.481 61.3465 202.038L67.3303 205.494C67.6629 205.707 68.0002 205.916 68.3446 206.115L95.3068 221.682Z" fill="url(#paint5_linear_481_4810)"/>
<path d="M95.3068 221.682C103.184 226.23 112.889 226.232 120.78 221.68L154.311 202.32C150.475 204.432 146.243 205.481 142.018 205.483C137.68 205.485 133.337 204.383 129.442 202.167L84.2226 176.06C83.2035 175.472 82.5757 174.384 82.5757 173.208L82.5757 106.445C82.5757 104.992 81 104.087 79.7451 104.813L61.3465 115.428C53.4682 119.976 48.6091 128.382 48.6089 137.487L48.6089 179.978C48.6089 189.074 53.4585 197.481 61.3465 202.038L67.3303 205.494C67.6629 205.707 68.0002 205.916 68.3446 206.115L95.3068 221.682Z" fill="url(#paint6_linear_481_4810)"/>
<path d="M154.765 202.068L154.756 202.063L154.311 202.32C154.463 202.237 154.615 202.155 154.765 202.068Z" fill="url(#paint7_linear_481_4810)"/>
<path d="M154.765 202.068L154.756 202.063L154.311 202.32C154.463 202.237 154.615 202.155 154.765 202.068Z" fill="url(#paint8_linear_481_4810)"/>
<path d="M142.003 205.487C146.408 205.481 150.805 204.358 154.751 202.071C162.641 197.519 167.488 189.106 167.488 180.009L167.489 89.8618C167.489 87.9222 165.44 86.667 163.712 87.5479L154.788 92.0972C141.739 98.7494 133.523 112.159 133.523 126.806L133.523 194.719C133.533 199.06 128.821 201.787 125.061 199.638L129.432 202.163C133.336 204.384 137.666 205.489 142.003 205.487Z" fill="url(#paint9_linear_481_4810)"/>
<defs>
<linearGradient id="paint0_linear_481_4810" x1="207.413" y1="61.882" x2="148.999" y2="163.067" gradientUnits="userSpaceOnUse">
<stop stop-color="#9189F7"/>
<stop offset="1" stop-color="#4135E9"/>
</linearGradient>
<linearGradient id="paint1_linear_481_4810" x1="188.235" y1="204.189" x2="264.21" y2="152.592" gradientUnits="userSpaceOnUse">
<stop stop-color="#4F42FD"/>
<stop offset="1" stop-color="#7274FF"/>
</linearGradient>
<linearGradient id="paint2_linear_481_4810" x1="207.413" y1="61.882" x2="148.999" y2="163.067" gradientUnits="userSpaceOnUse">
<stop stop-color="#9189F7"/>
<stop offset="1" stop-color="#4135E9"/>
</linearGradient>
<linearGradient id="paint3_linear_481_4810" x1="188.235" y1="204.189" x2="264.21" y2="152.592" gradientUnits="userSpaceOnUse">
<stop stop-color="#4F42FD"/>
<stop offset="1" stop-color="#7274FF"/>
</linearGradient>
<linearGradient id="paint4_linear_481_4810" x1="93.1761" y1="128.826" x2="66.2399" y2="104.746" gradientUnits="userSpaceOnUse">
<stop offset="0.25" stop-color="#4F42FD"/>
<stop offset="1" stop-color="#2C08AC"/>
</linearGradient>
<linearGradient id="paint5_linear_481_4810" x1="50.9004" y1="195.826" x2="109.315" y2="94.6412" gradientUnits="userSpaceOnUse">
<stop stop-color="#9189F7"/>
<stop offset="1" stop-color="#4135E9"/>
</linearGradient>
<linearGradient id="paint6_linear_481_4810" x1="70.0787" y1="53.5188" x2="-5.89657" y2="105.116" gradientUnits="userSpaceOnUse">
<stop stop-color="#4F42FD"/>
<stop offset="1" stop-color="#7274FF"/>
</linearGradient>
<linearGradient id="paint7_linear_481_4810" x1="50.9004" y1="195.826" x2="109.315" y2="94.6412" gradientUnits="userSpaceOnUse">
<stop stop-color="#9189F7"/>
<stop offset="1" stop-color="#4135E9"/>
</linearGradient>
<linearGradient id="paint8_linear_481_4810" x1="70.0787" y1="53.5188" x2="-5.89657" y2="105.116" gradientUnits="userSpaceOnUse">
<stop stop-color="#4F42FD"/>
<stop offset="1" stop-color="#7274FF"/>
</linearGradient>
<linearGradient id="paint9_linear_481_4810" x1="165.135" y1="128.882" x2="192.072" y2="152.962" gradientUnits="userSpaceOnUse">
<stop offset="0.25" stop-color="#4F42FD"/>
<stop offset="1" stop-color="#2C08AC"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.8 KiB

@@ -0,0 +1,5 @@
<svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="256" height="256" transform="matrix(-1 0 0 1 256 0)" fill="white"/>
<path d="M167.489 89.8618C167.489 87.9224 165.441 86.667 163.713 87.5473L154.788 92.0971C141.739 98.7493 133.523 112.159 133.523 126.806V194.718C133.533 199.053 128.837 201.777 125.08 199.647L84.2227 176.06C83.2036 175.472 82.5762 174.384 82.5762 173.207V106.445C82.5759 104.992 80.9999 104.087 79.7451 104.813L61.3467 115.428C53.4685 119.976 48.6096 128.382 48.6094 137.487V179.978C48.6094 189.074 53.4588 197.481 61.3467 202.039L67.3301 205.494C67.6627 205.707 68.0004 205.916 68.3447 206.115L95.3066 221.682C103.184 226.23 112.889 226.232 120.779 221.679L154.312 202.32C154.271 202.342 154.23 202.362 154.189 202.384C154.283 202.333 154.375 202.281 154.468 202.229L154.312 202.32C154.463 202.237 154.615 202.154 154.765 202.068L154.762 202.065C162.646 197.511 167.487 189.102 167.488 180.009L167.489 89.8618Z" fill="black"/>
<path d="M163.007 36.0259C155.13 31.4781 145.424 31.4763 137.533 36.0288L104.002 55.3882C104.029 55.3732 104.057 55.359 104.084 55.3442C104.02 55.3794 103.956 55.4159 103.893 55.4516L104.002 55.3882C103.851 55.4714 103.699 55.5536 103.549 55.6401L103.552 55.6411C95.6664 60.1948 90.8241 68.6053 90.8232 77.6987L90.8223 167.846C90.8223 169.786 92.8707 171.04 94.5986 170.16L103.523 165.611C116.573 158.959 124.788 145.549 124.788 130.902V62.9897C124.778 58.6479 129.49 55.9211 133.25 58.0698L174.091 81.6479C175.11 82.2363 175.737 83.3238 175.737 84.5005V151.263C175.738 152.716 177.314 153.621 178.568 152.895L196.967 142.28C204.845 137.732 209.704 129.326 209.704 120.221V77.73C209.704 68.6343 204.855 60.2267 196.967 55.6694L190.982 52.2143C190.65 52.0011 190.313 51.792 189.969 51.5932L163.007 36.0259Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

@@ -0,0 +1,5 @@
<svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="256" height="256" transform="matrix(-1 0 0 1 256 0)" fill="black"/>
<path d="M167.489 89.8618C167.489 87.9224 165.441 86.667 163.713 87.5473L154.788 92.0971C141.739 98.7493 133.523 112.159 133.523 126.806V194.718C133.533 199.053 128.837 201.777 125.08 199.647L84.2227 176.06C83.2036 175.472 82.5762 174.384 82.5762 173.207V106.445C82.5759 104.992 80.9999 104.087 79.7451 104.813L61.3467 115.428C53.4685 119.976 48.6096 128.382 48.6094 137.487V179.978C48.6094 189.074 53.4588 197.481 61.3467 202.039L67.3301 205.494C67.6627 205.707 68.0004 205.916 68.3447 206.115L95.3066 221.682C103.184 226.23 112.889 226.232 120.779 221.679L154.312 202.32C154.271 202.342 154.23 202.362 154.189 202.384C154.283 202.333 154.375 202.281 154.468 202.229L154.312 202.32C154.463 202.237 154.615 202.154 154.765 202.068L154.762 202.065C162.646 197.511 167.487 189.102 167.488 180.009L167.489 89.8618Z" fill="white"/>
<path d="M163.007 36.0259C155.13 31.4781 145.424 31.4763 137.533 36.0288L104.002 55.3882C104.029 55.3732 104.057 55.359 104.084 55.3442C104.02 55.3794 103.956 55.4159 103.893 55.4516L104.002 55.3882C103.851 55.4714 103.699 55.5536 103.549 55.6401L103.552 55.6411C95.6664 60.1948 90.8241 68.6053 90.8232 77.6987L90.8223 167.846C90.8223 169.786 92.8707 171.04 94.5986 170.16L103.523 165.611C116.573 158.959 124.788 145.549 124.788 130.902V62.9897C124.778 58.6479 129.49 55.9211 133.25 58.0698L174.091 81.6479C175.11 82.2363 175.737 83.3238 175.737 84.5005V151.263C175.738 152.716 177.314 153.621 178.568 152.895L196.967 142.28C204.845 137.732 209.704 129.326 209.704 120.221V77.73C209.704 68.6343 204.855 60.2267 196.967 55.6694L190.982 52.2143C190.65 52.0011 190.313 51.792 189.969 51.5932L163.007 36.0259Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

@@ -0,0 +1,4 @@
<svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="256" height="256" transform="matrix(-1 0 0 1 256 0)" fill="white"/>
<path d="M48.6094 179.978V137.487C48.6096 128.382 53.4685 119.976 61.3467 115.428L79.7451 104.813C80.9999 104.087 82.5759 104.992 82.5762 106.445V173.207C82.5762 174.384 83.2036 175.472 84.2227 176.06L125.08 199.647C128.837 201.777 133.533 199.053 133.523 194.718V126.806C133.523 112.388 141.484 99.1684 154.18 92.4135L154.788 92.0971L163.713 87.5473C165.441 86.667 167.489 87.9224 167.489 89.8618L167.488 180.009L167.474 180.86C167.181 189.624 162.399 197.653 154.762 202.065L154.765 202.068C154.615 202.154 154.463 202.237 154.312 202.32L154.468 202.229C154.375 202.281 154.283 202.333 154.189 202.384C154.23 202.362 154.271 202.342 154.312 202.32L120.779 221.679L120.034 222.092C112.534 226.093 103.539 226.092 96.0508 222.095L95.3066 221.682L68.3447 206.115C68.0004 205.916 67.6627 205.707 67.3301 205.494L61.3467 202.039C53.7053 197.624 48.9149 189.595 48.623 180.829L48.6094 179.978ZM175.737 84.5005C175.737 83.3974 175.186 82.3728 174.277 81.7641L174.091 81.6479L133.25 58.0698C129.49 55.9211 124.778 58.6479 124.788 62.9897V130.902L124.782 131.587C124.53 145.966 116.369 159.063 103.523 165.611L94.5986 170.16L94.4355 170.236C92.799 170.938 90.9459 169.803 90.8281 168.026L90.8223 167.846L90.8232 77.6987C90.8241 68.6053 95.6664 60.1948 103.552 55.6411L103.549 55.6401C103.699 55.5536 103.851 55.4714 104.002 55.3882L103.893 55.4516C103.956 55.4159 104.02 55.3794 104.084 55.3442C104.057 55.359 104.029 55.3732 104.002 55.3882L137.533 36.0288C145.424 31.4763 155.13 31.4781 163.007 36.0259L189.969 51.5932C190.313 51.792 190.65 52.0011 190.982 52.2143L196.967 55.6694C204.855 60.2267 209.704 68.6343 209.704 77.73V120.221L209.689 121.073C209.397 129.848 204.599 137.874 196.967 142.28L178.568 152.895C177.314 153.621 175.738 152.716 175.737 151.263V84.5005ZM176.814 149.866L176.819 149.864L176.832 149.856C176.826 149.859 176.82 149.862 176.814 149.866ZM137.023 194.71L137.019 195.038C136.802 201.878 129.341 206.087 123.354 202.692L123.33 202.678L82.4727 179.091C80.3698 177.877 79.0762 175.634 79.0762 173.207V109.239L63.0957 118.458C56.5124 122.259 52.3745 129.183 52.1221 136.752L52.1094 137.487V179.978C52.1094 187.823 56.2909 195.075 63.0957 199.007H63.0967L69.0801 202.462L69.1504 202.503L69.2188 202.547C69.5212 202.741 69.8111 202.92 70.0947 203.083L97.0566 218.651L97.6982 219.007C104.372 222.569 112.434 222.453 119.029 218.648L152.514 199.316L152.512 199.312C152.581 199.274 152.65 199.236 152.752 199.178L152.753 199.181C152.775 199.169 152.796 199.158 152.815 199.147L153.011 199.035C159.81 195.107 163.987 187.854 163.988 180.009V91.3344L156.378 95.2153C144.501 101.27 137.023 113.475 137.023 126.806V194.71ZM94.3223 166.372L101.934 162.493C113.811 156.438 121.288 144.233 121.288 130.902V62.9897C121.278 55.9521 128.901 51.5532 134.986 55.0307L135 55.0385L175.841 78.6167L176.036 78.7339C178.023 79.9714 179.237 82.15 179.237 84.5005V148.468L195.217 139.249C202.013 135.326 206.204 128.075 206.204 120.221V77.73C206.204 69.8848 202.022 62.6318 195.217 58.6997L189.232 55.2456L189.162 55.2046L189.094 55.1606C188.788 54.9649 188.5 54.787 188.219 54.6245L161.257 39.0571C154.463 35.135 146.091 35.1311 139.282 39.0591L139.283 39.06L105.765 58.4106L105.767 58.4135C105.713 58.443 105.723 58.4383 105.602 58.5063L105.6 58.5034C105.535 58.5391 105.481 58.5691 105.433 58.5962L105.302 58.6723C98.5016 62.5995 94.324 69.8532 94.3232 77.6987L94.3223 166.372Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

@@ -0,0 +1,4 @@
<svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="256" height="256" transform="matrix(-1 0 0 1 256 0)" fill="black"/>
<path d="M48.6094 179.978V137.487C48.6096 128.382 53.4685 119.976 61.3467 115.428L79.7451 104.813C80.9999 104.087 82.5759 104.992 82.5762 106.445V173.207C82.5762 174.384 83.2036 175.472 84.2227 176.06L125.08 199.647C128.837 201.777 133.533 199.053 133.523 194.718V126.806C133.523 112.388 141.484 99.1684 154.18 92.4135L154.788 92.0971L163.713 87.5473C165.441 86.667 167.489 87.9224 167.489 89.8618L167.488 180.009L167.474 180.86C167.181 189.624 162.399 197.653 154.762 202.065L154.765 202.068C154.615 202.154 154.463 202.237 154.312 202.32L154.468 202.229C154.375 202.281 154.283 202.333 154.189 202.384C154.23 202.362 154.271 202.342 154.312 202.32L120.779 221.679L120.034 222.092C112.534 226.093 103.539 226.092 96.0508 222.095L95.3066 221.682L68.3447 206.115C68.0004 205.916 67.6627 205.707 67.3301 205.494L61.3467 202.039C53.7053 197.624 48.9149 189.595 48.623 180.829L48.6094 179.978ZM175.737 84.5005C175.737 83.3974 175.186 82.3728 174.277 81.7641L174.091 81.6479L133.25 58.0698C129.49 55.9211 124.778 58.6479 124.788 62.9897V130.902L124.782 131.587C124.53 145.966 116.369 159.063 103.523 165.611L94.5986 170.16L94.4355 170.236C92.799 170.938 90.9459 169.803 90.8281 168.026L90.8223 167.846L90.8232 77.6987C90.8241 68.6053 95.6664 60.1948 103.552 55.6411L103.549 55.6401C103.699 55.5536 103.851 55.4714 104.002 55.3882L103.893 55.4516C103.956 55.4159 104.02 55.3794 104.084 55.3442C104.057 55.359 104.029 55.3732 104.002 55.3882L137.533 36.0288C145.424 31.4763 155.13 31.4781 163.007 36.0259L189.969 51.5932C190.313 51.792 190.65 52.0011 190.982 52.2143L196.967 55.6694C204.855 60.2267 209.704 68.6343 209.704 77.73V120.221L209.689 121.073C209.397 129.848 204.599 137.874 196.967 142.28L178.568 152.895C177.314 153.621 175.738 152.716 175.737 151.263V84.5005ZM176.814 149.866L176.819 149.864L176.832 149.856C176.826 149.859 176.82 149.862 176.814 149.866ZM137.023 194.71L137.019 195.038C136.802 201.878 129.341 206.087 123.354 202.692L123.33 202.678L82.4727 179.091C80.3698 177.877 79.0762 175.634 79.0762 173.207V109.239L63.0957 118.458C56.5124 122.259 52.3745 129.183 52.1221 136.752L52.1094 137.487V179.978C52.1094 187.823 56.2909 195.075 63.0957 199.007H63.0967L69.0801 202.462L69.1504 202.503L69.2188 202.547C69.5212 202.741 69.8111 202.92 70.0947 203.083L97.0566 218.651L97.6982 219.007C104.372 222.569 112.434 222.453 119.029 218.648L152.514 199.316L152.512 199.312C152.581 199.274 152.65 199.236 152.752 199.178L152.753 199.181C152.775 199.169 152.796 199.158 152.815 199.147L153.011 199.035C159.81 195.107 163.987 187.854 163.988 180.009V91.3344L156.378 95.2153C144.501 101.27 137.023 113.475 137.023 126.806V194.71ZM94.3223 166.372L101.934 162.493C113.811 156.438 121.288 144.233 121.288 130.902V62.9897C121.278 55.9521 128.901 51.5532 134.986 55.0307L135 55.0385L175.841 78.6167L176.036 78.7339C178.023 79.9714 179.237 82.15 179.237 84.5005V148.468L195.217 139.249C202.013 135.326 206.204 128.075 206.204 120.221V77.73C206.204 69.8848 202.022 62.6318 195.217 58.6997L189.232 55.2456L189.162 55.2046L189.094 55.1606C188.788 54.9649 188.5 54.787 188.219 54.6245L161.257 39.0571C154.463 35.135 146.091 35.1311 139.282 39.0591L139.283 39.06L105.765 58.4106L105.767 58.4135C105.713 58.443 105.723 58.4383 105.602 58.5063L105.6 58.5034C105.535 58.5391 105.481 58.5691 105.433 58.5962L105.302 58.6723C98.5016 62.5995 94.324 69.8532 94.3232 77.6987L94.3223 166.372Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

After

Width:  |  Height:  |  Size: 1.5 MiB

@@ -1125,7 +1125,7 @@ Naming (Python): N/A (Composable Components)
Supports: N
Observation: No explicit middleware/filters; modularity allows composable units but no dedicated interception hooks or callbacks for custom reading/modification mid-execution.
For more details, see the official documentation: [Atomic Agents Docs](https://brainblend-ai.github.io/atomic-agents/). No specific code examples available for interception.
No specific code examples available for interception.
#### Smolagents (Hugging Face)
+1
View File
@@ -0,0 +1 @@
../../../.github/skills/pull-requests
+4
View File
@@ -10,6 +10,10 @@ See `./.github/skills/build-and-test/SKILL.md` for detailed instructions on buil
See `./.github/skills/project-structure/SKILL.md` for an overview of the project structure.
## Pull Requests
See `./.github/skills/pull-requests/SKILL.md` for guidance on writing PR descriptions and handling/resolving PR review comments.
### Core types
- `AIAgent`: The abstract base class that all agents derive from, providing common methods for interacting with an agent.
+25 -19
View File
@@ -21,15 +21,16 @@
<PackageVersion Include="Aspire.Hosting.Azure.CognitiveServices" Version="$(AspireAppHostSdkVersion)" />
<PackageVersion Include="Aspire.Microsoft.Azure.Cosmos" Version="$(AspireAppHostSdkVersion)" />
<PackageVersion Include="CommunityToolkit.Aspire.OllamaSharp" Version="13.0.0" />
<PackageVersion Include="MessagePack" Version="3.1.7" /> <!-- Transitive dependency of Aspire pinned to newer version due to vulnerability in 2.5.192 -->
<!-- Azure.* -->
<PackageVersion Include="Azure.AI.AgentServer.Core" Version="1.0.0-beta.23" />
<PackageVersion Include="Azure.AI.AgentServer.Invocations" Version="1.0.0-beta.3" />
<PackageVersion Include="Azure.AI.AgentServer.Responses" Version="1.0.0-beta.4" />
<PackageVersion Include="Azure.AI.AgentServer.Core" Version="1.0.0-beta.25" />
<PackageVersion Include="Azure.AI.AgentServer.Invocations" Version="1.0.0-beta.4" />
<PackageVersion Include="Azure.AI.AgentServer.Responses" Version="1.0.0-beta.5" />
<PackageVersion Include="Azure.Search.Documents" Version="12.0.0" />
<PackageVersion Include="Azure.AI.Projects" Version="2.1.0-beta.2" />
<PackageVersion Include="Azure.AI.Agents.Persistent" Version="1.2.0-beta.10" />
<PackageVersion Include="Azure.AI.OpenAI" Version="2.9.0-beta.1" />
<PackageVersion Include="Azure.Core" Version="1.55.0" />
<PackageVersion Include="Azure.Core" Version="1.56.0" />
<PackageVersion Include="Azure.Identity" Version="1.21.0" />
<PackageVersion Include="DotNetEnv" Version="3.1.1" />
<PackageVersion Include="Azure.Monitor.OpenTelemetry.Exporter" Version="1.5.0" />
@@ -41,19 +42,19 @@
<!-- Newtonsoft.Json -->
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
<!-- System.* -->
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="10.0.6" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="10.0.8" />
<PackageVersion Include="Microsoft.Bcl.HashCode" Version="6.0.0" />
<PackageVersion Include="Microsoft.Bcl.Memory" Version="10.0.5" />
<PackageVersion Include="System.ClientModel" Version="1.11.0" />
<PackageVersion Include="System.ClientModel" Version="1.12.0" />
<PackageVersion Include="System.CodeDom" Version="10.0.0" />
<PackageVersion Include="System.Collections.Immutable" Version="10.0.1" />
<PackageVersion Include="System.CommandLine" Version="2.0.0-rc.2.25502.107" />
<PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="10.0.6" />
<PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="10.0.8" />
<PackageVersion Include="System.Linq.AsyncEnumerable" Version="10.0.5" />
<PackageVersion Include="System.Net.Http.Json" Version="10.0.0" />
<PackageVersion Include="System.Net.ServerSentEvents" Version="10.0.5" />
<PackageVersion Include="System.Text.Json" Version="10.0.6" />
<PackageVersion Include="System.Threading.Channels" Version="10.0.6" />
<PackageVersion Include="System.Text.Json" Version="10.0.8" />
<PackageVersion Include="System.Threading.Channels" Version="10.0.8" />
<PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.6.3" />
<PackageVersion Include="System.Net.Security" Version="4.3.2" />
<!-- OpenTelemetry -->
@@ -72,12 +73,12 @@
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0" />
<PackageVersion Include="Swashbuckle.AspNetCore.SwaggerUI" Version="10.0.0" />
<!-- Microsoft.Extensions.* -->
<PackageVersion Include="Microsoft.Extensions.AI" Version="10.5.1" />
<PackageVersion Include="Microsoft.Extensions.AI.Abstractions" Version="10.5.1" />
<PackageVersion Include="Microsoft.Extensions.AI.Evaluation" Version="10.4.0" />
<PackageVersion Include="Microsoft.Extensions.AI.Evaluation.Quality" Version="10.4.0" />
<PackageVersion Include="Microsoft.Extensions.AI.Evaluation.Safety" Version="10.3.0-preview.1.26109.11" />
<PackageVersion Include="Microsoft.Extensions.AI.OpenAI" Version="10.5.1" />
<PackageVersion Include="Microsoft.Extensions.AI" Version="10.6.0" />
<PackageVersion Include="Microsoft.Extensions.AI.Abstractions" Version="10.6.0" />
<PackageVersion Include="Microsoft.Extensions.AI.Evaluation" Version="10.6.0" />
<PackageVersion Include="Microsoft.Extensions.AI.Evaluation.Quality" Version="10.6.0" />
<PackageVersion Include="Microsoft.Extensions.AI.Evaluation.Safety" Version="10.6.0" />
<PackageVersion Include="Microsoft.Extensions.AI.OpenAI" Version="10.6.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Compliance.Abstractions" Version="10.5.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="10.0.1" />
@@ -86,12 +87,12 @@
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="10.0.1" />
<PackageVersion Include="Microsoft.Extensions.Configuration.UserSecrets" Version="10.0.1" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.1" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.6" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.8" />
<PackageVersion Include="Microsoft.Extensions.FileSystemGlobbing" Version="10.0.6" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="10.0.1" />
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.1" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.6" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.8" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="10.0.1" />
<PackageVersion Include="Microsoft.Extensions.ServiceDiscovery" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.VectorData.Abstractions" Version="9.7.0" />
@@ -99,7 +100,7 @@
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.InMemory" Version="1.67.0-preview" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Qdrant" Version="1.67.0-preview" />
<!-- Agent SDKs -->
<PackageVersion Include="GitHub.Copilot.SDK" Version="1.0.0-beta.2" />
<PackageVersion Include="GitHub.Copilot.SDK" Version="1.0.0" />
<PackageVersion Include="Microsoft.Agents.CopilotStudio.Client" Version="1.3.171-beta" />
<!-- M365 Agents SDK -->
<PackageVersion Include="AdaptiveCards" Version="3.1.0" />
@@ -109,7 +110,7 @@
<PackageVersion Include="A2A" Version="1.0.0-preview2" />
<PackageVersion Include="A2A.AspNetCore" Version="1.0.0-preview2" />
<!-- MCP -->
<PackageVersion Include="ModelContextProtocol" Version="1.1.0" />
<PackageVersion Include="ModelContextProtocol" Version="1.2.0" />
<!-- Hyperlight -->
<PackageVersion Include="Hyperlight.HyperlightSandbox.Api" Version="0.4.0" />
<PackageVersion Include="Hyperlight.HyperlightSandbox.Guest.Python" Version="0.4.0" />
@@ -138,10 +139,15 @@
<PackageVersion Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="2.1.0" />
<PackageVersion Include="Microsoft.Azure.Functions.Worker.Extensions.Mcp" Version="1.0.0" />
<PackageVersion Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.7" />
<!-- Valkey -->
<!-- Redis -->
<PackageVersion Include="StackExchange.Redis" Version="2.10.1" />
<!-- Valkey -->
<PackageVersion Include="Valkey.Glide" Version="1.1.0" />
<!-- Console UX -->
<PackageVersion Include="Spectre.Console" Version="0.49.1" />
<!-- AWS -->
<PackageVersion Include="AWSSDK.Extensions.Bedrock.MEAI" Version="4.0.6.10" />
<!-- Test -->
<PackageVersion Include="FluentAssertions" Version="8.8.0" />
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Condition="'$(TargetFramework)' == 'net8.0'" Version="8.0.22" />
+13 -1
View File
@@ -24,7 +24,6 @@
<File Path="samples/02-agents/AgentProviders/README.md" />
<Project Path="samples/02-agents/AgentProviders/Agent_With_A2A/Agent_With_A2A.csproj" />
<Project Path="samples/02-agents/AgentProviders/Agent_With_Anthropic/Agent_With_Anthropic.csproj" />
<Project Path="samples/02-agents/AgentProviders/Agent_With_AzureAIAgentsPersistent/Agent_With_AzureAIAgentsPersistent.csproj" />
<Project Path="samples/02-agents/AgentProviders/Agent_With_AzureAIProject/Agent_With_AzureAIProject.csproj" />
<Project Path="samples/02-agents/AgentProviders/Agent_With_AzureFoundryModel/Agent_With_AzureFoundryModel.csproj" />
<Project Path="samples/02-agents/AgentProviders/Agent_With_AzureOpenAIChatCompletion/Agent_With_AzureOpenAIChatCompletion.csproj" />
@@ -129,6 +128,7 @@
<Project Path="samples/02-agents/Harness/Harness_Step02_Research_WithBackgroundAgents/Harness_Step02_Research_WithBackgroundAgents.csproj" />
<Project Path="samples/02-agents/Harness/Harness_Step03_DataProcessing/Harness_Step03_DataProcessing.csproj" />
<Project Path="samples/02-agents/Harness/Harness_Step04_CodeExecution/Harness_Step04_CodeExecution.csproj" />
<Project Path="samples/02-agents/Harness/Harness_Step05_Loop/Harness_Step05_Loop.csproj" />
</Folder>
<Folder Name="/Samples/02-agents/AGUI/Step05_StateManagement/">
<Project Path="samples/02-agents/AGUI/Step05_StateManagement/Client/Client.csproj" />
@@ -174,6 +174,7 @@
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step23_LocalMCP/Agent_Step23_LocalMCP.csproj" />
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step24_CodeInterpreterFileDownload/Agent_Step24_CodeInterpreterFileDownload.csproj" />
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step25_FoundryToolboxMcp/Agent_Step25_FoundryToolboxMcp.csproj" />
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step26_FoundryToolboxMcpSkills/Agent_Step26_FoundryToolboxMcpSkills.csproj" />
</Folder>
<Folder Name="/Samples/02-agents/Evaluation/">
<Project Path="samples/02-agents/Evaluation/Evaluation_CustomEvals/Evaluation_CustomEvals.csproj" />
@@ -193,6 +194,8 @@
<Project Path="samples/02-agents/AgentWithMemory/AgentWithMemory_Step02_MemoryUsingMem0/AgentWithMemory_Step02_MemoryUsingMem0.csproj" />
<Project Path="samples/02-agents/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/AgentWithMemory_Step04_MemoryUsingFoundry.csproj" />
<Project Path="samples/02-agents/AgentWithMemory/AgentWithMemory_Step05_BoundedChatHistory/AgentWithMemory_Step05_BoundedChatHistory.csproj" />
<Project Path="samples/02-agents/AgentWithMemory/AgentWithMemory_Step03_MemoryUsingValkey/AgentWithMemory_Step03_MemoryUsingValkey.csproj" />
<Project Path="samples/02-agents/AgentWithMemory/AgentWithMemory_Step03_MemoryUsingValkey_Bedrock/AgentWithMemory_Step03_MemoryUsingValkey_Bedrock.csproj" />
</Folder>
<Folder Name="/Samples/02-agents/AgentWithOpenAI/">
<File Path="samples/02-agents/AgentWithOpenAI/README.md" />
@@ -343,6 +346,12 @@
<Folder Name="/Samples/04-hosting/FoundryHostedAgents/responses/Hosted-Toolbox/">
<Project Path="samples/04-hosting/FoundryHostedAgents/responses/Hosted-Toolbox/HostedToolbox.csproj" />
</Folder>
<Folder Name="/Samples/04-hosting/FoundryHostedAgents/responses/Hosted-Toolbox-AuthPaths/">
<Project Path="samples/04-hosting/FoundryHostedAgents/responses/Hosted-Toolbox-AuthPaths/Hosted-Toolbox-AuthPaths.csproj" />
</Folder>
<Folder Name="/Samples/04-hosting/FoundryHostedAgents/responses/Hosted-ToolboxMcpSkills/">
<Project Path="samples/04-hosting/FoundryHostedAgents/responses/Hosted-ToolboxMcpSkills/HostedToolboxMcpSkills.csproj" />
</Folder>
<Folder Name="/Samples/04-hosting/FoundryHostedAgents/responses/Hosted-AzureSearchRag/">
<Project Path="samples/04-hosting/FoundryHostedAgents/responses/Hosted-AzureSearchRag/HostedAzureSearchRag.csproj" />
</Folder>
@@ -604,6 +613,7 @@
<Project Path="src/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.csproj" />
<Project Path="src/Microsoft.Agents.AI.Hosting.AzureFunctions/Microsoft.Agents.AI.Hosting.AzureFunctions.csproj" />
<Project Path="src/Microsoft.Agents.AI.Hosting.OpenAI/Microsoft.Agents.AI.Hosting.OpenAI.csproj" />
<Project Path="src/Microsoft.Agents.AI.Hosting.AspNetCore/Microsoft.Agents.AI.Hosting.AspNetCore.csproj" />
<Project Path="src/Microsoft.Agents.AI.Hosting/Microsoft.Agents.AI.Hosting.csproj" />
<Project Path="src/Microsoft.Agents.AI.Hyperlight/Microsoft.Agents.AI.Hyperlight.csproj" />
<Project Path="src/Microsoft.Agents.AI.Mcp/Microsoft.Agents.AI.Mcp.csproj" />
@@ -617,6 +627,7 @@
<Project Path="src/Microsoft.Agents.AI.Workflows.Generators/Microsoft.Agents.AI.Workflows.Generators.csproj" />
<Project Path="src/Microsoft.Agents.AI.Workflows/Microsoft.Agents.AI.Workflows.csproj" />
<Project Path="src/Microsoft.Agents.AI/Microsoft.Agents.AI.csproj" />
<Project Path="src/Microsoft.Agents.AI.Valkey/Microsoft.Agents.AI.Valkey.csproj" />
</Folder>
<Folder Name="/Tests/" />
<Folder Name="/Tests/IntegrationTests/">
@@ -670,5 +681,6 @@
<Project Path="tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.Workflows.Generators.UnitTests/Microsoft.Agents.AI.Workflows.Generators.UnitTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.Workflows.UnitTests/Microsoft.Agents.AI.Workflows.UnitTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.Valkey.UnitTests/Microsoft.Agents.AI.Valkey.UnitTests.csproj" />
</Folder>
</Solution>
+4 -1
View File
@@ -20,14 +20,17 @@
"src\\Microsoft.Agents.AI.Hosting.A2A.AspNetCore\\Microsoft.Agents.AI.Hosting.A2A.AspNetCore.csproj",
"src\\Microsoft.Agents.AI.Hosting.A2A\\Microsoft.Agents.AI.Hosting.A2A.csproj",
"src\\Microsoft.Agents.AI.Hosting.AGUI.AspNetCore\\Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.csproj",
"src\\Microsoft.Agents.AI.Hosting.AzureFunctions\\Microsoft.Agents.AI.Hosting.AzureFunctions.csproj",
"src\\Microsoft.Agents.AI.Hosting.AspNetCore\\Microsoft.Agents.AI.Hosting.AspNetCore.csproj",
"src\\Microsoft.Agents.AI.Hosting.AzureFunctions\\Microsoft.Agents.AI.Hosting.AzureFunctions.csproj",
"src\\Microsoft.Agents.AI.Hosting.OpenAI\\Microsoft.Agents.AI.Hosting.OpenAI.csproj",
"src\\Microsoft.Agents.AI.Hosting\\Microsoft.Agents.AI.Hosting.csproj",
"src\\Microsoft.Agents.AI.Mcp\\Microsoft.Agents.AI.Mcp.csproj",
"src\\Microsoft.Agents.AI.Mem0\\Microsoft.Agents.AI.Mem0.csproj",
"src\\Microsoft.Agents.AI.OpenAI\\Microsoft.Agents.AI.OpenAI.csproj",
"src\\Microsoft.Agents.AI.Purview\\Microsoft.Agents.AI.Purview.csproj",
"src\\Microsoft.Agents.AI.Tools.Shell\\Microsoft.Agents.AI.Tools.Shell.csproj",
"src\\Microsoft.Agents.AI.Workflows.Declarative.Foundry\\Microsoft.Agents.AI.Workflows.Declarative.Foundry.csproj",
"src\\Microsoft.Agents.AI.Workflows.Declarative.Mcp\\Microsoft.Agents.AI.Workflows.Declarative.Mcp.csproj",
"src\\Microsoft.Agents.AI.Workflows.Declarative\\Microsoft.Agents.AI.Workflows.Declarative.csproj",
"src\\Microsoft.Agents.AI.Workflows.Generators\\Microsoft.Agents.AI.Workflows.Generators.csproj",
"src\\Microsoft.Agents.AI.Workflows\\Microsoft.Agents.AI.Workflows.csproj",
@@ -50,19 +50,6 @@ internal static class AgentsSamples
],
},
new SampleDefinition
{
Name = "Agent_With_AzureAIAgentsPersistent",
ProjectPath = "samples/02-agents/AgentProviders/Agent_With_AzureAIAgentsPersistent",
RequiredEnvironmentVariables = ["AZURE_AI_PROJECT_ENDPOINT"],
OptionalEnvironmentVariables = ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
ExpectedOutputDescription =
[
"The output should contain a joke about a pirate.",
"The output should not contain error messages or stack traces.",
],
},
new SampleDefinition
{
Name = "Agent_With_AzureAIProject",
+3 -3
View File
@@ -1,14 +1,14 @@
<Project>
<PropertyGroup>
<!-- Central version prefix - applies to all nuget packages. -->
<VersionPrefix>1.7.0</VersionPrefix>
<VersionPrefix>1.10.0</VersionPrefix>
<RCNumber>1</RCNumber>
<DateSuffix>260526</DateSuffix>
<DateSuffix>260610</DateSuffix>
<PackageVersion Condition="'$(IsReleaseCandidate)' == 'true'">$(VersionPrefix)-rc$(RCNumber)</PackageVersion>
<PackageVersion Condition="'$(IsReleaseCandidate)' != 'true' AND '$(VersionSuffix)' != ''">$(VersionPrefix)-$(VersionSuffix).$(DateSuffix).1</PackageVersion>
<PackageVersion Condition="'$(IsReleaseCandidate)' != 'true' AND '$(VersionSuffix)' == ''">$(VersionPrefix)-preview.$(DateSuffix).1</PackageVersion>
<PackageVersion Condition="'$(IsReleased)' == 'true'">$(VersionPrefix)</PackageVersion>
<GitTag>1.7.0</GitTag>
<GitTag>1.10.0</GitTag>
<Configurations>Debug;Release;Publish</Configurations>
<IsPackable>true</IsPackable>
@@ -10,6 +10,11 @@ WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpClient().AddLogging();
builder.Services.AddAGUI();
// WARNING: When adding session persistence (e.g., WithInMemorySessionStore), or running in production,
// make sure to also register a SessionIsolationKeyProvider to scope sessions by principal in multi-user
// deployments, e.g.:
// builder.Services.UseClaimsBasedSessionIsolation(new() { ClaimType = ClaimTypes.NameIdentifier });
WebApplication app = builder.Build();
string endpoint = builder.Configuration["AZURE_OPENAI_ENDPOINT"]
@@ -14,6 +14,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Hosting.AGUI.AspNetCore\Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.csproj" />
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Hosting.AspNetCore\Microsoft.Agents.AI.Hosting.AspNetCore.csproj" />
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.OpenAI\Microsoft.Agents.AI.OpenAI.csproj" />
</ItemGroup>
@@ -16,6 +16,11 @@ builder.Services.ConfigureHttpJsonOptions(options =>
options.SerializerOptions.TypeInfoResolverChain.Add(SampleJsonSerializerContext.Default));
builder.Services.AddAGUI();
// WARNING: When adding session persistence (e.g., WithInMemorySessionStore), or running in production,
// make sure to also register a SessionIsolationKeyProvider to scope sessions by principal in multi-user
// deployments, e.g.:
// builder.Services.UseClaimsBasedSessionIsolation(new() { ClaimType = ClaimTypes.NameIdentifier });
WebApplication app = builder.Build();
string endpoint = builder.Configuration["AZURE_OPENAI_ENDPOINT"]
@@ -14,6 +14,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Hosting.AGUI.AspNetCore\Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.csproj" />
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Hosting.AspNetCore\Microsoft.Agents.AI.Hosting.AspNetCore.csproj" />
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.OpenAI\Microsoft.Agents.AI.OpenAI.csproj" />
</ItemGroup>
@@ -10,6 +10,11 @@ WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpClient().AddLogging();
builder.Services.AddAGUI();
// WARNING: When adding session persistence (e.g., WithInMemorySessionStore), or running in production,
// make sure to also register a SessionIsolationKeyProvider to scope sessions by principal in multi-user
// deployments, e.g.:
// builder.Services.UseClaimsBasedSessionIsolation(new() { ClaimType = ClaimTypes.NameIdentifier });
WebApplication app = builder.Build();
string endpoint = builder.Configuration["AZURE_OPENAI_ENDPOINT"]
@@ -14,6 +14,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Hosting.AGUI.AspNetCore\Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.csproj" />
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Hosting.AspNetCore\Microsoft.Agents.AI.Hosting.AspNetCore.csproj" />
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.OpenAI\Microsoft.Agents.AI.OpenAI.csproj" />
</ItemGroup>
@@ -27,6 +27,11 @@ builder.Services.ConfigureHttpJsonOptions(options =>
options.SerializerOptions.TypeInfoResolverChain.Add(ApprovalJsonContext.Default));
builder.Services.AddAGUI();
// WARNING: When adding session persistence (e.g., WithInMemorySessionStore), or running in production,
// make sure to also register a SessionIsolationKeyProvider to scope sessions by principal in multi-user
// deployments, e.g.:
// builder.Services.UseClaimsBasedSessionIsolation(new() { ClaimType = ClaimTypes.NameIdentifier });
WebApplication app = builder.Build();
app.UseHttpLogging();
@@ -14,6 +14,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Hosting.AGUI.AspNetCore\Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.csproj" />
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Hosting.AspNetCore\Microsoft.Agents.AI.Hosting.AspNetCore.csproj" />
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.OpenAI\Microsoft.Agents.AI.OpenAI.csproj" />
</ItemGroup>
@@ -17,6 +17,11 @@ builder.Services.AddAGUI();
// Configure to listen on port 8888
builder.WebHost.UseUrls("http://localhost:8888");
// WARNING: When adding session persistence (e.g., WithInMemorySessionStore), or running in production,
// make sure to also register a SessionIsolationKeyProvider to scope sessions by principal in multi-user
// deployments, e.g.:
// builder.Services.UseClaimsBasedSessionIsolation(new() { ClaimType = ClaimTypes.NameIdentifier });
WebApplication app = builder.Build();
string endpoint = builder.Configuration["AZURE_OPENAI_ENDPOINT"]
@@ -14,6 +14,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Hosting.AGUI.AspNetCore\Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.csproj" />
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Hosting.AspNetCore\Microsoft.Agents.AI.Hosting.AspNetCore.csproj" />
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.OpenAI\Microsoft.Agents.AI.OpenAI.csproj" />
</ItemGroup>
@@ -1,44 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
#pragma warning disable CS0618 // Type or member is obsolete - sample uses deprecated PersistentAgentsClientExtensions
// This sample shows how to create and use a simple AI agent with Microsoft Foundry Agents as the backend.
using Azure.AI.Agents.Persistent;
using Azure.Identity;
using Microsoft.Agents.AI;
var endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-5.4-mini";
const string JokerName = "Joker";
const string JokerInstructions = "You are good at telling jokes.";
// Get a client to create/retrieve server side agents with.
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
var persistentAgentsClient = new PersistentAgentsClient(endpoint, new DefaultAzureCredential());
// You can create a server side persistent agent with the Azure.AI.Agents.Persistent SDK.
var agentMetadata = await persistentAgentsClient.Administration.CreateAgentAsync(
model: deploymentName,
name: JokerName,
instructions: JokerInstructions);
// You can retrieve an already created server side persistent agent as an AIAgent.
AIAgent agent1 = await persistentAgentsClient.GetAIAgentAsync(agentMetadata.Value.Id);
// You can also create a server side persistent agent and return it as an AIAgent directly.
AIAgent agent2 = await persistentAgentsClient.CreateAIAgentAsync(
model: deploymentName,
name: JokerName,
instructions: JokerInstructions);
// You can then invoke the agent like any other AIAgent.
AgentSession session = await agent1.CreateSessionAsync();
Console.WriteLine(await agent1.RunAsync("Tell me a joke about a pirate.", session));
// Cleanup for sample purposes.
await persistentAgentsClient.Administration.DeleteAgentAsync(agent1.Id);
await persistentAgentsClient.Administration.DeleteAgentAsync(agent2.Id);
@@ -1,26 +0,0 @@
# Classic Foundry Agents
This sample demonstrates how to create an agent using the classic Foundry Agents experience.
# Classic vs New Foundry Agents
Below is a comparison between the classic and new Foundry Agents approaches:
[Migration Guide](https://learn.microsoft.com/en-us/azure/ai-foundry/agents/how-to/migrate?view=foundry)
# Prerequisites
Before you begin, ensure you have the following prerequisites:
- .NET 10 SDK or later
- Microsoft Foundry service endpoint and deployment configured
- Azure CLI installed and authenticated (for Azure credential authentication)
**Note**: This demo uses Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Microsoft Foundry resource. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively).
Set the following environment variables:
```powershell
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Microsoft Foundry resource endpoint
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-5.4-mini" # Optional, defaults to gpt-5.4-mini
```
@@ -8,8 +8,8 @@ using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Foundry;
var endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-5.4-mini";
var endpoint = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("FOUNDRY_MODEL") ?? "gpt-5.4-mini";
const string JokerName = "JokerAgent";
@@ -1,4 +1,4 @@
# New Foundry Agents
# New Foundry Agents
This sample demonstrates how to create an agent using the new Foundry Agents experience.
@@ -21,6 +21,6 @@ Before you begin, ensure you have the following prerequisites:
Set the following environment variables:
```powershell
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Microsoft Foundry resource endpoint
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-5.4-mini" # Optional, defaults to gpt-5.4-mini
$env:FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Microsoft Foundry resource endpoint
$env:FOUNDRY_MODEL="gpt-5.4-mini" # Optional, defaults to gpt-5.4-mini
```
@@ -13,7 +13,7 @@ using OpenAI.Chat;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var apiKey = Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY");
var model = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "Phi-4-mini-instruct";
var model = Environment.GetEnvironmentVariable("FOUNDRY_MODEL") ?? "Phi-4-mini-instruct";
// Since we are using the OpenAI Client SDK, we need to override the default endpoint to point to Microsoft Foundry.
var clientOptions = new OpenAIClientOptions() { Endpoint = new Uri(endpoint) };
@@ -1,4 +1,4 @@
## Overview
## Overview
This sample shows how to use the OpenAI SDK to create and use a simple AI agent with any model hosted in Microsoft Foundry.
@@ -13,7 +13,7 @@ Before you begin, ensure you have the following prerequisites:
- .NET 10 SDK or later
- Microsoft Foundry resource
- A model deployment in your Microsoft Foundry resource. This example defaults to using the `Phi-4-mini-instruct` model,
so if you want to use a different model, ensure that you set your `AZURE_AI_MODEL_DEPLOYMENT_NAME` environment
so if you want to use a different model, ensure that you set your `FOUNDRY_MODEL` environment
variable to the name of your deployed model.
- An API key or role based authentication to access the Microsoft Foundry resource
@@ -30,5 +30,5 @@ $env:AZURE_OPENAI_ENDPOINT="https://ai-foundry-<myresourcename>.services.ai.azur
$env:AZURE_OPENAI_API_KEY="************"
# Optional, defaults to Phi-4-mini-instruct
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="Phi-4-mini-instruct"
$env:FOUNDRY_MODEL="Phi-4-mini-instruct"
```
@@ -6,6 +6,7 @@
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<NoWarn>$(NoWarn);GHCP001</NoWarn>
</PropertyGroup>
<ItemGroup>
@@ -2,21 +2,22 @@
// This sample shows how to create a GitHub Copilot agent with shell command permissions.
using GitHub.Copilot.SDK;
using GitHub.Copilot;
using GitHub.Copilot.Rpc;
using Microsoft.Agents.AI;
// Permission handler that prompts the user for approval
static Task<PermissionRequestResult> PromptPermission(PermissionRequest request, PermissionInvocation invocation)
static Task<PermissionDecision> PromptPermission(PermissionRequest request, PermissionInvocation invocation)
{
Console.WriteLine($"\n[Permission Request: {request.Kind}]");
Console.Write("Approve? (y/n): ");
string? input = Console.ReadLine()?.Trim().ToUpperInvariant();
PermissionRequestResultKind kind = input is "Y" or "YES"
? PermissionRequestResultKind.Approved
: PermissionRequestResultKind.Rejected;
PermissionDecision decision = input is "Y" or "YES"
? PermissionDecision.ApproveOnce()
: PermissionDecision.Reject();
return Task.FromResult(new PermissionRequestResult { Kind = kind });
return Task.FromResult(decision);
}
// Create and start a Copilot client
@@ -36,7 +36,7 @@ dotnet run
You can customize the agent by providing additional configuration:
```csharp
using GitHub.Copilot.SDK;
using GitHub.Copilot;
using Microsoft.Agents.AI;
// Create and start a Copilot client
@@ -16,7 +16,6 @@ See the README.md for each sample for the prerequisites for that sample.
|---|---|
|[Creating an AIAgent with A2A](./Agent_With_A2A/)|This sample demonstrates how to create AIAgent for an existing A2A agent.|
|[Creating an AIAgent with Anthropic](./Agent_With_Anthropic/)|This sample demonstrates how to create an AIAgent using Anthropic Claude models as the underlying inference service|
|[Creating an AIAgent with Foundry Agents using Azure.AI.Agents.Persistent](./Agent_With_AzureAIAgentsPersistent/)|This sample demonstrates how to create a Foundry Persistent agent and expose it as an AIAgent using the Azure.AI.Agents.Persistent SDK|
|[Creating an AIAgent with Foundry Agents using Azure.AI.Project](./Agent_With_AzureAIProject/)|This sample demonstrates how to create an Foundry Project agent and expose it as an AIAgent using the Azure.AI.Project SDK|
|[Creating an AIAgent with Foundry Model](./Agent_With_AzureFoundryModel/)|This sample demonstrates how to use any model deployed to Microsoft Foundry to create an AIAgent|
|[Creating an AIAgent with Azure OpenAI ChatCompletion](./Agent_With_AzureOpenAIChatCompletion/)|This sample demonstrates how to create an AIAgent using Azure OpenAI ChatCompletion as the underlying inference service|
@@ -26,6 +26,9 @@ var skillsProvider = new AgentSkillsProvider(
SubprocessScriptRunner.RunAsync);
// --- Agent Setup ---
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
.GetResponsesClient()
.AsAIAgent(new ChatClientAgentOptions
@@ -67,6 +67,9 @@ var unitConverterSkill = new AgentInlineSkill(
var skillsProvider = new AgentSkillsProvider(unitConverterSkill);
// --- Agent Setup ---
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
.GetResponsesClient()
.AsAIAgent(new ChatClientAgentOptions
@@ -22,6 +22,9 @@ var unitConverter = new UnitConverterSkill();
var skillsProvider = new AgentSkillsProvider(unitConverter);
// --- Agent Setup ---
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
.GetResponsesClient()
.AsAIAgent(new ChatClientAgentOptions
@@ -64,6 +64,9 @@ var skillsProvider = new AgentSkillsProviderBuilder()
.Build();
// --- Agent Setup ---
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
.GetResponsesClient()
.AsAIAgent(new ChatClientAgentOptions
@@ -80,6 +80,9 @@ var weightSkill = new WeightConverterSkill();
var skillsProvider = new AgentSkillsProvider(distanceSkill, weightSkill);
// --- Agent Setup ---
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
.GetResponsesClient()
.AsAIAgent(
@@ -16,6 +16,9 @@ var guestPath = Environment.GetEnvironmentVariable("HYPERLIGHT_PYTHON_GUEST_PATH
using var codeAct = new HyperlightCodeActProvider(HyperlightCodeActProviderOptions.CreateForWasm(guestPath));
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIAgent agent = new AzureOpenAIClient(
new Uri(endpoint),
new DefaultAzureCredential())
@@ -39,6 +39,9 @@ options.Tools = [fetchDocs, queryData, sendEmail];
using var codeAct = new HyperlightCodeActProvider(options);
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIAgent agent = new AzureOpenAIClient(
new Uri(endpoint),
new DefaultAzureCredential())
@@ -31,6 +31,9 @@ var instructions =
+ "and calling `execute_code` instead of computing values yourself.\n\n"
+ executeCode.BuildInstructions(toolsVisibleToModel: false);
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIAgent agent = new AzureOpenAIClient(
new Uri(endpoint),
new DefaultAzureCredential())
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net10.0</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.AI.OpenAI" />
<PackageReference Include="Azure.Identity" />
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.OpenAI\Microsoft.Agents.AI.OpenAI.csproj" />
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.Valkey\Microsoft.Agents.AI.Valkey.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,55 @@
// Copyright (c) Microsoft. All rights reserved.
// This sample demonstrates using Valkey for persistent chat history with the Agent Framework.
// ValkeyChatHistoryProvider persists conversation history across sessions using Valkey lists.
//
// Prerequisites:
// - A running Valkey server (any version):
// docker run -d --name valkey -p 6379:6379 valkey/valkey:latest
// - Azure OpenAI endpoint and deployment configured via environment variables
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Valkey;
using Microsoft.Extensions.AI;
using OpenAI.Chat;
using Valkey.Glide;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-5.4-mini";
var valkeyConnection = Environment.GetEnvironmentVariable("VALKEY_CONNECTION") ?? "localhost:6379";
var connection = await ConnectionMultiplexer.ConnectAsync(valkeyConnection);
Console.WriteLine("=== ValkeyChatHistoryProvider — Persistent Chat History ===\n");
var historyProvider = new ValkeyChatHistoryProvider(
connection,
_ => new ValkeyChatHistoryProvider.State($"sample-{Guid.NewGuid():N}"),
new ValkeyChatHistoryProviderOptions
{
KeyPrefix = "sample_chat",
MaxMessages = 20
});
AIAgent historyAgent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
.GetChatClient(deploymentName)
.AsAIAgent(new ChatClientAgentOptions()
{
ChatOptions = new() { Instructions = "You are a helpful assistant that remembers our conversation." },
ChatHistoryProvider = historyProvider
});
AgentSession session1 = await historyAgent.CreateSessionAsync();
Console.WriteLine(await historyAgent.RunAsync("Hello! My name is Alex and I'm a software engineer.", session1));
Console.WriteLine(await historyAgent.RunAsync("I'm working on a project using Valkey for caching.", session1));
Console.WriteLine(await historyAgent.RunAsync("What do you remember about me?", session1));
var messageCount = await historyProvider.GetMessageCountAsync(session1);
Console.WriteLine($"\n Stored {messageCount} messages in Valkey.\n");
// Clean up
connection.Dispose();
Console.WriteLine("Done!");
@@ -0,0 +1,30 @@
# Agent with Memory Using Valkey
This sample demonstrates using Valkey for persistent chat history with the Agent Framework.
## Components
- **ValkeyChatHistoryProvider** — Persists conversation history across sessions using Valkey lists. Works with any Valkey or Redis OSS server (no search module required).
## Prerequisites
- Azure OpenAI endpoint and deployment
- A running Valkey server (any version):
```bash
docker run -d --name valkey -p 6379:6379 valkey/valkey:latest
```
## Environment Variables
| Variable | Description | Default |
|---|---|---|
| `AZURE_OPENAI_ENDPOINT` | Azure OpenAI endpoint URL | (required) |
| `AZURE_OPENAI_DEPLOYMENT_NAME` | Model deployment name | `gpt-5.4-mini` |
| `VALKEY_CONNECTION` | Valkey connection string | `localhost:6379` |
## Running
```bash
dotnet run
```
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net10.0</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AWSSDK.Extensions.Bedrock.MEAI" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI\Microsoft.Agents.AI.csproj" />
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.Valkey\Microsoft.Agents.AI.Valkey.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,57 @@
// Copyright (c) Microsoft. All rights reserved.
// This sample demonstrates using Valkey for persistent chat history with the Agent Framework,
// powered by Amazon Bedrock.
//
// Prerequisites:
// - A running Valkey server (any version):
// docker run -d --name valkey -p 6379:6379 valkey/valkey:latest
// - AWS credentials configured (environment variables, AWS profile, or IAM role)
// - Access to an Amazon Bedrock model (e.g., Anthropic Claude)
using Amazon;
using Amazon.BedrockRuntime;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Valkey;
using Microsoft.Extensions.AI;
using Valkey.Glide;
var awsRegion = Environment.GetEnvironmentVariable("AWS_REGION") ?? "us-east-1";
var modelId = Environment.GetEnvironmentVariable("BEDROCK_MODEL_ID") ?? "anthropic.claude-3-5-sonnet-20241022-v2:0";
var valkeyConnection = Environment.GetEnvironmentVariable("VALKEY_CONNECTION") ?? "localhost:6379";
// Create the Bedrock runtime client.
var bedrockRuntime = new AmazonBedrockRuntimeClient(RegionEndpoint.GetBySystemName(awsRegion));
IChatClient chatClient = bedrockRuntime.AsIChatClient(modelId);
var connection = await ConnectionMultiplexer.ConnectAsync(valkeyConnection);
Console.WriteLine("=== ValkeyChatHistoryProvider — Persistent Chat History (Bedrock) ===\n");
var historyProvider = new ValkeyChatHistoryProvider(
connection,
_ => new ValkeyChatHistoryProvider.State($"bedrock-sample-{Guid.NewGuid():N}"),
new ValkeyChatHistoryProviderOptions
{
KeyPrefix = "bedrock_chat",
MaxMessages = 20
});
AIAgent historyAgent = chatClient.AsAIAgent(new ChatClientAgentOptions()
{
ChatOptions = new() { Instructions = "You are a helpful assistant that remembers our conversation." },
ChatHistoryProvider = historyProvider
});
AgentSession session1 = await historyAgent.CreateSessionAsync();
Console.WriteLine(await historyAgent.RunAsync("Hello! My name is Alex and I'm a software engineer.", session1));
Console.WriteLine(await historyAgent.RunAsync("I'm working on a project using Valkey for caching.", session1));
Console.WriteLine(await historyAgent.RunAsync("What do you remember about me?", session1));
var messageCount = await historyProvider.GetMessageCountAsync(session1);
Console.WriteLine($"\n Stored {messageCount} messages in Valkey.\n");
// Clean up
connection.Dispose();
Console.WriteLine("Done!");
@@ -0,0 +1,41 @@
# Agent with Memory Using Valkey + Amazon Bedrock
This sample demonstrates using Valkey for persistent chat history with the Agent Framework, powered by Amazon Bedrock via the `AWSSDK.Extensions.Bedrock.MEAI` adapter.
## Components
- **ValkeyChatHistoryProvider** — Persists conversation history across sessions using Valkey lists. Works with any Valkey or Redis OSS server (no search module required).
- **Amazon Bedrock** — Provides the LLM via `AWSSDK.Extensions.Bedrock.MEAI`, which implements `IChatClient` from `Microsoft.Extensions.AI`.
## Prerequisites
- AWS credentials configured (environment variables, AWS CLI profile, or IAM role)
- Access to an Amazon Bedrock model (e.g., Anthropic Claude 3.5 Sonnet)
- A running Valkey server (any version):
```bash
docker run -d --name valkey -p 6379:6379 valkey/valkey:latest
```
## Environment Variables
| Variable | Description | Default |
|---|---|---|
| `AWS_REGION` | AWS region for Bedrock | `us-east-1` |
| `BEDROCK_MODEL_ID` | Bedrock model identifier | `anthropic.claude-3-5-sonnet-20241022-v2:0` |
| `VALKEY_CONNECTION` | Valkey connection string | `localhost:6379` |
| `AWS_ACCESS_KEY_ID` | AWS access key (if not using profile/role) | — |
| `AWS_SECRET_ACCESS_KEY` | AWS secret key (if not using profile/role) | — |
## Running
```bash
# Using default AWS credential chain (profile, env vars, or IAM role)
dotnet run
# Or with explicit credentials
export AWS_ACCESS_KEY_ID="your-access-key"
export AWS_SECRET_ACCESS_KEY="your-secret-key"
export AWS_REGION="us-east-1"
dotnet run
```
@@ -13,9 +13,9 @@ using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Foundry;
string foundryEndpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
string foundryEndpoint = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set.");
string memoryStoreName = Environment.GetEnvironmentVariable("AZURE_AI_MEMORY_STORE_ID") ?? "memory-store-sample";
string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-5.4-mini";
string deploymentName = Environment.GetEnvironmentVariable("FOUNDRY_MODEL") ?? "gpt-5.4-mini";
string embeddingModelName = Environment.GetEnvironmentVariable("AZURE_AI_EMBEDDING_DEPLOYMENT_NAME") ?? "text-embedding-ada-002";
// Create an AIProjectClient for Foundry with Azure Identity authentication.
@@ -1,4 +1,4 @@
# Agent with Memory Using Microsoft Foundry
# Agent with Memory Using Microsoft Foundry
This sample demonstrates how to create and run an agent that uses Microsoft Foundry's managed memory service to extract and retrieve individual memories across sessions.
@@ -22,11 +22,11 @@ This sample demonstrates how to create and run an agent that uses Microsoft Foun
```bash
# Microsoft Foundry project endpoint and memory store name
export AZURE_AI_PROJECT_ENDPOINT="https://your-account.services.ai.azure.com/api/projects/your-project"
export FOUNDRY_PROJECT_ENDPOINT="https://your-account.services.ai.azure.com/api/projects/your-project"
export AZURE_AI_MEMORY_STORE_ID="my_memory_store"
# Model deployment names (models deployed in your Foundry project)
export AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-5.4-mini"
export FOUNDRY_MODEL="gpt-5.4-mini"
export AZURE_AI_EMBEDDING_DEPLOYMENT_NAME="text-embedding-ada-002"
```
@@ -13,8 +13,8 @@ using OpenAI.Files;
using OpenAI.Responses;
using OpenAI.VectorStores;
var endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-5.4-mini";
var endpoint = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("FOUNDRY_MODEL") ?? "gpt-5.4-mini";
// Create an AI Project client and get an OpenAI client that works with the foundry service.
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
@@ -23,7 +23,7 @@
<PackageReference Include="Azure.AI.OpenAI" Version="2.9.0-beta.1" />
<PackageReference Include="Azure.Identity" Version="1.19.0" />
<PackageReference Include="Microsoft.Agents.AI.OpenAI" Version="1.0.0-rc4" />
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="10.4.0" />
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="10.6.0" />
<PackageReference Include="Neo4j.AgentFramework.GraphRAG" Version="0.1.0-preview.2" />
<PackageReference Include="Neo4j.Driver" Version="5.28.0" />
</ItemGroup>
@@ -10,8 +10,8 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ModelContextProtocol.Server;
var endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-5.4-mini";
var endpoint = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("FOUNDRY_MODEL") ?? "gpt-5.4-mini";
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
@@ -1,4 +1,4 @@
This sample demonstrates how to expose an existing AI agent as an MCP tool.
This sample demonstrates how to expose an existing AI agent as an MCP tool.
## Run the sample
@@ -21,9 +21,9 @@ To use the [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector)
```
1. Open a web browser and navigate to the URL displayed in the terminal. If not opened automatically, this will open the MCP Inspector interface.
1. In the MCP Inspector interface, add the following environment variables to allow your MCP server to access Microsoft Foundry Project to create and run the agent:
- AZURE_AI_PROJECT_ENDPOINT = https://your-resource.openai.azure.com/ # Replace with your Microsoft Foundry Project endpoint
- AZURE_AI_MODEL_DEPLOYMENT_NAME = gpt-5.4-mini # Replace with your model deployment name
- FOUNDRY_PROJECT_ENDPOINT = https://your-resource.openai.azure.com/ # Replace with your Microsoft Foundry Project endpoint
- FOUNDRY_MODEL = gpt-5.4-mini # Replace with your model deployment name
1. Find and click the `Connect` button in the MCP Inspector interface to connect to the MCP server.
1. As soon as the connection is established, open the `Tools` tab in the MCP Inspector interface and select the `Joker` tool from the list.
1. Specify your prompt as a value for the `query` argument, for example: `Tell me a joke about a pirate` and click the `Run Tool` button to run the tool.
1. The agent will process the request and return a response in accordance with the provided instructions that instruct it to always start each joke with 'Aye aye, captain!'.
1. The agent will process the request and return a response in accordance with the provided instructions that instruct it to always start each joke with 'Aye aye, captain!'.
@@ -8,9 +8,9 @@ using Azure.AI.Agents.Persistent;
using Azure.Identity;
using Microsoft.Agents.AI;
var endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
var endpoint = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set.");
var deepResearchDeploymentName = Environment.GetEnvironmentVariable("AZURE_AI_REASONING_DEPLOYMENT_NAME") ?? "o3-deep-research";
var modelDeploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-5.4-mini";
var modelDeploymentName = Environment.GetEnvironmentVariable("FOUNDRY_MODEL") ?? "gpt-5.4-mini";
var bingConnectionId = Environment.GetEnvironmentVariable("AZURE_AI_BING_CONNECTION_ID") ?? throw new InvalidOperationException("AZURE_AI_BING_CONNECTION_ID is not set.");
// Configure extended network timeout for long-running Deep Research tasks.
@@ -1,4 +1,4 @@
# What this sample demonstrates
# What this sample demonstrates
This sample demonstrates how to create an Azure AI Agent with the Deep Research Tool, which leverages the o3-deep-research reasoning model to perform comprehensive research on complex topics.
@@ -37,7 +37,7 @@ Set the following environment variables:
```powershell
# Replace with your Microsoft Foundry project endpoint
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-project.services.ai.azure.com/"
$env:FOUNDRY_PROJECT_ENDPOINT="https://your-project.services.ai.azure.com/"
# Replace with your Bing Grounding connection ID (full ARM resource URI)
$env:AZURE_AI_BING_CONNECTION_ID="/subscriptions/<sub-id>/resourceGroups/<rg>/providers/Microsoft.CognitiveServices/accounts/<account>/projects/<project>/connections/<connection-name>"
@@ -46,4 +46,4 @@ $env:AZURE_AI_BING_CONNECTION_ID="/subscriptions/<sub-id>/resourceGroups/<rg>/pr
$env:AZURE_AI_REASONING_DEPLOYMENT_NAME="o3-deep-research"
# Optional, defaults to gpt-5.4-mini
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-5.4-mini"
$env:FOUNDRY_MODEL="gpt-5.4-mini"
@@ -40,6 +40,9 @@ using OpenAI.Chat;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-5.4-mini";
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
var chatClient = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
.GetChatClient(deploymentName);
@@ -9,13 +9,16 @@ using Azure.AI.Projects.Agents;
using Azure.Identity;
using Microsoft.Agents.AI.Foundry;
string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-5.4-mini";
string endpoint = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("FOUNDRY_MODEL") ?? "gpt-5.4-mini";
const string JokerName = "JokerAgent";
// Create the AIProjectClient to manage server-side agents.
AIProjectClient aiProjectClient = new(new Uri(endpoint), new AzureCliCredential());
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
// Create a server-side agent version using the native SDK.
ProjectsAgentVersion agentVersion = await aiProjectClient.AgentAdministrationClient.CreateAgentVersionAsync(
@@ -1,4 +1,4 @@
# Agent Step 00 - FoundryAgent Lifecycle
# Agent Step 00 - FoundryAgent Lifecycle
This sample demonstrates the full lifecycle of a `FoundryAgent` backed by a server-side versioned agent in Microsoft Foundry: create → run → delete.
@@ -6,14 +6,14 @@ This sample demonstrates the full lifecycle of a `FoundryAgent` backed by a serv
- A Microsoft Foundry project endpoint
- A model deployment name (defaults to `gpt-5.4-mini`)
- Azure CLI installed and authenticated
- An authenticated Azure identity (for example, sign in with `az login`)
## Environment Variables
| Variable | Description | Required |
| --- | --- | --- |
| `AZURE_AI_PROJECT_ENDPOINT` | Microsoft Foundry project endpoint | Yes |
| `AZURE_AI_MODEL_DEPLOYMENT_NAME` | Model deployment name | No (defaults to `gpt-5.4-mini`) |
| `FOUNDRY_PROJECT_ENDPOINT` | Microsoft Foundry project endpoint | Yes |
| `FOUNDRY_MODEL` | Model deployment name | No (defaults to `gpt-5.4-mini`) |
## Running the sample
@@ -6,8 +6,8 @@ using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-5.4-mini";
string endpoint = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("FOUNDRY_MODEL") ?? "gpt-5.4-mini";
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
@@ -14,15 +14,15 @@ Before you begin, ensure you have the following prerequisites:
- .NET 10 SDK or later
- Microsoft Foundry service endpoint and deployment configured
- Azure CLI installed and authenticated (for Azure credential authentication)
- An authenticated Azure identity (for example, sign in with `az login`)
**Note**: This demo uses Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Microsoft Foundry resource. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively).
**Note**: This sample uses `DefaultAzureCredential`. `az login` is the easiest local development path, but Visual Studio, VS Code, and managed identity credentials also work when available.
Set the following environment variables:
```powershell
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-5.4-mini"
$env:FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
$env:FOUNDRY_MODEL="gpt-5.4-mini"
```
## Run the sample
@@ -7,8 +7,8 @@ using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-5.4-mini";
string endpoint = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("FOUNDRY_MODEL") ?? "gpt-5.4-mini";
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
@@ -15,15 +15,15 @@ Before you begin, ensure you have the following prerequisites:
- .NET 10 SDK or later
- Microsoft Foundry service endpoint and deployment configured
- Azure CLI installed and authenticated (for Azure credential authentication)
- An authenticated Azure identity (for example, sign in with `az login`)
**Note**: This demo uses Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Microsoft Foundry resource. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively).
**Note**: This sample uses `DefaultAzureCredential`. `az login` is the easiest local development path, but Visual Studio, VS Code, and managed identity credentials also work when available.
Set the following environment variables:
```powershell
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-5.4-mini"
$env:FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
$env:FOUNDRY_MODEL="gpt-5.4-mini"
```
## Run the sample
@@ -9,8 +9,8 @@ using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-5.4-mini";
string endpoint = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("FOUNDRY_MODEL") ?? "gpt-5.4-mini";
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
@@ -15,15 +15,15 @@ Before you begin, ensure you have the following prerequisites:
- .NET 10 SDK or later
- Microsoft Foundry service endpoint and deployment configured
- Azure CLI installed and authenticated (for Azure credential authentication)
- An authenticated Azure identity (for example, sign in with `az login`)
**Note**: This demo uses Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Microsoft Foundry resource. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively).
**Note**: This sample uses `DefaultAzureCredential`. `az login` is the easiest local development path, but Visual Studio, VS Code, and managed identity credentials also work when available.
Set the following environment variables:
```powershell
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-5.4-mini"
$env:FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
$env:FOUNDRY_MODEL="gpt-5.4-mini"
```
## Run the sample
@@ -15,8 +15,8 @@ static string GetWeather([Description("The location to get the weather for.")] s
// Define the function tool.
AITool tool = AIFunctionFactory.Create(GetWeather);
string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-5.4-mini";
string endpoint = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("FOUNDRY_MODEL") ?? "gpt-5.4-mini";
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
@@ -16,15 +16,15 @@ Before you begin, ensure you have the following prerequisites:
- .NET 10 SDK or later
- Microsoft Foundry service endpoint and deployment configured
- Azure CLI installed and authenticated (for Azure credential authentication)
- An authenticated Azure identity (for example, sign in with `az login`)
**Note**: This demo uses Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Microsoft Foundry resource. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively).
**Note**: This sample uses `DefaultAzureCredential`. `az login` is the easiest local development path, but Visual Studio, VS Code, and managed identity credentials also work when available.
Set the following environment variables:
```powershell
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-5.4-mini"
$env:FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
$env:FOUNDRY_MODEL="gpt-5.4-mini"
```
## Run the sample
@@ -12,8 +12,8 @@ using Microsoft.Extensions.AI;
static string GetWeather([Description("The location to get the weather for.")] string location)
=> $"The weather in {location} is cloudy with a high of 15°C.";
string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-5.4-mini";
string endpoint = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("FOUNDRY_MODEL") ?? "gpt-5.4-mini";
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
@@ -13,13 +13,13 @@ This sample demonstrates how to use function tools that require human-in-the-loo
- .NET 10 SDK or later
- Microsoft Foundry service endpoint and deployment configured
- Azure CLI installed and authenticated (`az login`)
- An authenticated Azure identity (for example, sign in with `az login`)
Set the following environment variables:
```powershell
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-5.4-mini"
$env:FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
$env:FOUNDRY_MODEL="gpt-5.4-mini"
```
## Run the sample
@@ -12,8 +12,8 @@ using SampleApp;
#pragma warning disable CA5399
string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-5.4-mini";
string endpoint = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("FOUNDRY_MODEL") ?? "gpt-5.4-mini";
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
@@ -12,13 +12,13 @@ This sample demonstrates how to configure an agent to produce structured output
- .NET 10 SDK or later
- Microsoft Foundry service endpoint and deployment configured
- Azure CLI installed and authenticated (`az login`)
- An authenticated Azure identity (for example, sign in with `az login`)
Set the following environment variables:
```powershell
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-5.4-mini"
$env:FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
$env:FOUNDRY_MODEL="gpt-5.4-mini"
```
## Run the sample
@@ -7,8 +7,8 @@ using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-5.4-mini";
string endpoint = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("FOUNDRY_MODEL") ?? "gpt-5.4-mini";
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid

Some files were not shown because too many files have changed in this diff Show More