enforce-code-owner
1199 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
56d13bce4e |
.NET: Add BackgroundAgentsProvider.ReleaseSessionAsync to cancel and release per-session background tasks (#7602)
* Add the abilty for the caller to release and cancel background tasks * Improve param validation * Address PR comments * Address PR comments. * Address PR comments: cancel tasks before publishing the release Set IsReleased and publish the ReleaseCompletion only after the in-flight tasks have actually been cancelled, so a failure to cancel leaves the session un-released instead of flagging it as released while its tasks are still running. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
4ca093371e |
.NET: Add Options for Hosted Agent to Allow Backend Storage (#7572)
* Let the container choose who stores a hosted turn, and say so when it is stored twice Turning storage off downstream was unconditional and silent. It is now a container choice, and a deployment that ends up storing anyway is reported instead of quietly recording the conversation in two places nothing reconciles. FoundryResponsesOptions, passed through AddFoundryResponses, carries two settings. AllowStoredOutputEnabled defaults to false, which is when hosting turns storage off for every run and checks the result. Setting it to true leaves the agent's own configuration exactly as the container built it, and nothing is checked, overridden, or refused. IncludeReasoningEncryptedContent applies while storage is off, asking for the encrypted form of the reasoning tokens so reasoning survives between turns, mirroring AsIChatClientWithStoredOutputDisabled. Two checks replace the 400 that used to refuse a session carrying a conversation id. The readiness probe runs each registered agent with its chat client swapped for one that calls nothing, so the request the agent builds on its own is visible without leaving the container, and an agent asking for its responses to be stored keeps the container out of rotation. Per request, a conversation id on the session after the run means the agent's own service kept the turn, which fails with 501 and leaves the session unsaved so later turns do not resume onto it. A misconfigured container is a server problem, not a bad request, hence 5xx. Only a confirmed "this asks to be stored" fails either check. An agent that is not a ChatClientAgent, a request shape carrying no such setting, and a run that could not be completed all pass: this package cannot tell what those would do. * Rename the stored-session flag to say what it means * Say plainly what server-side storage does to a hosted turn * Read the store gate as an allow, and align the messages The flag that decides whether the session may be saved reads as an allow at every use, while the test it comes from keeps saying what is not allowed, so neither side has to be read inside out. The wording now matches what the readiness probe says: server side storage must be off, because with it on the agent's own service records a conversation and response nothing tracks while the hosted agent records its own for the same request. The message the readiness probe raises no longer travels through a shared constant, since each check says its own thing. * Address the review comments left open on the merged PR Five points raised on #7525 were marked resolved without a code change, and the code they pointed at was still there. A hosted workflow session is now recognised by its full type name, so a session of the same short name from another namespace is not mistaken for one. The test double moves into the namespace it stands in for, otherwise it would no longer exercise the check. The per-run chat history provider is handed over on AgentRunOptions.AdditionalProperties, which ChatClientAgent copies onto the chat options with precedence, rather than being written onto the chat options here. The test that pins down who supplies the history said the agent's own provider is used, while it asserts the opposite, so it is renamed after what it checks. Reading a response back in the hosted integration tests no longer swallows every failure: only "not stored" and "not readable through this endpoint" are, so an expired token or a server fault cannot be mistaken for an absent response and pass the test. Also fills in the readiness message for the case where storing is explicitly allowed. * Address the review on #7572 Four findings, all real, all in code this branch introduced. A container that allows its own service to keep the conversation was still being handed the platform history on every turn. That service replays the earlier turns itself, so the model was getting each of them twice, which is the very thing this work exists to prevent. The history now goes in only while nothing else holds it: the first turn of such a conversation still gets it, and the service takes over from there. A turn that fails for storing downstream was announcing itself as completed first and only then failing, leaving the caller with two different answers for the same turn. The completed event is now held back until the run is wound up and the session can be read, because the id of any conversation the agent's service kept only lands there at the very end. The readiness probe replaced the chat client but left the agent's chat history provider running, so a provider backed by a database was reading and writing on every probe, and adding the probe's empty turn to a real conversation. It is stood down for that run now. The probe also treated any cancellation as the health check's own, so a timeout inside an agent could fail readiness. Only a cancellation of the health check's token is left to propagate. Fixing the completed event turned up a latent problem: the terminal event types are named the same in two namespaces this file pulls in, and the short name binds to the ones the response stream never produces, so vt is ResponseCompletedEvent was quietly always false. The three terminal types are now named explicitly. * Let the chat history provider carry the conversation The handler used to read the hosting service's record of the conversation and prepend it to the input of every run, then work out who should not get it: a resumed workflow by the name of its session type, and a container whose own service already holds the conversation. Two exceptions, a type name matched as a string, and a shape where the same turns could arrive from two directions. An agent that reads its history through a provider is now given one, seeded with that record, for the length of the run. The turns arrive the way the agent expects them rather than as fresh input, so nothing is stored back as if it had just been said, and the provider is dropped when the run ends. Only the new input is passed to the run now. Everything else supplies its own history and is left alone: an agent built with a provider keeps using it, an agent whose service keeps the conversation reads it from there, and an agent that is not a ChatClientAgent, a hosted workflow for instance, carries the conversation in its own session state and wants only the new input. The workflow session type name check is gone with it. The session is saved on every turn again. It was being withheld when the agent's own service had kept the turn, which is a decision about that service, not about the session; nothing this handler adds for a turn reaches the session anyway. * Fail a turn to skip its session, and name the store check after what it detects The session was being withheld from the store on a condition about the agent's own service rather than about the turn, and guarded by an emptiness check on a key that is never empty. A turn that is being failed now says so, and only that skips the save. A turn that ends incomplete, waiting on OAuth consent or interrupted by a shutdown, is not a failure: the caller comes back for it and needs the state built up so far, the tool approval ids among it. The session key is resolved once as a value that always exists, so both the load and the save use it without asking again whether it is there. CheckNotAllowedStoreUsage and notAllowedStoreUsageDetected now read as what they are: a check for an agent storing when it should not, and the flag saying it was seen. * Read a hosted response through the agent client, and only forgive a 404 Reading a response back tried the project-level client first and then the per-agent one, swallowing 403 as well as 404 to get past the first. The project-level client cannot see a hosted agent's responses at all, so that attempt only ever produced the 403 the catch then had to forgive, and any other 403, an authorization failure for instance, was read as "nothing is stored" and passed the test. Only the per-agent client is used now, and only a 404 counts as not stored. Verified against the service: a well-formed id it has no response for answers 404 invalid_request_error "Response '...' not found", the same id through the project-level client answers 403 session_not_accessible, and a malformed id answers 400. Everything but the 404 now surfaces. * Move the store setting next to the code that reads and writes it The two halves of the stored output concern lived in a shared helper: one that installs the factory turning storage off, and one that reads back what a request would have asked for. Each had exactly one caller, so the helper only added a hop. They now sit in the converter that builds the request and in the probe client that inspects it, and the helper keeps just the error the handler throws. The test double standing in for a hosted workflow session is also gone. It was declared inside the Workflows namespace because the handler used to recognise a resumed workflow by the full name of its session type; that comparison no longer exists, so the double only needs to not be a ChatClientAgent. * Say that the stored output setting could not be determined, which is the case being logged |
||
|
|
8a0731ad92 |
.NET: Prevent telemetry serialization failures from failing workflows (#7612)
* Prevent telemetry serialization failures from failing workflows * Address PR comments |
||
|
|
6fff2c9b1f | Fix misleading workflow protocol attribute diagnostics (#7609) | ||
|
|
0d75365331 |
.NET: Add Cosmos NoSQL vector memory sample (#7552)
* .NET: Add Cosmos NoSQL vector memory sample * Address Cosmos memory sample review feedback * Fix Cosmos NoSQL memory sample build --------- Co-authored-by: nos-redacted <nosxredacted@gmail.com> |
||
|
|
5eb3eb745e | Improve string parsing in declarative workflows (#7535) | ||
|
|
c987529df3 |
.NET: [BREAKING] Rename to AgentIsolationKeyProvider (#7567)
* Update store isolation documentation Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 824a2e29-e79c-42aa-b28e-aa6c12ec3292 * Rename store isolation key provider Rename the shared session isolation abstraction to reflect its use for both session and task stores. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 824a2e29-e79c-42aa-b28e-aa6c12ec3292 * Rename to AgentIsolationKeyProvider per review feedback Drops the `Store` qualifier and keeps an `Agent` prefix so the type is not confused with generic isolation-key abstractions from other libraries, while leaving room for future non-store isolation (memory, retrieval). - StoreIsolationKeyProvider -> AgentIsolationKeyProvider - ClaimsIdentityStoreIsolationKeyProvider(+Options) -> ClaimsIdentityAgentIsolationKeyProvider(+Options) - GetStoreIsolationKeyAsync -> GetIsolationKeyAsync - UseClaimsBasedStoreIsolation -> UseClaimsBasedAgentIsolation XML docs now state that the `Agent` prefix identifies the hosting API domain and does not mean agent instances are isolated. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 824a2e29-e79c-42aa-b28e-aa6c12ec3292 * Update hosting spec for AgentIsolationKeyProvider rename Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 824a2e29-e79c-42aa-b28e-aa6c12ec3292 --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 824a2e29-e79c-42aa-b28e-aa6c12ec3292 |
||
|
|
aaaa56bc60 |
.NET: Store executable function calls bypassed by declaration-only tool calls (#7388)
* Allow storing executable functions when mixed with non-executable * Address PR review feedback on executable function bypassing - Guard enumerator acquisition so pending bypassed calls are restored when the inner client throws synchronously, before the first MoveNextAsync. - Always surface buffered streaming updates, even when stripping empties them, so metadata such as ConversationId and ResponseId is not discarded. - Document that the decorator must sit below ApprovalResponseBindingChatClient, which drops approval responses that have no recorded request. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR comments --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
18ceb182b1 |
.NET: Give a hosted agent a single source of conversation history (#7525)
* Read hosted chat history through a provider instead of the request input The handler used to fetch the platform conversation history and prepend it to the input of every turn. For a ChatClientAgent that runs in parallel with its own chat history provider, so the conversation had two sources at once. It also had a hidden cost: platform items carry no chat-history source marker, so the agent's provider stored them again as if this turn had written them, leaving a second copy of the conversation inside the persisted session that then diverges from the platform. Make the chat history provider the single source for a ChatClientAgent: - Add FoundryChatHistoryProvider, which reads the conversation through ResponseContext.GetHistoryAsync (it already resolves previous_response_id and the conversation the request belongs to) and stores nothing, because the platform persists the response items itself. An instance is created per request because it holds that request's context, and it is passed as a run-scoped override so the host does not have to mutate the agent. - Register it only when the agent was created without a chat history provider. When one was supplied at construction, that provider owns the conversation and the platform history is not used at all. - Stop adding the platform history to the input for a ChatClientAgent, since the provider now delivers it. A workflow hosted as an agent is not a ChatClientAgent and has no provider pipeline, so it keeps receiving the platform history from the handler exactly as before. * Add regression tests for the duplicated hosted chat history Cover the three symptoms the previous handler produced, each verified to fail when the handler is reverted to fetching the platform history into the turn input: - the conversation the service already keeps was copied into the persisted agent session by the default in-memory history provider; - a custom history provider was asked to write that same conversation into its own database, because platform items carry no chat-history source marker and so look like content this turn produced; - an agent with its own provider received both that provider's history and the platform's in a single request. Also state precisely, in the provider's remarks, why nothing is written back: for a stored request the response orchestrator hands the finished response to its responses provider, which persists the input and output items that a later turn then reads back through GetHistoryAsync; for a non-stored request nothing is persisted and nothing is readable, so the request is self-contained either way. * Keep unstored turns in the session so mixed conversations stay whole A conversation can mix turns the service stores with turns it does not. History is resolved from previous_response_id or the conversation regardless of the current request's store flag, so an unstored turn still reads the stored ones back, but the service records nothing for it and a later turn would never see it again. Reading the platform history through FoundryChatHistoryProvider alone lost those turns: from the second turn onwards the handler treats the session as a resume and stops feeding history in, and the provider kept nothing of its own, so an unstored turn simply vanished from the conversation. A regression test drives three turns of one conversation, the first stored and the rest not, and without this change the model receives only [second question, ok, third question]: the stored opening turn is gone. Give the provider both halves instead of choosing one: - reading returns what the service serves, followed by the turns kept in the session, which are by definition later than anything the service recorded; - writing keeps a turn only when the service was not asked to store it, so a stored turn is never duplicated and an unstored one is never lost. The turns are held in the agent session under the provider's own state key, so they travel with the session the host already persists. * Refuse a stored turn once a conversation holds unstored ones A conversation can move between stored and unstored turns, and the unstored ones live only in the agent session. Going back to a stored turn after that would have the service record it on top of turns the service never saw, so anyone reading the conversation back from the service would find an answer with no question. Refuse it before the model is called instead of writing that gap. Cover the whole shape with a walkthrough of nine turns over one conversation and three provider instances, each with its own session: - an instance that never took an unstored turn starts from the turn the service last saved, and does not see another instance's unstored turns; - an instance that did keeps reading the saved turns and adds its own on top; - asking such an instance for a stored turn is refused, twice, while unstored turns keep working; - a turn stored from one instance does not appear for another, because it sits on a different branch of the conversation and so is not among the turns leading to what that other instance last saved. * Say plainly that kept turns belong to the session The turns the service was not asked to store are written into the agent session's state bag under this provider's own state key, and a new provider is built for every request, so nothing is held on the provider object itself. The walkthrough named its three threads after provider instances, which read as if the object carried the memory. Name them after the sessions they are, and add a test that pins the behaviour down: a turn kept through one provider object is read back by a different one given the same session, and is absent for one given another session. * Show which half of the conversation each provider decides The session decides what is kept, but the provider still decides two things: which service-side conversation is read, because it holds the request's response context, and whether the turn is kept at all, because it holds the request's store flag. Add two tests that separate those from the session: - two providers reading one session, each built for a request of a different conversation, return the same kept turn behind different served turns; - two providers writing to one session, one for a stored request and one for an unstored one, leave only the unstored turn behind. * Say why a hosted workflow keeps taking history from the handler The comment stated that a workflow hosted as an agent has no provider pipeline without saying what that means. It derives from AIAgent directly, so it never calls a ChatHistoryProvider and does not read the run options' additional properties: the provider could not reach it even if it were registered. * Ask the session store whether a turn is a resume The handler decided that a turn was resuming an existing conversation by looking for state on the session. That reading broke once the handler itself started writing to the session before the check: it records the caller's identity there, so a session created moments earlier already carried state and the very first turn of a conversation looked like a resume. Its history was then never fetched, and the agent answered knowing nothing of a conversation the service was already holding. It only showed up when hosted, because running locally there is no identity to record. Let the store answer the question instead. GetSessionAsync now returns null when nothing is stored rather than quietly handing back a new session, so a non-null result means a prior turn established this session and nothing else has to be inferred. Callers that just want a usable session can use the new GetOrCreateSessionAsync, which is written in terms of GetSessionAsync so a store overriding one gets the other for free. Both store implementations and their tests follow the plain-lookup contract: a miss creates nothing, deserializes nothing, and touches no directory. * Drop the experimental marker from an internal type FoundryChatHistoryProvider is internal, so the attribute reached no caller: the marker exists to warn people consuming the public surface. It also does not follow from the base type, which does not carry one, and most internal types in this package have none either. Removing it leaves two usings behind, so they go as well. * Stand down the agent's second-manager guard for the host's own provider An agent refuses a second history manager once the model reports a conversation id of its own, which happens as soon as the container lets the model keep the conversation. The guard is meant for an application that configured a provider by hand and would otherwise end up with two of them. Here the host is the one supplying the provider, deliberately and for every turn, so the guard was rejecting the arrangement it is hosting: the first turn failed while streaming, and every later one failed before reaching the model at all. Turn the three conflict settings off on the agent the host is serving, and let the provider decide what reaches the model. A test drives two turns of one conversation against a model that reports a conversation id and asserts both complete. * Pass a caller's request not to store on to the chat client A request asking the hosting service not to store the response was honoured there and nowhere else, so the service behind the agent's own chat client kept recording the conversation and reporting an id for it. A caller opting out of storage still ended up with a stored conversation, and the container went on continuing it. Only that direction travels. Carrying store=true across would either force storage on a container whose author turned it off on purpose or change nothing, since storing is already the default. * Hand the conversation to the agent's own provider instead of a host one The host no longer supplies a chat history provider of its own. It writes the turns the service holds into the provider the agent already created for itself, and only when that is the stock in-memory one, so an agent given a provider keeps sole control of its storage and the model receives the conversation once. A conversation the caller stops asking the service to store moves into the session state and stays there. The session's conversation id no longer names anything the service records and cannot be cleared, so the session is cloned without it on that single turn. Asking for a stored turn afterwards is refused: the service would record a turn whose predecessors it does not hold. An agent that does not read history through a provider, a hosted workflow for example, is still given its prior turns as input, now marked as chat history so no provider along the way stores them as new. * Run the agent's own request factory instead of replacing it ChatClientAgent chains a request's raw representation factory with the agent's by taking the agent's only when the request's returns null. The factory added for an unstored turn always answers, so anything the container configured on the agent's ChatOptions was silently dropped for that turn. The agent's factory is now invoked first and its result is what carries the setting. A result that is not a CreateResponseOptions belongs to some other chat client, which has no notion of storing a response, so it is handed back untouched. * Cover a stored conversation that stops being stored and asks again The refusal was only tested on a conversation the service never stored. Reaching it from a stored one goes through the turn that rebuilds the session without its conversation id, so the mark saying the conversation left the service has to survive that rebuild to be found on the next turn. * Leave the conversation to the AgentServer storage provider alone The AgentServer SDK records a hosted turn through its own storage provider, around the handler, and serves the conversation back through ResponseContext.GetHistoryAsync. Anything the container stores of its own is a second conversation that storage provider never sees and no one reconciles. The handler now takes that history as the single source and hands it to the agent as input alongside this turn's messages. The agent's own provider is replaced for the run by one holding its messages in a field, so a run that calls tools still has what its earlier calls produced while nothing survives the request. The service behind the agent's chat client is asked not to store on every turn, whatever the caller asked of the hosting service. A session that still carries a conversation id means that service is recording a second conversation regardless, so the turn is refused with a 400 rather than run against something nobody can reconcile. * Narrow the history skip to a resumed workflow Withholding the conversation from every agent that is not a ChatClientAgent assumed they all carry it in their own session. A hand-written one that keeps nothing would answer with no history from its second turn on, so the check is now on the session type a workflow runs with, which is what actually accumulates the turns. The conversation and previous response id tests went with it: the session key falls back to the partition of a freshly minted response id, which never has a session saved for it, so a loaded session already implies one of the two was sent. Also asks a Chat Completions client not to store, since the setting carries the same name on both OpenAI request shapes. * Add a live test that a hosted turn is not stored twice The AgentServer SDK's storage provider records every hosted turn around the handler, and that record is the conversation the caller reads. The agent's own run inside the container talks to its own service, and when that service is asked to keep the turn it writes a second copy of the same exchange, on a trail of its own that nobody reads and nobody reconciles. The caller's conversation looks clean, so the second copy goes unnoticed. The new downstream-store scenario runs an ordinary Foundry ChatClientAgent, like the first hosted agent sample, wrapped so that after the run it appends DOWNSTREAM_ID=<id> to the reply, carrying whatever its own run left behind. The tests then go looking for that id on the service: finding it means a second copy exists. Verified live against a Foundry project. On main both tests fail, reporting a readable id such as resp_0940e276..., and here the container reports DOWNSTREAM_ID=none and both pass. * Let the session carry the conversation in the downstream store test The run options were setting the conversation on every call, which the session already does. The single turn test now binds the session to the conversation up front, and the multi turn test starts from the agent's own default session and reads back what the hosted agent kept for the caller off ChatClientAgentSession once the first turn returns. Re-verified live: still fails on main, reporting a readable id such as resp_0c07a5e4..., and still passes here. |
||
|
|
ec32e86646 |
.NET: Aggregate usage across looping agents and chat clients (#7539)
* Ensure usage is merged for all looping components * Add max tool approval loop fixes * Fix net472 build break in usage aggregation tests DateTimeOffset.UnixEpoch is not available on .NET Framework 4.7.2, so the WithAggregatedUsage copy tests failed to compile for that target framework. Use an explicit DateTimeOffset instead; the specific instant is irrelevant, the value only needs to be non-default so the copy assertion is meaningful. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR comments --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
94bbfb2ac8 |
.NET: Harden file skill discovery (#7540)
* .NET: Harden file skill discovery Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8ff072fa-42d6-44b0-b226-2182cfc7639c * .NET: Handle inaccessible skill directories Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8ff072fa-42d6-44b0-b226-2182cfc7639c --------- Copilot-Session: 8ff072fa-42d6-44b0-b226-2182cfc7639c |
||
|
|
74a144085a |
.NET: Bound the tool-approval auto-approval loop (#7472) (#7474)
* .NET: Bound the tool-approval auto-approval loop (#7472) `ToolApprovalAgent` re-invoked the inner agent from two unbounded `while (true)` loops whenever every surfaced approval request was auto-approved. Each pass is a fresh `InnerAgent.RunAsync` / `RunStreamingAsync` call, so a per-request cap such as `FunctionInvokingChatClient.MaximumIterationsPerRequest` restarts every time and cannot bound the chain. Under `AllToolsAutoApprovalRule` a model that keeps requesting an auto-approved tool therefore drives billable model calls indefinitely; the reporter measured 100M+ tokens over three days. Adds `ToolApprovalAgentOptions.MaxAutoApprovalIterations` (default `ToolApprovalAgent.DefaultMaxAutoApprovalIterations`, 10) and bounds both loops. Naming, default and `Throw.IfLessThan` validation follow the existing `LoopAgent.DefaultMaxIterations` / `LoopAgentOptions.MaxIterations` convention in this assembly. On reaching the cap the agent takes one final inner turn without auto-approving again, so a remaining approval request is surfaced to the caller to decide. Returning early instead would hand back an empty response, because `ProcessAndQueueOutboundApprovalRequestsAsync` strips every approval request once they are all auto-approved -- the case the loop exists to avoid. This mirrors the Python behaviour, which logs and issues one final request with tools disabled once its iteration budget is spent (`_tools.py`). Python is not affected: it caps at `DEFAULT_MAX_ITERATIONS` (40) and persists `attempt_count` in the budget state across approval resumes, so a resumed run continues the count rather than restarting it. Tests: the runaway is reproduced on both the streaming and non-streaming paths with an inner agent that never stops requesting an auto-approved tool. Inner invocations equal the cap plus the final turn, and scale with the configured cap, so the assertions fail if the bound is removed. No sample changes: with the loop bounded, Agent_Step01, Agent_Step06, Agent_Step07 and Hosted-AgentSkills are safe as written. * .NET: Add Arrange/Act/Assert comments to the cap constructor test Matches the test convention documented in dotnet/AGENTS.md and used by the surrounding tests in this file. * Increase default max auto approval iterations to 40 * Apply suggestion from @westey-m Co-authored-by: westey <164392973+westey-m@users.noreply.github.com> * Update comments in ToolApprovalAgent.cs --------- Co-authored-by: westey <164392973+westey-m@users.noreply.github.com> |
||
|
|
a4d4eafa5e |
Add CodeQL suppression comment for DevUI proxy validation (#7505)
The proxy target validation in ValidateProxyTarget already ensures requests stay on the configured backend. Add an inline suppression comment following the repo's established pattern. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4363ab44-4d9e-41a0-97d3-4ab0b973f0b2 |
||
|
|
da056275e6 |
.NET: [Experimental] Extend A2A task store with isolation key scoping (#7504)
* .NET: Add tenant-scoped task store isolation for A2A hosting Wrap ITaskStore with IsolationKeyScopedTaskStore when a SessionIsolationKeyProvider is registered, mirroring the existing session store isolation pattern. This ensures task operations are scoped per tenant in multi-user deployments. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: adc30d6c-ce66-40bb-933e-9801c2156cda * fix formatting issue --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: adc30d6c-ce66-40bb-933e-9801c2156cda |
||
|
|
1da571860a | Updating version for dotnet release 1.17.0 (#7514) | ||
|
|
d56e81357e | Fail declarative workflows when an agent returns an error (#7497) | ||
|
|
84d5a5eec1 |
Consolidate Dependabot dependency updates (#7445)
* Bump AgentMemory from 1.2.0 to 1.3.0 --- updated-dependencies: - dependency-name: AgentMemory dependency-version: 1.3.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * .NET: consolidate #7280 AgentMemory.AgentFramework 1.3.0 * Bump github/codeql-action/init from 4.37.0 to 4.37.3 Bumps [github/codeql-action/init](https://github.com/github/codeql-action) from 4.37.0 to 4.37.3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/99df26d4f13ea111d4ec1a7dddef6063f76b97e9...e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81) --- updated-dependencies: - dependency-name: github/codeql-action/init dependency-version: 4.37.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * Bump astral-sh/setup-uv from 8.3.2 to 9.0.0 Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 8.3.2 to 9.0.0. - [Release notes](https://github.com/astral-sh/setup-uv/releases) - [Commits](https://github.com/astral-sh/setup-uv/compare/11f9893b081a58869d3b5fccaea48c9e9e46f990...c771a70e6277c0a99b617c7a806ffedaca235ff9) --- updated-dependencies: - dependency-name: astral-sh/setup-uv dependency-version: 9.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * Bump github/codeql-action/analyze from 4.37.0 to 4.37.3 Bumps [github/codeql-action/analyze](https://github.com/github/codeql-action) from 4.37.0 to 4.37.3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/99df26d4f13ea111d4ec1a7dddef6063f76b97e9...e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81) --- updated-dependencies: - dependency-name: github/codeql-action/analyze dependency-version: 4.37.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * Bump actions/cache from 5.0.5 to 6.1.0 Bumps [actions/cache](https://github.com/actions/cache) from 5.0.5 to 6.1.0. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/27d5ce7f107fe9357f9df03efb73ab90386fccae...55cc8345863c7cc4c66a329aec7e433d2d1c52a9) --- updated-dependencies: - dependency-name: actions/cache dependency-version: 6.1.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * Bump actions/checkout from 6.0.2 to 7.0.1 Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.2 to 7.0.1. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/de0fac2e4500dabe0009e67214ff5f5447ce83dd...3d3c42e5aac5ba805825da76410c181273ba90b1) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 7.0.1 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * Bump astral-sh/setup-uv in /.github/actions/python-setup Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 8.3.2 to 9.0.0. - [Release notes](https://github.com/astral-sh/setup-uv/releases) - [Commits](https://github.com/astral-sh/setup-uv/compare/11f9893b081a58869d3b5fccaea48c9e9e46f990...c771a70e6277c0a99b617c7a806ffedaca235ff9) --- updated-dependencies: - dependency-name: astral-sh/setup-uv dependency-version: 9.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * Bump ty from 0.0.60 to 0.0.64 in /python Bumps [ty](https://github.com/astral-sh/ty) from 0.0.60 to 0.0.64. - [Release notes](https://github.com/astral-sh/ty/releases) - [Changelog](https://github.com/astral-sh/ty/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ty/compare/0.0.60...0.0.64) --- updated-dependencies: - dependency-name: ty dependency-version: 0.0.65 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * Bump prek from 0.4.10 to 0.4.11 in /python Bumps [prek](https://github.com/j178/prek) from 0.4.10 to 0.4.11. - [Release notes](https://github.com/j178/prek/releases) - [Changelog](https://github.com/j178/prek/blob/master/CHANGELOG.md) - [Commits](https://github.com/j178/prek/compare/v0.4.10...v0.4.11) --- updated-dependencies: - dependency-name: prek dependency-version: 0.4.11 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * Bump uv from 0.11.29 to 0.11.32 in /python Bumps [uv](https://github.com/astral-sh/uv) from 0.11.29 to 0.11.32. - [Release notes](https://github.com/astral-sh/uv/releases) - [Changelog](https://github.com/astral-sh/uv/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/uv/compare/0.11.29...0.11.32) --- updated-dependencies: - dependency-name: uv dependency-version: 0.12.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * Bump ruff from 0.15.22 to 0.16.0 in /python Bumps [ruff](https://github.com/astral-sh/ruff) from 0.15.22 to 0.16.0. - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ruff/compare/0.15.22...0.16.0) --- updated-dependencies: - dependency-name: ruff dependency-version: 0.16.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * update uv-build requirement in /python --- updated-dependencies: - dependency-name: uv-build dependency-version: 0.12.0 dependency-type: direct:development ... Signed-off-by: dependabot[bot] <support@github.com> * Python: align workspace pins for #7436-#7439 * Python: support ty 0.0.64 diagnostics for #7436 * Python: apply Ruff 0.16 formatting for #7439 * Update workflow action version annotations --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
9c8151699a | Fix Handoff orchestration sample not responding to user input (#7442) | ||
|
|
43309018be |
.NET and Python: Extract Durable Task and Azure Functions integrations (#7465)
* Extract Durable Task and Azure Functions integrations Remove the migrated implementations, samples, tests, documentation, and repository wiring now owned by microsoft/agent-framework-durable-extension. Preserve Python compatibility through the agent_framework.azure shim and agent-framework-core[all], and leave customer-facing redirects to the new repository. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6181dcf9-857b-43ea-9fd2-fcd6b175ffdd * Fix feature registry validation after extraction Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6181dcf9-857b-43ea-9fd2-fcd6b175ffdd * Narrow external feature package paths Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6181dcf9-857b-43ea-9fd2-fcd6b175ffdd --------- Copilot-Session: 6181dcf9-857b-43ea-9fd2-fcd6b175ffdd |
||
|
|
2aa267e028 |
.NET: Updating version for dotnet release 1.16.0 (#7441)
Co-authored-by: SergeyMenshykh <SergeMenshikh@outlook.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 28ce674d-8c40-4d49-864c-5d02894fe762 |
||
|
|
6a3d535204 |
.NET: Add regression tests and sample guidance for stable agent IDs in checkpointed workflows (#7415)
* Add regression tests and sample guidance for stable agent IDs in checkpointed workflows * Updated tests to address PR comments * Improve test for checkpoint state. |
||
|
|
73f48d255e |
.NET: Add FileMemoryProvider sample to 02-agents/AgentWithMemory (#7401)
* Add FileMemoryProvider sample * Address PR comments |
||
|
|
d07edffaed |
Python: Fix Actions token environment (#7427)
* Fix Copilot Actions token environment Expose workflow tokens through GITHUB_TOKEN so Copilot CLI uses native Actions authentication, while preserving user-token integration test support. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d9fa4e9c-c32d-42fb-8ee4-4772473e6479 * Gate Copilot integration tests explicitly Use GitHub Actions authentication only when both GITHUB_ACTIONS and GITHUB_TOKEN are present, and require an explicit local opt-in that relies on stored Copilot login. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d9fa4e9c-c32d-42fb-8ee4-4772473e6479 --------- Copilot-Session: d9fa4e9c-c32d-42fb-8ee4-4772473e6479 |
||
|
|
4a7af303af |
Bump .NET SDK from 10.0.301 to 10.0.302 (#7376)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 63ac3381-c9ca-47af-b0e7-9a09c0a5b2be |
||
|
|
0ca6a3e652 |
.NET: Add source (ZIP) deploy oriented hosted agent samples (#7372)
* Add zip/code-deploy POC for Hosted-ChatClientAgent (.NET) Migrate the sample to Foundry source (ZIP) deployment as the default: add azure.yaml with codeConfiguration (remote_build, dotnet_10) and the tool-generated .agentignore, make the csproj self-contained (single target, CPM off, published PackageReferences), and simplify Program.cs to the pristine end-user hosting path. Container files are kept for now; contributor and remaining samples handled in follow-ups. * .NET: Auto-bind Foundry hosted port for zip/code deploy; migrate Hosted-ChatClientAgent to source (ZIP) Foundry.Hosting: AddFoundryResponses now binds Kestrel to FoundryEnvironment.Port (the PORT env var, default 8088) for a plain WebApplication.CreateBuilder (Tier 3) host, mirroring AgentHostBuilder. This lets a source/ZIP-deployed .NET agent pass the readiness probe with no Dockerfile. It respects an explicit ASPNETCORE_URLS override and is idempotent. Adds FoundryListenPortTests plus a serialized env-var collection. Hosted-ChatClientAgent: migrate to source (ZIP) deploy as the default. Add azure.yaml with codeConfiguration (remote_build, dotnet_10) and the tool-generated .agentignore, make the csproj self-contained (single target, CPM off) with a local Directory.Packages.props, embed the local-dev per-agent route so the Using-Samples REPL can reach the local server, and rewrite the README around the azd flow. Documents AZURE_TOKEN_CREDENTIALS=dev for local runs. Using-Samples/SimpleAgent: fix the per-agent endpoint scheme rewrite so the local HTTP dev port is preserved (the policy now lives on the per-agent ProjectOpenAIClientOptions that actually serves the request). * .NET: Bind Foundry hosted port unconditionally; drop container files and the local-only agent route Zip/code deploy runs the sample as a plain ASP.NET app, so the Foundry readiness port was never bound and every invoke returned HTTP 424 session_not_ready. The first attempt skipped the binding when ASPNETCORE_URLS was already set, but the .NET base image always sets it to port 80, so the skip always tripped. Kestrel ListenAnyIP overrides ASPNETCORE_URLS, so the binding is now unconditional and PORT stays the only knob. Sample cleanup for zip deploy: * Remove Dockerfile, Dockerfile.contributor, agent.manifest.yaml and agent.yaml. Source deploy needs none of them. * Remove LocalDevEndpoint.cs and the invented per-agent local route. The local server already serves the standard POST /responses route, so the client can reach it directly. * Trim .env.example: the port and environment variables are no longer needed. * Exclude .checkpoints/ from the upload so local session state does not ship. SimpleAgent now asks at startup whether to chat with the local server or the deployed agent, the same choice azd ai agent invoke exposes through --local. Local uses an OpenAI responses client pointed at http://localhost:8088; Foundry uses the per-agent endpoint. Add scripts/New-ContributorStage.ps1, which stages a sample to a temp folder with the local Agent Framework source packed into a feed inside the upload, so contributors can deploy framework changes through the same azd flow end users run. * Pin hosted agent listen port in azure.yaml * Use the documented env map in azure.yaml * Make the contributor flow an extra step inside the end-user flow * Keep contributor scaffolding out of the sample project file * Document the full deploy walkthrough and add a bash contributor script * Trim troubleshooting detail from the sample README * Pass the model deployment name to the hosted container * Add --local and --remote flags to the SimpleAgent REPL * Use central package management in the hosted sample * Clarify where the contributor step fits in the deploy walkthrough * Restore the HTTP scheme rewrite for local AIProjectClient runs * Keep the sample package versions in the project file * Drop the sample Directory.Packages.props * Add a container deploy variant of the hosted chat client agent sample * Treat a blank model deployment variable as unset * Let azd prompt for the Foundry project and expand the contributor section * Document the stale conversation 404 in the hosted agent samples * Remove using directives already covered by global usings * Bind the Foundry listen port only inside a hosted container * Resolve the Foundry listen port from IConfiguration |
||
|
|
5543bc94fc | Fix and re-enable flaky InputWaiter timeout test (#7377) | ||
|
|
8d22bb9177 |
.NET: Add Anthropic-backed live tests for OpenAI Responses hosting helpers (#7362)
Mirrors OpenAIResponsesHostingLiveTests with the hosted agent backed by an Anthropic chat client, confirming the app-owned hosting helper surface (OpenAIResponses + AgentSessionStore) is provider-agnostic end to end. Skipped unless ANTHROPIC_API_KEY is configured. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
ad20ab009b |
.NET: Add GitHub Copilot BYOK sample (#7337)
* .NET: Add GitHub Copilot BYOK sample Demonstrates routing GitHubCopilotAgent requests through a custom OpenAI-compatible endpoint via SessionConfig.Provider instead of the default GitHub Copilot backend. * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Update dotnet/samples/02-agents/AgentProviders/github-copilot/Agent_With_GitHubCopilot_BYOK/README.md Co-authored-by: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com> * Update dotnet/samples/02-agents/AgentProviders/github-copilot/Agent_With_GitHubCopilot_BYOK/README.md Co-authored-by: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com> * .NET: Address remaining BYOK sample review feedback - Make the provider type configurable via BYOK_PROVIDER_TYPE (default "openai") instead of hardcoding "openai", since the sample already documents Azure/Anthropic support. - Stop calling the endpoint "OpenAI-compatible" everywhere; Anthropic isn't OpenAI-wire- compatible, so reword to "your own endpoint" and list the actual supported providers. - Move the "About BYOK" explainer to the top of the README so the term is introduced before it's used, and finish applying the WireApi/ModelId comment suggestions. - Reword the AgentProviders/README.md entry to match (not OpenAI-specific). * .NET: Fix UTF-8 BOM on BYOK sample Program.cs The repo's .editorconfig requires utf-8-bom for .cs files; check-format was failing because the new file was written without one. --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com> |
||
|
|
aa53182dc0 |
.NET: Preserve table state across declarative EditTable operations (#7353)
* Preserve table state across declarative EditTable operations * Address PR comments. |
||
|
|
859f56fb49 |
.NET: Skip flaky InputWaiterTests timeout test blocking the merge queue (#7361)
InputWaiter_WaitForInputAsync_CompletesWhenTimeoutExpiresAsync races a 300ms SemaphoreSlim timeout against a 5s Task.Delay guard and asserts which one won by object identity. On the loaded net472/windows-latest leg, thread pool starvation can delay the 300ms continuation past the 5s guard, so Task.Delay wins and the assertion fails. It failed in 7 of the last 18 failed dotnet-build-and-test runs, always on net472/windows-latest and always in merge_group, blocking PRs that do not touch the workflows code. Quarantine it following the existing convention used for #5845, and track the real fix in #7360. Copilot-Session: 0be6f810-51de-4f49-b9c7-8d1c7efa2c43 |
||
|
|
2694120383 |
Forward A2A MessageSendParams.Configuration in the A2A adapter (#7365)
The A2A hosting layer now forwards the caller-supplied SendMessageConfiguration from RequestContext.Configuration into AgentRunOptions.AdditionalProperties under the key 'a2a.configuration'. This covers all three handler paths: non-streaming, streaming, and task continuation. The server-configured AgentRunMode remains authoritative for AllowBackgroundResponses — the caller's ReturnImmediately is forwarded but does not override the server decision. Closes microsoft/agent-framework#5869 Co-authored-by: SergeyMenshykh <SergeMenshikh@outlook.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ee820de-3e34-493b-a19c-1db6bc04871d |
||
|
|
0b0dcaa5af |
fix(dotnet): preserve table after EditTable add (#7324)
Signed-off-by: KXH <shepherdlaurie238@gmail.com> |
||
|
|
35a8891d67 |
.NET: Add Microsoft.Agents.AI.LocalCodeAct to release solution filter (#7343)
* Initial plan * Add Microsoft.Agents.AI.LocalCodeAct to release solution filter --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> |
||
|
|
28e02d4669 | Create session for tool approval agent when non-present (#7310) | ||
|
|
c6442de528 |
.NET: Graduate GitHub Copilot agent to stable (#7313)
Promote Microsoft.Agents.AI.GitHub.Copilot from release candidate to released by replacing IsReleaseCandidate=true with IsReleased=true, so the package builds with the stable central version (no -rc suffix). Also clears the package-validation baseline and disables package validation for this first stable release, since the package has never shipped a stable NuGet to validate against (mirrors the Microsoft.Agents.AI.Harness graduation in #7119). Non-breaking: the package exposes no [Experimental] APIs to un-mark. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f523064c-60b4-4d18-bf95-c16c5fda9126 |
||
|
|
d0a0d5a3df |
.NET: Add TodoProvider and AgentModeProvider samples (#7262)
* Add samples for todo and mode providers * Address PR review: add Step21 to samples index and trim slash-command input Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Print agent mode after each turn in AgentMode sample Reflects mode changes the agent makes itself via the mode_set tool. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
c59a65da5e |
.NET: fix InMemoryChatHistoryProvider persisting when service stores history (#7284)
* Fix chat history storage bug * Improve error messaging * Address PR comment |
||
|
|
0d5c0f8fa0 |
.NET: Add language and prompt customization to Magentic orchestration (#7263)
* Add language and prompt customization to Magentic orchestration * Update default prompts formatting |
||
|
|
ad26cfe8c7 |
.NET: Switch to using new community toolkit VectorData packages (#5694)
* Switch to using new community toolkit VectorData packages * Fix formatting. * Update dotnet/Directory.Packages.props Co-authored-by: Adam Sitnik <adam.sitnik@gmail.com> * Fix build error. * Upgrade MEAI * Upgrade additional dependencies * Address rename after package upgrade. * Revert some packages versions due to version mismatches --------- Co-authored-by: Adam Sitnik <adam.sitnik@gmail.com> |
||
|
|
c68c099347 |
.NET: Fix expensive logging (#7268)
* .NET: Guard workflow warning logging Avoid unnecessary structured logging argument evaluation when warning logging is disabled, resolving CA1873 in release builds. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0bc01e26-22ba-42ce-ac1e-6fe166500f4f * .NET: Use generated workflow logging Align the no-progress warning with the repository-standard LoggerMessage source generator pattern. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0bc01e26-22ba-42ce-ac1e-6fe166500f4f * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Ben Thomas <25218250+alliscode@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Copilot-Session: 0bc01e26-22ba-42ce-ac1e-6fe166500f4f |
||
|
|
ddb0622f9c |
.NET: Added GettingStarted example demonstrating Dapr as an agent provider (#1615)
* Added example demonstrating creating an AIAgent using the Microsoft.AI.Extensions implementation of IChatClient using Dapr as the inference backend provider - in this example, using Ollama Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Update dotnet/samples/GettingStarted/AgentProviders/Agent_With_Dapr/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Added copyright statement at top of file Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Update dotnet/agent-framework-dotnet.slnx That's odd the IDE added it a second time. Co-authored-by: westey <164392973+westey-m@users.noreply.github.com> * Address review nits: configurable Dapr gRPC endpoint and document VersionOverride Make the Dapr sidecar gRPC endpoint configurable via the DAPR_GRPC_ENDPOINT environment variable (defaulting to http://localhost:3501) and document it in the README. Add a comment explaining why the Microsoft.Extensions.* VersionOverride entries are needed and when they can be removed. --------- Signed-off-by: Whit Waldo <whit.waldo@innovian.net> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: westey <164392973+westey-m@users.noreply.github.com> Co-authored-by: Roger Barreto <19890735+RogerBarreto@users.noreply.github.com> |
||
|
|
12b23250f4 |
Updating dotnet version for release. (#7265)
Co-authored-by: Ben Thomas <25218250+alliscode@users.noreply.github.com> |
||
|
|
bfc73a5b14 |
.NET: Fix declarative autosend output (#7217)
* Fix declarative workflow auto-send output Restore completed responses for workflow-conversation agents while preventing hosted workflow adapters from materializing streamed responses twice. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c2d86826-ead0-40bc-b84b-a513ac4d325f * Correlate streamed workflow responses by message Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c2d86826-ead0-40bc-b84b-a513ac4d325f * Handle empty streaming message IDs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c2d86826-ead0-40bc-b84b-a513ac4d325f * Restore workflow conversation auto-send Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c2d86826-ead0-40bc-b84b-a513ac4d325f * Address workflow response review feedback Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c2d86826-ead0-40bc-b84b-a513ac4d325f * Ignore whitespace workflow message IDs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c2d86826-ead0-40bc-b84b-a513ac4d325f * Correlate all content-bearing agent updates Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c2d86826-ead0-40bc-b84b-a513ac4d325f --------- Co-authored-by: Ben Thomas <25218250+alliscode@users.noreply.github.com> |
||
|
|
1f1da1bddb |
.NET: [BREAKING] Hosting OpenAI Responses protocol helpers and optional execution state (#7000)
* .NET: Add OpenAI Responses protocol helpers and optional execution state (ADR-0032)
* Fix netstandard2.0/net472 build; harden helpers and workflow checkpoint key per review
* .NET: Migrate hosting Responses samples to Azure.AI.Projects and fix workflow resume
Migrate HostingResponsesAgent and HostingResponsesWorkflow samples from
Azure.AI.OpenAI to Azure.AI.Projects (AIProjectClient.AsAIAgent), using the
FOUNDRY_PROJECT_ENDPOINT/FOUNDRY_MODEL convention.
Fix HostedWorkflowState.RunOrResumeAsync: on subsequent turns, restore the
session's latest checkpoint and run the workflow forward with the new turn's
input (mirroring the Python hosting host's restore-then-run semantics) instead
of resuming a halted run with no input, which waited on input indefinitely.
Add round-trip resume tests and update ADR-0032/spec-003 wording.
* .NET: Fix HostedWorkflowState resume hang on unserviced external requests
On resume, HostedWorkflowState.RunOrResumeAsync drained the workflow with the
blocking WatchStreamAsync overload, so a workflow that halts at an unserviced
RequestInfoEvent (human-in-the-loop / approval) blocked forever — asymmetric
with the first-turn RunAsync path, which returns at the same halt. Break the
drain when a superstep completes with HasPendingRequests, restoring symmetry
with turn 1. Add a HITL approval-gate workflow and a resume-does-not-block test.
* .NET: Warn when a HostedWorkflowState resume makes no progress
Add an optional ILoggerFactory to HostedWorkflowState and log a warning when a
resumed turn produces no events, mirroring the Python host's zero-event restore
warning (a stale checkpoint or an input that does not match the workflow's
expected type leaves session state unprogressed). Add a non-chat string workflow
helper, a capturing logger, and a red/green test.
* .NET: Resume HostedWorkflowState from durable checkpoint on cursor miss
Add CheckpointManager.GetLatestCheckpointAsync(sessionId) and have
HostedWorkflowState fall back to it when its in-memory head cursor misses, so a
durable CheckpointManager resumes a session across a process restart or a new
holder instead of restarting from the workflow's start executor. Mirrors the
Python host's per-turn get_latest read-through. Add a counting workflow that
proves resume-vs-fresh via accumulated state, plus a red/green test, and update
ADR-0032/spec-003 and the XML remarks.
* .NET: Serialize HostedWorkflowState turns through a workflow lock
A single workflow instance backs the holder and workflow instances do not
support concurrent runs (the runner throws "already owned by another runner"),
so concurrent turns could fault or race the head cursor. Serialize all turns
through one SemaphoreSlim (mirroring the Python host's workflow lock) and make
HostedWorkflowState IDisposable to own it. Add a gated workflow and a
deterministic concurrency red/green test.
* .NET: Cover non-chat resume and multi-turn checkpoint advance
Add tests for HostedWorkflowState resuming a non-chat-protocol workflow (no
TurnToken) and for a third turn continuing to advance the head checkpoint,
closing the coverage gaps the parity review flagged.
* .NET: Add streaming workflow resume path and stream the workflow sample
Add HostedWorkflowState.RunOrResumeStreamingAsync, which yields the turn's
WorkflowEvents as they occur (fresh run or checkpoint resume) under the same
serialization lock and records the head checkpoint after the stream drains,
keeping the blocking and streaming workflow paths in lockstep with the Python
host. Honor stream:true in the HostingResponsesWorkflow sample by projecting
AgentResponseUpdateEvent updates over the Responses SSE wire. Add a streaming
resume test and update the README/spec.
* .NET: Cover Responses input adaptation to a typed workflow start executor
Demonstrate that HostedWorkflowState's generic RunOrResumeAsync<TInput> is the
input-adaptation seam (parity with Python's ResponsesChannel run hook): the app
adapts the Responses input into the workflow start executor's own type at the
call site. Add a typed-brief workflow and a test, and note the seam in spec-003.
* .NET: Drain workflow resume non-blocking to prevent hang and truncation
The resume drain used a SuperStepCompletedEvent{HasPendingRequests} proxy over
the blocking public WatchStreamAsync. That proxy (a) truncated a resumed turn
when a superstep both emitted a request and queued downstream work, and (b)
could fail to fire at all — re-introducing the indefinite hang — when a resume
input drove no superstep (e.g. a rejected non-chat input).
Make StreamingRun.WatchStreamAsync(bool blockOnPendingRequest, CancellationToken)
public and drain both the blocking and streaming resume paths with
blockOnPendingRequest:false, exactly matching the first-turn RunAsync semantics
(Run.RunToNextHaltAsync). Add guard tests: resume with a rejected input does not
hang, and a resume superstep with a request plus downstream work is not
truncated (verified red against the old proxy).
* .NET: Return file-store checkpoint index in commit order
CheckpointManager.GetLatestCheckpointAsync takes the last entry of a store's
index as the head checkpoint. FileSystemJsonCheckpointStore backed its index
with a HashSet, whose enumeration order is not contractual: after a rollback
frees and reuses a slot, enumeration can diverge from commit order, so the
durable read-through could resume a stale checkpoint. Mirror the HashSet with an
insertion-ordered list and enumerate it from RetrieveIndexAsync so 'latest' is
reliable. Add a CheckpointManager.GetLatestCheckpointAsync contract test over the
file store.
Note: the HashSet disorder is only reachable via the internal rollback path, so
the test locks the ordering contract rather than reproducing the rare disorder.
* .NET: Advance cursor when a streaming resume is abandoned
RunOrResumeStreamingAsync recorded the head checkpoint only after the stream was
fully enumerated. If an SSE consumer disconnected mid-turn after supersteps had
committed, the in-memory cursor kept the previous turn's head; because the next
turn is then a cursor hit, durable read-through could not self-heal, so it
resumed pre-disconnect state. Record the run's last committed checkpoint in a
finally so an abandoned stream still advances the cursor. Add a red/green test.
* .NET: Stream only the final agent's updates in the workflow sample
ExtractUpdates streamed every agent's updates, so the sequential Writer->Reviewer
sample streamed the intermediate draft and the final answer over SSE, differing
from the non-streaming response (final message only). Filter the streamed updates
to the final agent so streaming and non-streaming produce the same response.
Live-verified against Foundry: one output item streamed instead of two.
* .NET: Isolate the holder lock in the concurrency test
The concurrency test asserted the second same-session turn did not enter the
workflow, which also passes via the engine's concurrent-run ownership guard
(which faults) rather than the holder lock (which waits). Assert instead that the
second turn is not completed while the first holds the lock: a fault would
complete the task, so a pending task isolates the holder lock from the engine
guard. Verified red with the lock removed.
* Fix IDE1006 naming in tests; address review feedback and add hosting/live tests
* Document commit-order contract for ICheckpointStore.RetrieveIndexAsync
* Restructure hosting samples under af-hosting with client/server split matching Python parity
* Clarify hosting sample README wording and drop Python comparisons
* Make AgentSessionStore.DeleteSessionAsync abstract and rename session id parameter to sessionStoreId
* Rename OpenAIResponses id helpers and parse the request once for id extraction
* Reclaim per-session locks in HostedAgentState and demonstrate session locking in the agent sample
* Internalize per-session locking in HostedAgentState (automatic, on by default) and remove mirroring-Python wording from code and spec
* Remove HostedAgentState; app-owned routes use AgentSessionStore directly
HostedAgentState only bundled an AIAgent with an AgentSessionStore and, after
the per-session lock was removed, its GetOrCreateSessionAsync/SaveSessionAsync/
DeleteSessionAsync were pass-throughs that just bound the agent argument.
Create-on-miss already lives in the store (unlike Python, whose get/set-only
SessionStore justifies its AgentState holder), so the type earned its place
only via the lock.
Each AgentSessionStore.GetSessionAsync now returns an independent session
instance per call, so concurrent gets fork the same stored state (e.g.
branching from previous_response_id or managing several conversation ids)
without sharing an instance. The store does no cross-call locking; serializing
concurrent runs against the same id is the application's concern.
- Delete HostedAgentState and its unit tests.
- Rewire the local_responses sample and the OpenAI hosting unit/integration
tests to call AgentSessionStore (GetSessionAsync/SaveSessionAsync) directly.
- Update ADR-0032, spec-003, and the af-hosting sample READMEs.
* Isolate hosted session snapshots and distinguish conversation vs response continuation
Mirrors the Python hosted-session isolation work: a hosted session read must be
an independent copy, and the app-owned route must persist under the right
continuation key depending on how the caller continued the thread.
- AgentSessionStore.GetSessionAsync: document the isolation invariant (each
call returns an independent AgentSession so concurrent branches from one
previous_response_id do not observe each other's mutations or alter stored
state); fix the stale "or null if not found" wording (in-box stores return a
fresh created session on miss). The in-box stores already satisfy this via a
serialize/deserialize snapshot round-trip.
- local_responses sample + hosting unit-test route: choose the save key by
channel. A stable conversation id is a mutable head (write back under the
same id; app owns single-writer coordination). A previous_response_id
continuation or first turn is an immutable snapshot (save under the new
response id so branches from the same prior response stay independent).
- Add regression tests: independent get returns a distinct instance
(InMemoryAgentSessionStore); previous_response_id supports independent
branches ([1,2,2,3,3]); conversation id advances the mutable head ([1,2]).
- Update the sample README and ADR-0032 wording.
* Add workflow-factory support to HostedWorkflowState for concurrent sessions
HostedWorkflowState backed every session with one shared Workflow instance and
serialized all turns through a lock, so independent sessions could not run
concurrently. Add a workflow-factory constructor and remove the run lock.
- New constructor HostedWorkflowState(Func<CancellationToken, ValueTask<Workflow>>
workflowFactory, ..., bool cacheWorkflow = false):
- cacheWorkflow: false (default) builds a fresh instance per run, so independent
sessions run in parallel. A resume rehydrates a fresh instance from the
session's checkpoint in the shared store.
- cacheWorkflow: true builds the workflow once, lazily on first use, and reuses
it (a deferred, cached target that, like a shared instance, cannot run
concurrent turns).
- Remove the internal SemaphoreSlim run lock and IDisposable; the instance
constructor is unchanged in behaviour (one shared instance still cannot run
concurrent turns). Turns are no longer serialized by the holder; a single
writer per session is the application's responsibility.
- Switch the local_responses_workflow sample to the factory constructor with an
explicit cacheWorkflow: false, and document the option.
- Add tests: parallel independent sessions (factory), fresh-instance resume,
cached factory builds once and reuses, uncached factory builds per run.
- Update ADR-0032, spec-003, and the sample README.
* Clarify in ADR-0032 how .NET covers AgentState factory and async-setup via DI
* Rebuild cached workflow after a faulted build and add checkpoint index dedup tests
|
||
|
|
f6a3c43e9a | .NET: Add source-type-agnostic consent regression test for a2a_preview (#7229) | ||
|
|
e6f7b3e9be | Version bump for .net release (#7237) | ||
|
|
c033adb1f4 |
.NET: [BREAKING] Graduate HarnessAgent (#7119)
* Graduate HarnessAgent * Switch harness project to released and remove unreleased shell dependency * Address PR comments. |
||
|
|
09473fa7ed |
.NET: [BREAKING] Bind tool-approval responses to surfaced approval requests (#7111)
* .NET: Bind tool-approval responses to surfaced approval requests Harden the tool-approval flow so an approved tool call always matches the request the framework surfaced for approval. Add ApprovalResponseBindingChatClient as the outermost decorator above FunctionInvokingChatClient. It records each model-originated ToolApprovalRequestContent in the session state and, on the next request, binds every ToolApprovalResponseContent to its recorded request: the response tool call is rebound to the recorded call, matched entries are consumed for one-time use, and only approvals tied to a framework-issued request take effect. Apply the same binding in the ToolApprovalAgent harness by tracking the requests it surfaces and binding collected responses to them during a queue cycle. Add ChatClientAgentOptions.DisableApprovalResponseBinding (default off) and a UseApprovalResponseBinding builder extension for custom chat client stacks. Includes unit tests for the decorator and the harness. * .NET: Bind approval responses once per turn and avoid re-enumeration Address review feedback on the approval-response binding decorator: consume a matched request from the per-turn lookup so a duplicate response with the same request id in one turn is honored only once, and return the materialized message list instead of the original enumerable so a single-use sequence is not enumerated twice. Rename the local pending list to pendingRequests for clarity. Adds a duplicate-response regression test. * .NET: Snapshot recorded approval requests and consume duplicates in the harness Address review feedback on ToolApprovalAgent: store a snapshot of each surfaced/pending approval request (cloned tool call with a copied arguments dictionary) so a later mutation of the caller-visible instance cannot change the recorded call used to bind the response, and consume a surfaced request on match so a duplicate response with the same request id in one pass is honored only once. Apply both symmetrically in the harness and the ApprovalResponseBindingChatClient decorator. Adds regression tests for the snapshot and duplicate-response cases. * .NET: Address review feedback on approval-response binding - Harness: store surfaced approval requests in a dictionary and consume matches directly, drop the extra hashset and the redundant record-time dedup; replace clear-on-resolution with a debug assert. - Harness pipeline: add UseApprovalResponseBinding() as the outermost decorator in HarnessAgent (it uses UseProvidedChatClientAsIs) behind a new DisableApprovalResponseBinding option, with tests. - Decorator: avoid message/content allocations when nothing changes, keep the original content when a response already matches the recorded call, clear pending each inbound turn, and shorten helpers. * .NET: Compare tool calls by fields instead of serializing Replace the JSON-serialization comparison in the approval-response binding decorator with a direct field comparison. Fast-path FunctionCallContent by comparing CallId, Name, and arguments field by field; any other tool call shape rebinds. The comparison only skips an allocation (the call is always rebound to the recorded request otherwise), so a miss just triggers a safe rebuild. Adds a test that a matching response is forwarded unchanged. * .NET: Bind approval responses against requests present in history Fix a merge-queue regression where AG-UI mixed server/client tool invocation stopped executing the server tool. The binding decorator validated approval responses only against its own recorded pending state, so a matched approval request/response pair replayed from conversation history was treated as unbound and dropped, and the auto-approved server tool never ran. Treat known requests as the recorded pending state plus any approval requests already present in the current messages, and stop dropping approval requests (a request in history is the pairing authority). A response with no known request anywhere is still dropped, so a forged approval cannot execute. Also address review feedback: return the mutable contents buffer from a helper instead of a null-forgiving operator, and use clearer naming (PrepareMutableContentsBuffer / mutableContentsBuffer). Adds regression tests for a request in history and a response bound to a history request with empty pending state. |
||
|
|
9cf5143321 |
.NET: Populate AgentResponse metadata in CopilotStudioAgent (#6791)
* .NET: Populate AgentResponse metadata in CopilotStudioAgent Map CreatedAt, FinishReason, RawRepresentation and AdditionalProperties onto AgentResponse and AgentResponseUpdate, and map the activity timestamp and properties onto ChatMessage, so Copilot Studio agents expose the same metadata surface as other AIAgent implementations. Streaming sets the finish reason on the terminal update while still emitting already-received content if the source faults. Add unit tests covering the metadata mapping. * fix: add Async suffix to async test methods (IDE1006) |
||
|
|
3ab2630243 |
.NET: Refactor Workflows MessageMerger to preserve message order and structure (#6826)
* Refactor MessageMerger to preserve message order Refactored MessageMerger to delegate update grouping and merging to M.E.AI, preserving the correct order and structure of assistant messages, especially for reasoning content without message IDs. Removed per-message bucketing and CreatedAt-based sorting. Added tests to verify message order and correct merging of reasoning and text updates. * Set CreatedAt from merged responses preservation of original message timestamps during merging. * Set merged message CreatedAt to current UTC time Removed logic for tracking unique creation times and now always assign DateTimeOffset.UtcNow to the merged response's CreatedAt property. This simplifies timestamp handling during message merging. * Refactor MessageMerger id-less folding logic Refactored MessageMerger to fold identifierless reasoning segments into the following id'd message at the flattened-message level, ensuring correct merging across response buckets (fixes #6329). Updated ComputeMerged to merge id-less messages with the next message of the same role. Removed redundant per-bucket folding logic. Added unit tests to verify correct folding behavior and role matching. * Remove unused property Removed the unused Role property from MessageMergeState for code cleanliness. * Refactor MessageMerger to iterate backward for merging Changed MessageMerger to iterate messages in reverse order, ensuring all consecutive messages without IDs preceding a message with an ID are merged correctly. Updated merging logic, index handling, and comments to reflect this new approach. * Update code comment to better reflect its behavior. --------- Co-authored-by: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com> |