main
711 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
a2a8635417 |
.NET: Stabilize Foundry recovery tests (#7817)
* .NET: Fix Foundry recovery test synchronization * .NET: Observe completed recovery checkpoints in test * .NET: Stabilize Foundry crash recovery integration test |
||
|
|
abe1f629a2 |
.NET: Add support for Resilient long-running and Steerable Foundry Hosted Agents (#7370)
* feat(foundry): add resilient background hosting Enable AgentServer recovery and steering through FoundryResponsesOptions. Persist AgentSession snapshots during long background turns while workflow checkpointing remains owned by the workflow runtime. * feat(foundry): complete resilient and steerable hosting * fix(foundry): address resilience review feedback * feat(foundry): align resilient workflow checkpoints * docs(foundry): update resilience review guidance |
||
|
|
eccf47154f |
.NET: Fix ReasoningSummary passthrough in GitHub Copilot resume config (#6441)
* .NET: Fix ReasoningSummary passthrough in GitHub Copilot resume config CopyResumeSessionConfig hand-copies a subset of SessionConfigBase into a new ResumeSessionConfig instead of using Clone(). It was missing ReasoningSummary, so callers that set SessionConfig.ReasoningSummary got readable extended-thinking summaries on the first turn but had it silently dropped on every resumed turn. ContextTier (a sibling model/context knob passed alongside ReasoningEffort/ReasoningSummary) was missing too. Both are now copied, mirroring ReasoningEffort. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * .NET: Assert ReasoningSummary/ContextTier defaults in null-source resume test Addresses PR review: the null-source CopyResumeSessionConfig test now also asserts ReasoningSummary and ContextTier default to null, locking the intended default behavior of the newly copied properties. Co-authored-by: Copilot <223556219+Copilot@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> |
||
|
|
8f68f6be5d |
Bump Anthropic from 12.35.1 to 12.42.0 (#7778)
* Bump Anthropic from 12.35.1 to 12.42.0 --- updated-dependencies: - dependency-name: Anthropic dependency-version: 12.42.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * Update Anthropic test clients for SDK interfaces Co-authored-by: rogerbarreto <19890735+rogerbarreto@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: rogerbarreto <19890735+rogerbarreto@users.noreply.github.com> |
||
|
|
43b3ce6027 |
.NET: Add feature-usage bitmask (#7709)
* Add feature-usage User-Agent telemetr * Removed static keyword from irrelevant methods. * Update method names and fix CI test issue. * Revert irrelevant changes. * Address PR comments. * Fix CI issue from merge conflict resolution. |
||
|
|
96560bbf65 |
.NET: [BREAKING] Migrate MCP long-running task support to the 2026-07-28 Tasks extension (#7774)
* Migrate MCP long-running task support to the 2026-07-28 Tasks extension * Address PR comments. * Address PR comments. * Address PR comments |
||
|
|
ab0f7d5d08 |
.NET: Persist hosted agent state in Foundry (#7649)
* Point the AgentServer packages at the local preview drop The durable state-store API this branch is built on ships in Core beta.28, which is not on nuget.org yet. The local feed is a stopgap for developing against it and must be removed before this branch ships. * Keep hosted agent state on the platform instead of the container disk A hosted agent kept its sessions, and a hosted workflow its checkpoints, in files under the container's own directory. That state is lost when the container is replaced and cannot be read by another instance of the same agent, so a conversation could not survive a restart or be served by more than one instance. Both now go to the Foundry durable state store when the process runs in a Foundry container, and stay on disk everywhere else: - FoundryAgentSessionStore holds the agent sessions, partitioned by agent, conversation and end user. - FoundryJsonCheckpointStore holds the workflow checkpoints, one item per checkpoint plus a per-session index that keeps them in commit order. Retrieving a checkpoint deletes the rest of that session's checkpoints, which is the only point at which nothing can still reach them, and is what stops the index growing past the size the platform accepts for one item. A workflow agent is redirected to that checkpoint store when it is resolved for a request, so nothing changes in how a container registers one. An agent built with a checkpoint manager of its own is left alone and reported by the new foundry-workflow-checkpointing readiness check, because its state would go somewhere hosting does not manage. Workflow agents are recognised through a new WorkflowAgentMetadata returned by GetService, which still finds them behind middleware. * Keep the readiness probe from running the agent's providers The stored-output probe ran the registered agent with its chat client replaced, which still set the agent's chat history provider and context providers running. Those are the parts most likely to reach outside the container and to write state, so every readiness probe could make external calls and add its own empty turn to real conversations. The probe now runs a stand-in built from the agent's own options with both kinds of provider dropped. It keeps what decides the setting, the chat options and the raw request factory, and cannot see a decorator wrapped around the agent, which is accepted for a readiness check. * build: bump AgentServer preview packages Core beta.29 adds the shared local state-store fallback used by hosted sessions and workflow checkpoints. Align its Azure Core and System package dependencies to avoid assembly and downgrade conflicts. * feat(foundry): use AgentServer state fallback Use FoundryStateStore for sessions and workflow checkpoints in every environment. Core beta.29 selects Foundry Storage when hosted and a file-backed local store otherwise, so local runs exercise the production storage shape without requiring Azure credentials. Give the hosted workflow sample stable inner-agent identities so its checkpoints remain compatible after container replacement. * fix(hosting): harden durable state storage Use published AgentServer packages so CI no longer depends on a local package source. * build(hosting): scope AgentServer versions Keep public package versions on their consumers so unrelated projects retain the central versions from main. * build(hosting): use public AgentServer packages Remove project overrides and keep package selection in the central catalog now that the required public releases are available. * fix(hosting): preserve durable state identity Keep keyed and default aliases on one session partition. Reject unstable unnamed direct-store usage and preserve live checkpoint branches during pruning. * refactor(hosting): centralize hosted metadata Carry storage identity through a hosting-specific agent wrapper, keep unknown middleware non-blocking at readiness, and align StateStore constructor parameter order. * refactor(hosting): move session identity into store * docs(hosting): explain session identity resolution * fix(hosting): preserve protocol mismatch status Reject unsupported protocol requests before AgentServer wraps handler failures in ResilientTaskException and converts the intended 501 response into a generic 500. |
||
|
|
e6617c407a |
.NET: Add Azure Blob Storage session persistence (#1893)
* setup azurestorage proj * setup for azure blob as agentthreadstore * add azurite as dependency for dotnet tests * use services * rollback * azurite as a step * move and rename * renames / fixes * rename to unit tests * copilot changes * .NET: Modernize Azure Blob session storage Copilot-Session: 35e63850-1a85-4f7c-ac80-2274534c13b5 * .NET: Test hosted Blob session persistence Copilot-Session: 35e63850-1a85-4f7c-ac80-2274534c13b5 * .NET: Address Azure Blob storage review feedback Copilot-Session: 35e63850-1a85-4f7c-ac80-2274534c13b5 * .NET: Use default test target frameworks Copilot-Session: 35e63850-1a85-4f7c-ac80-2274534c13b5 --------- Co-authored-by: Roger Barreto <19890735+RogerBarreto@users.noreply.github.com> Copilot-Session: 35e63850-1a85-4f7c-ac80-2274534c13b5 |
||
|
|
d29e7be7fd |
.NET: Fix A2A streaming artifact updates (#7722)
* .NET: Fix A2A streaming artifact updates Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: d07945ad-4b76-48da-a499-a915c076cff9 * Flush buffered A2A artifacts on stream failure Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: d07945ad-4b76-48da-a499-a915c076cff9 * Aggregate A2A message streams incrementally Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: d07945ad-4b76-48da-a499-a915c076cff9 * Fix duplicate A2A message declaration Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: d07945ad-4b76-48da-a499-a915c076cff9 --------- Co-authored-by: SergeyMenshykh <SergeMenshikh@outlook.com> Copilot-Session: d07945ad-4b76-48da-a499-a915c076cff9 |
||
|
|
e2938f4531 |
.NET: Remove AGUI history special cases (#7741)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> |
||
|
|
064751c5f3 |
.NET: Upgrade AG-UI SDK packages to 0.0.5 (#7742)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 425c405b-1fd3-4ba6-b332-a195598374b4 |
||
|
|
10bf8d7d9e |
.NET: agent-hooks interception contract as a first-class experimental feature (#7564)
* feat(dotnet): agent-hooks interception contract as an experimental package Add Microsoft.Agents.AI.AgentHooks, implementing the AGENT-HOOKS-0.1 control contract on the framework's native decorator seams, mirroring the merged Python feature (#7515) in .NET idiom: - One public factory (CreateAIAgentWithAgentHooks, per-run and host-owned-session overloads) composes agent, chat and function seams as one indivisible unit; the seam decorators are internal, so partial installs are impossible by construction. - All eight interception points: input/output at the agent seam, pre/post_model_call below the function-invocation loop (every model service call bracketed individually), pre/post_tool_call via the function-invocation middleware seam, agent_startup/agent_shutdown bracketing each run. - Fail-closed enforcement throughout: transforms write back into the native messages/arguments/results or throw; rich content is preserved as AIContent objects; interceptor crashes surface as host_error denies; enforcement-layer failures halt the run through FunctionInvocationContext.Terminate (the loop's only loud escape). - Streaming is fully buffered per spec buffered_output semantics: a deny releases zero updates; transformed responses re-derive the released updates so egress never diverges from verdicted content. - Verdict-before-durability: end-of-run history and context-provider writes defer behind the output verdict via gating provider wrappers (flushed post-transform with verdicted-message substitution for streams, dropped on deny); per-service-call persistence sits above the chat seam and is covered by its own post_model_call verdict; per-run history-provider overrides in run options are wrapped too; nested guarded sub-agents persist inline at their own boundaries. - Opt-in dependency: ResponsibleAI.AgentHooks 0.1.0-alpha.4 (bundles native runtimes) referenced only by the new package; no existing framework source is modified. - 58 tests: deny-before-execution and transform write-back per seam, rich-content preservation, streaming ordering with zero egress on deny, error bracketing, concurrency isolation, host-owned sessions, evaluate_only, approval-seam lift, persistence gating, misuse fail-closed paths, and codec units. Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com> * fix(dotnet): close structural bypasses at the ChatClientAgent boundary Address both reviewers' probe-confirmed findings; the runtime enforcement held everywhere, every fix is at the structural boundary: - Gate the implicit default ChatHistoryProvider: with no provider configured, ChatClientAgent creates an InMemoryChatHistoryProvider the factory never saw, so denied output became durable session history and replayed to the model on the zero-config path (both stream modes). The factory now materializes and gates the default, setting the history-conflict flags to mimic implicit-default semantics for service-managed-history agents. - Wrap per-run provider overrides on BOTH dictionaries: base AgentRunOptions.AdditionalProperties is merged into the chat options with precedence, so a base-level override bypassed (and displaced) the wrapped ChatOptions-level entry. Plain AgentRunOptions is covered too, and the wrap is copy-on-write — the caller's options and dictionaries are never mutated. - Reject per-run ChatClientFactory on guarded agents (fail closed): it would replace the guarded chat pipeline and the tool-wrapping stage riding it, silently removing the chat and tool seams. - Reject a supplied client already containing a FunctionInvokingChatClient: it would execute tools below the chat seam, before any post_model_call verdict and outside the tool seam. - Run wire projections inside the guarded blocks at the chat and function seams: a poisoned value whose serialization throws now fails the run closed (function seam: host_error halt; chat seam: gated persistence refused before the failure propagates). - Suppress provider failure notifications once a run-level deny or halt stands, so the denied turn's request messages never reach provider code. - Document the deferred-OpenTelemetry observer channel (request-side spans capture pre-transform content under sensitive-data telemetry). - Rename the factory to AsAIAgentWithAgentHooks per repo convention. 10 new boundary regression tests mined from the review probes (default-provider durability in both stream modes with session-replay assertions, both override dictionaries incl. the displacement shape, plain-run-options override, copy-on-write, factory and supplied-FICC rejections, poisoned-projection fail-closed); 68 total, all green. Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com> * fix(dotnet): redact denied-run failure notifications for both provider kinds The deny/halt handling of provider failure notifications only covered the chat-history wrapper; a context provider still received the denied turn's request messages on its failure notification. Both gating wrappers now REDACT instead of suppress: the notification is forwarded with empty request messages and the original exception, preserving the documented failure-cleanup contract (providers releasing per-run resources on the failure signal keep working) while the denied turn's request messages never reach provider code. Regression tests assert both provider kinds receive the redacted notification (zero request messages) on a denied run and full notifications on ordinary, verdict-free failures. 70 tests total. Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com> * fix(dotnet): address Copilot review on the agent-hooks PR - Run options: always clone chat-typed run options (the framework's function-invocation middleware chains its per-run factory onto the instance it receives, so forwarding the caller's instance leaked that factory into it — reuse tripped the rejection, concurrent reuse raced), and recognize the framework middleware's own factory as legitimate: it wraps the guarded pipeline (tool rewriting), so outer function-middleware composition now works, while its chained factories are walked so a caller-supplied factory cannot ride in unnoticed. - Streaming: re-derived (transformed) updates preserve the response's ContinuationToken (ToAgentResponseUpdates does not project it), so transformed background streaming responses remain resumable; a message-less response releases a metadata-only update carrying it. - Codecs: transformed tool calls are validated for complete shape and uniqueness before reconciliation (non-empty string id and name, object-valued args, distinct ids) — malformed shapes fail closed instead of becoming invalid native calls. Deliberately stricter than the merged Python codec, which coerces added-call shapes. - Role defaulting in message write-backs is confirmed exact Python parity (user/assistant defaults per the merged codecs) and is now locked by tests rather than changed. - ADR 0035 records the seam order, persistence gating, fail-closed behavior, alternatives and known limitations. 14 new tests (options reuse, outer function-middleware composition, smuggled-factory rejection, continuation-token preservation, 8 malformed tool-call shapes, 2 role-default parity); 84 total, green. Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com> * feat(dotnet): project the per-call tool set on pre_model_call emissions Context providers can register additional tools during run preparation, after agent_startup has been emitted, so tools_registered is inherently a run-start snapshot and can be a partial view of the tools eventually offered to the model. - Emit the spec's optional pre_model_call tools field ({name, description?}) from the per-call effective ChatOptions.Tools — the completed set for each call, including provider-added tools. - Document tools_registered as the run-start snapshot on the agent seam (dynamic registrations surface per call and are bracketed by the tool seam when invoked). - Probe-confirm enforcement completeness for provider-added tools: they flow through the guarded pipeline's tool-wrapping stage, emit pre/post_tool_call, and a pre_tool_call deny blocks their invocation exactly like constructor-registered tools. Two new tests (bracketing + audit projections, deny-blocks); 86 total, green. Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com> * refactor(dotnet): one artifact per file; rewrap foreign gating wrappers Per review: - Split the three multi-type files (AgentHooksGatingProviders.cs, AgentHooksRunState.cs, AgentHooksWireCodecs.cs) into one type per file, file name matching the type name, per repo convention. No behavior changes; namespaces and access levels unchanged. - Close a validation asymmetry at the provider gate: the per-run override wrap skipped any gating wrapper, including one owned by a DIFFERENT agent-hooks installation — which runs inline under this run's state (its own gate is not covering here), so a denied run's history could persist straight through it. Overrides are now re-wrapped unless the wrapper belongs to this installation (reference-equal configuration). The provider seam's inline behavior for foreign/absent state is otherwise deliberate: inline is the safe direction there (content of unguarded or differently guarded runs is covered by its own verdicts or none), and throwing would break the legitimate double-wrap flush flow. One new regression test (foreign wrapper as per-run override on a denied run persists nothing); 87 total, green. Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com> * refactor(dotnet): accept params IEnumerable for agent-hooks interceptors Per review: the constructor only iterates the interceptors, so widen the parameter from params IInterceptor[] to the C# 13 params IEnumerable<IInterceptor>. The sequence is enumerated exactly once into the internal registration list (sequences may be single-enumeration); per-item null validation and the factory's at-least-one-interceptor check are unchanged, and an explicit null sequence now throws ArgumentNullException. Params-form call sites are source-compatible. Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com> * build(dotnet): ship Microsoft.Agents.AI.AgentHooks as a preview package Per maintainer review on the PR: - Add the project to agent-framework-release.slnf and import the shared packaging props so the package ships. Version follows the repo default for unmarked packages (preview suffix), matching the package's [Experimental] surface and alpha upstream dependency: 1.17.0-preview.<date>.1. - Package metadata: sibling-style title, fuller description, tags; shared icon and NUGET.md readme via the packaging props. Verified dotnet pack locally: ResponsibleAI.AgentHooks 0.1.0-alpha.4 flows as a normal dependency and the project references become 1.17.0 package dependencies. - Update ADR 0035: shipping as preview per maintainer decision replaces the build-only-pending-maturity stance. Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com> * build(dotnet): version the agent-hooks package as alpha Per maintainer review: the package's maturity marker follows the ResponsibleAI.AgentHooks dependency it is built on (alpha), rather than the repo's default preview suffix. Packs as 1.17.0-alpha.260804.1; ADR 0035 updated. Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com> * refactor(dotnet): group agent-hooks internals into Core and Codecs folders Per review: only the public surface (the factory extensions and options) stays at the project root; the internal seam decorators, run state and gating providers move to Core/, and the wire projection codecs to Codecs/. Pure file moves — namespaces stay flat per the core package's folder convention (ChatClient/, Memory/); no content changes. Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com> * docs(dotnet): clarify session scoping and name the sessionId argument Per review: - Name the AgentContextBuilder arguments at the run-state factory so the GUID reads as what it is (the per-run agent-hooks session id). - Document both branches of CreateRunState: session-scoped means the host owns the emitter/builder and the session boundaries (one session spanning runs, no agent_startup/agent_shutdown emitted by the agent); the default is one session per run with a fresh emitter, fresh sequence and isolated record trail, which is what keeps concurrent runs' emissions from interleaving. Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com> * fix(dotnet): harden agent-hooks factory and input projection per review - Input projection returns (payload, content, role) as one typed result so the emission site never re-reads payload properties by name: the both-fields-exist invariant holds by construction. (The previous reads were fail-closed even hypothetically — JsonObject's indexer yields null, and a null content is rejected by the SDK's envelope validation — but reading back what we just produced was needlessly fragile-looking.) - Reject UseProvidedChatClientAsIs on the factory: it signals a fully custom, do-not-touch client stack, which is incompatible with a factory whose job is to decorate the supplied client and rely on the agent's default pipeline above the chat seam. Honoring it would silently change where (and whether) the seams sit. - Log swallowed agent_shutdown emission failures (logger resolved the same way the agent resolves its own: services, then the chat client, then null) so incomplete session trails are trackable; OutOfMemoryException stays unswallowed. The swallow remains correct: the run's own outcome is already propagating and the trail closure is best-effort by contract. 89th test: UseProvidedChatClientAsIs rejection. Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com> * build(dotnet): attribute the Agent-Hooks protocol in the package identity Per review: - Title per suggestion: 'Microsoft Agent Framework - Responsible AI Agent-Hooks Protocol Support'; description names the protocol precisely (AGENT-HOOKS-0.1, maintained by the Responsible AI project at github.com/responsibleai/agent-hooks) so the package reads as protocol support, not a MAF-owned feature; tags aligned. - Drop the [Experimental] attributes: per repo convention the attribute gates unstable surface inside released packages (Harness, core), while pre-release packages (Valkey and Mcp at alpha, Mem0 and LocalCodeAct at preview) carry none — the version suffix is the maturity signal. - Drop the describing comment on the central package version entry. Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com> * refactor(dotnet): split agent-hooks test fixtures into Support files Per review: one type per file under Support/ (mock client, guards, recording providers, helpers), matching the src-side convention; pure mechanical split, flat namespace. Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com> --------- Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com> |
||
|
|
f330457042 |
.NET: Pass IServiceProvider to ChatClientAgent in AddAIAgent overloads (#7737)
All four AddAIAgent overloads in AgentHostingServiceCollectionExtensions created a ChatClientAgent without forwarding the IServiceProvider, so the FunctionInvokingChatClient in the agent's pipeline had no service provider and tools could not resolve their dependencies at invocation time. Fixes #4453 Co-authored-by: Max Montes Soza <max-montes@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
4be584cc53 |
.NET: Add session-persisted chat client routing (#7641)
* Add RoutePersistingRoutingChatClient * Address PR comments * Address PR comments |
||
|
|
c6584ffaad |
.NET: Add Cosmos chat history retrieval API (#7412)
* .NET: Add Cosmos chat history retrieval API * Clarify Cosmos message ordering semantics |
||
|
|
74808cb6c7 |
.NET: Add Foundry hosted session and user identity pass-through (#7648)
* .NET: Add Foundry session and user identity pass-through Let user agents pin hosted agent_session_id on AgentSession and send x-ms-user-identity per call for Foundry hosted agents. * .NET: Add live ITs for Foundry session and user identity Cover service-managed and admin-pinned hosted sandboxes, sticky hosted session id, and per-call x-ms-user-identity isolation with separate AgentSessions sharing one sandbox. Echo container avoids model quota for identity assertions. * .NET: Reject Foundry hosted session switch when sticky Persist sticky id in finally, clone run options before factory wrap, validate whitespace pin on CreateHostedSessionAsync, and throw on unexpected hosted session id change in the response. Docs: distinct AgentSessions per user identity may share one sandbox. * .NET: Clear nested user identity and preserve run options Always assign UserIdentityScope including null so nested runs do not inherit a parent identity. When upgrading plain AgentRunOptions, keep background, format, and additional properties on the specialized clone. * .NET: Clarify previous_response_id user binding in docs Align WithUserIdentity guidance with Foundry Learn multiplex docs: response chains are bound to the creating user even inside a shared hosted sandbox. * refactor(foundry): clarify hosted agent APIs |
||
|
|
047ec7eaff |
.NET: Allow agents to opt into concurrent tool invocation (#7650)
* .NET: allow agents to opt into concurrent tool invocation * .NET: address concurrent invocation review feedback |
||
|
|
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) | ||
|
|
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> |
||
|
|
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 |
||
|
|
d56e81357e | Fail declarative workflows when an agent returns an error (#7497) | ||
|
|
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 |
||
|
|
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. |
||
|
|
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 |
||
|
|
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> |
||
|
|
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> |
||
|
|
28e02d4669 | Create session for tool approval agent when non-present (#7310) | ||
|
|
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 |
||
|
|
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) | ||
|
|
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> |