MentorAgent Docs
Menu

Questa pagina non è ancora tradotta in italiano: qui la trovi in inglese.

ReferencePackages: Novior.MentorAgent, Novior.MentorAgent.Abstractions, Novior.MentorAgent.Blazor, Novior.MentorAgent.Declarative, Novior.MentorAgent.Server

MentorAgent 1.2.0 release notes

What MentorAgent 1.2.0 adds and fixes: only permitted actions reach the model, your own policies, who confirmed, the AI notice, and how to upgrade.

On this page

MentorAgent 1.2.0 was published on NuGet on 27 October 2026 (UTC). It is a minor version: it adds features and fixes defects. It has one intended break, in how a tool result becomes a generative card: see Breaking changes.

The five packages require each other at exactly this version: upgrade them together. MentorAgent 1.2.0 runs on .NET 10. To upgrade, see Upgrade from 1.1.0.

The roles say who may use an action. Many rules depend on the data instead: a permission per company or per record, or a limit on an amount. An IMentorActionPolicy, registered with AddMentorActionPolicy<T>(), decides whether a user may use an action and whether a call may run. It applies on every surface: the widget, the hub, the MCP server and A2A tasks, the specialists, the teams and the MCP servers that you consume.

CaseTeamPolicy.cs
public class CaseTeamPolicy : IMentorActionPolicy
{
    // Whether the user may use the action: only the support team closes cases
    public ValueTask<bool> IsAllowedAsync(
        MentorActionRequest request, CancellationToken cancellationToken)
        => ValueTask.FromResult(
            request.ToolName != "close_case"
            || request.User?.HasClaim("team", "support") == true);

    // Before each call: a case that is already closed is not closed again
    public async ValueTask<MentorActionVerdict> BeforeActionAsync(
        MentorActionRequest request, CancellationToken cancellationToken)
    {
        if (request.ToolName != "close_case"
            || request.Arguments?.GetValueOrDefault("caseNumber")?.ToString()
                is not { } caseNumber)
            return MentorActionVerdict.Proceed;

        var store = request.Services.GetRequiredService<ICaseStore>();
        var status = await store.GetStatusAsync(caseNumber);
        return status.Contains("closed", StringComparison.OrdinalIgnoreCase)
            ? MentorActionVerdict.Stop($"Case {caseNumber} is already closed.")
            : MentorActionVerdict.Proceed;
    }
}

A policy can let a call proceed, ask for a confirmation or stop it, and the strictest answer wins. A policy that throws, or that does not answer within ActionPolicyTimeout, stops the call. See Action policies and observers.

Who confirmed, and every decision of the gate

Section titled “Who confirmed, and every decision of the gate”

A record of who approved a change belongs in your ledger, next to the change. Inside an action that asked for confirmation, MentorActionConfirmation.Current says who confirmed it and when, with the ID of the confirmation and the tool. It works in Blocking and in Native mode, and it is null for an action that nobody confirmed:

CaseEscalation.cs
public class CaseEscalation(ILogger<CaseEscalation> logger)
{
    [MentorAction(
        Description = "Escalates a support case to the second line",
        RequiresConfirmation = true)]
    public string EscalateCase(string caseNumber)
    {
        // Who confirmed this call, and when: null when nobody confirmed it
        var confirmation = MentorActionConfirmation.Current;
        logger.LogInformation(
            "Case {Case} escalated, confirmed by {User} at {Time}",
            caseNumber,
            confirmation?.ConfirmedBy?.Identity?.Name,
            confirmation?.ConfirmedAt);
        return $"Case {caseNumber} escalated.";
    }
}

An IMentorActionObserver, registered with AddMentorActionObserver<T>(), receives every decision about a tool call, on every surface and for every agent: the outcome, a reason code that stays the same across versions, the tool, the agent, the session, the user and who confirmed. It never receives the arguments, and a decision never waits for it:

ActionAuditLog.cs
public class ActionAuditLog(ILogger<ActionAuditLog> logger) : IMentorActionObserver
{
    public ValueTask OnDecisionAsync(
        MentorActionDecision decision, CancellationToken cancellationToken)
    {
        logger.LogInformation(
            "{Tool}: {Outcome} ({Reason}) for {User}, confirmed by {ConfirmedBy}",
            decision.ToolName, decision.Outcome, decision.ReasonCode,
            decision.User?.Identity?.Name ?? "anonymous",
            decision.ConfirmedBy?.Identity?.Name ?? "nobody");
        return ValueTask.CompletedTask;
    }
}

IMentorStateService.OnActionStarted also gives the technical name of the tool that starts, next to the text shown.

A component that shows or checks your actions needs to see them as MentorAgent does. IMentorActionCatalog.GetActions() lists the actions of the coordinator, of the specialists and of the team members, with their roles, whether they ask for a confirmation, whether they only read, and whether the MCP server and the A2A card publish them.

ActionChecks.cs
// The writes that the MCP server publishes to every caller, for example
// to fail a test when one appears
public static IEnumerable<string> WritesPublishedToEveryone(
    IServiceProvider services) =>
    services.GetRequiredService<IMentorActionCatalog>().GetActions()
        .Where(action => !action.ReadOnly
            && action.McpExposure == MentorMcpExposure.Published)
        .Select(action => action.ToolName);

A confirmation box that nobody answers kept the action waiting. With ConfirmationTimeout, a box that nobody answers in time counts as rejected: the action does not run, and the model is told that nobody confirmed it in time. By default there is no limit, as before.

A notice and a mark for AI-generated replies

Section titled “A notice and a mark for AI-generated replies”

Article 50 of the EU AI Act asks that people know when they talk to an AI system, and that generated content can be told apart. The widget shows a notice under its header, ShowAiDisclosure, with a default text in the ten languages, or yours in AiDisclosureMessage. Every reply of the model also carries a machine-readable mark: data-ai-generated="true" in the widget, "aiGenerated": true on /mentor/chat, the hub event ReplyStarted and aiGenerated on A2A. Whether the article applies to your application, and which words to show, is yours to decide.

A site in more than one language needs the widget in the language of the page. <ChatWidget Language="…" /> gives one page a language of its own: the widget's texts, its voice, its tour and the assistant's answers follow it.

MainLayout.razor
<ChatWidget Language="@_language" />

@code {
    // From the route (/it/…) or from a language switch in your layout
    [Parameter] public MentorLanguage? Lang { get; set; }

    private MentorLanguage _language => Lang ?? MentorLanguage.English;
}

Clients send it as language on /mentor/chat or with the hub method SendUserMessage. A2A and MCP stay on Language.

  • A theme that follows the system. A site with a light and a dark mode had to pick one theme for the widget. MentorTheme.Auto shows the light theme, Minimal, or the dark one, Default, as the visitor's system asks.
  • The context of a request at the end of the conversation. With SessionContextPlacement set to LastUserMessage, the page, its data and the time go with the user's last message, and the instructions stay the same through the session. A model that caches the prompt by its beginning, such as a local model, then answers sooner.
  • Roles: the model sees only the actions the user may run. At the start of every turn, MentorAgent decides which actions the user may run, and offers the model only those: on the coordinator, the specialists, the team members and the agents of an IMentorAgentSource. The model is told that some actions are not available, without naming them, in the language of the turn. A call to an action left out is still refused, as before. Under Fixes you find what this fixes.
  • Proactive no longer closes with next steps. With MentorshipLevel.Proactive, the answers no longer end with suggested next steps: the model offered steps that the application does not have (BUG-074). The explanations and the warnings stay. To bring the suggestions back, set ProactiveNextSteps to true.
  • The widget shows the AI notice by default. Under its header, the widget now says "You are talking to an AI assistant: its replies are generated automatically.", in the language of Language. Write your own text with AiDisclosureMessage, or set ShowAiDisclosure to false.
  • The hub sends ReplyStarted before each reply. A client that does not register it keeps working, but SignalR logs a warning for every reply. Register it, even with a handler that does nothing, as the components of Any front end do. The WebAssembly client of Novior.MentorAgent.Blazor registers it.
  • Traces and logs. The mentor.tool.* spans carry the outcome, the reason code, the confirmation and the surface, and a failed call sets the status of the span to error. A call refused for its roles is logged once, at Warning, in both modes. The MCP server tells apart, in its log, a call refused for its roles, an action that it never publishes and an unknown name.
  • A check of the public API. At pack time, every package is checked against the public API of 1.1.0. An intended break is listed under Breaking changes.
  • Security and support policy. SECURITY.md says that a vulnerability report is answered within 5 working days, on a best-effort basis, and how long each version gets security fixes: see Versioning and support.

Each defect has a severity from S1, the most severe, to S4: see Versioning and support.

Breaking. Severity S3 (BUG-212). A tool result became a generative card by its shape alone: any object with a non-empty string kind. So a record of yours with such a property, for example a support ticket with a link that a customer wrote, was drawn as a card, the link became a button, and the other fields never reached the model.

Now a result is read for cards only when its method is declared to return them: MentorCard, or a sequence of MentorCard, directly or through a Task.

What to do. If a method returns cards as object, as a JsonElement built by hand or as a type of your own, declare its return type as MentorCard or IEnumerable<MentorCard>. Otherwise its cards are shown as text. A MentorCard that an OnToolResult hook returns is still a card. The result of an MCP tool never is.

The model is offered only what the user may run

Section titled “The model is offered only what the user may run”

Severity S3 (BUG-200, BUG-201, BUG-205) and S2 (BUG-224). The coordinator's model was offered every action, including those the user had no role for. Asked for one, it announced it, and the check of the roles then refused it. Specialists, team members and the agents of an IMentorAgentSource had the same defect (BUG-205). Now each of them is offered only the actions that the user may run. With RequireActionForChangeRequests, such an action is no longer one that the first call can be required to make (BUG-201).

The notice that tells the model that some actions are not available was in English in every language, and in Italian the model still offered the action. The notice now follows the language of the turn, and tells the model not to offer such an action and not to use another tool in its place (BUG-224). A residue remains: see BUG-224.

  • BUG-204 (S2): a Stop pressed after the user confirmed an action, but before the action started, did not stop it. Now the action does not run, and the model is told that the user stopped the request.
  • BUG-203 (S2): in Native mode, a tool that the RequiresApproval predicate gated after the coordinator was built ran with no confirmation. Now the confirmation box appears, as in Blocking mode.
  • BUG-208 (S3): in Native mode, the confirmation box of an action could take its text from another method with the same tool name, so the user was asked to confirm what the other method does. The box now reads as the method that the call runs, as the 1.1.0 notes said for both modes (BUG-188).
  • BUG-225 (S2): in Native mode, a model response with two calls of one ID broke the conversation until it was reset. Now the second call gets an ID of its own, except with UseServiceManagedHistory.
  • BUG-202 (S2): the MCP server read the RequiresApproval predicate once, at the first request. An action that the predicate gated after that stayed in tools/list and ran for a caller that nobody could ask. Now the predicate is read for every list and every call.
  • BUG-198 (S3): a shared MCP server that connected but did not answer ping was opened and dropped again in a loop, and the turn never reached the model. Now it is left out for a while, like a server that is down, and a warning says so.
  • BUG-197 (S2): on A2A 1.0.0-preview3, a turn delegated to a remote agent came back empty, with no error. Now MentorAgent declares streaming, and streams to a peer only when its card declares it.
  • BUG-199 (S2): a caller that cancelled an A2A task was answered Working, and the task went on. Now cancelling stops the task as Stop does, and the task ends Canceled.
  • BUG-210 (S2): a Stop pressed while a request was delegated to a remote agent did not tell the remote agent, which finished the task. Now MentorAgent cancels the task on the remote agent.
  • BUG-211 (S3): a Stop during the first message, while the card of a remote agent was being read, kept that agent out of the session. Now the next message reaches it.
  • BUG-226 (S2): an A2A caller told about the task at once (ReturnImmediately) could become anonymous, so its actions with roles were refused. Now the caller is read when the task starts.
  • BUG-217 (S1): a message that held the text that the widget uses inside as a placeholder for a code block, such as @@BMCODE7@@, threw out of the render. On Blazor Server the circuit ended, and the page stopped responding until a reload. The text may come from a user, or from the model repeating data that others wrote. Now it is shown as it is.
  • BUG-209 (S3): the links to the widget's stylesheet and script kept ?v=10 in every release, so a returning visitor's browser could keep the files of the previous release. Now the links carry the package version. If you write the two links yourself, see Upgrade from 1.1.0.
  • BUG-213 (S3): under a Content-Security-Policy without style-src 'unsafe-inline', such as default-src 'self', the Dark and Minimal themes, a PrimaryColor and the colors of <MentorDashboard /> were dropped. Now they work with any option.
  • BUG-215 (S3): a Markdown link in a reply was shown as raw text. Now an http or https link opens in a new tab. Any other address stays text.
  • BUG-216 (S3): a numbered list with a line of text between its steps started again from 1.
  • BUG-214 (S4): IntelliSense described MentorTheme.Default as a light theme. Default is dark glass, and the light theme is Minimal: see Theme.
  • BUG-206 (S3): the tour was written from every action, including those that need a role, and GET /mentor/tour answers anyone. Now it is written from the actions that need no role.
  • BUG-207 (S4): a visitor who closed the page while the model wrote the tour ended the generation, and every visitor got the built-in tour until a restart. Now the generation does not depend on any visitor.
  • BUG-221 (S3): with a Language other than English, the instructions that MentorAgent writes for the model were in English, and the model could copy them into its reply. Now they follow Language.
  • BUG-223 (S2): with RefuseOutOfScope, a short reply such as "Sì" to the assistant's question was judged on its own, and refused as out of scope. Now it is judged with the end of the assistant's last reply.
  • BUG-222 (S2): RequireLookupForRecordQuestions had no effect without semantic tool filtering, and the model could make up a list of records. Now, without the filter, the first call of a record question must be a declared read that the user may run, or a handoff.
  • BUG-195 (S2): an agent offered two tools with the same name failed every turn with "Duplicate tool name". Now each agent is offered one tool per name, the one that a call by that name runs, and a warning names the copies.
  • BUG-196 (S2): MentorDeclarativeOptions.ConfigurationSection gave a definition of a specialist in YAML no values. Now the package loads the section itself, as before.
  • BUG-227 (S4): the option tables of the Server README left out ConfirmationMessage, although the 1.1.0 notes said that every option was listed (BUG-163). Now a test checks both READMEs.

This version fixes no security advisory.

Generative cards only from a declared return type

Section titled “Generative cards only from a declared return type”
  • Previous behavior: any tool result with a non-empty string kind was drawn as a generative card.
  • New behavior: a result is read for cards only when its method declares MentorCard, or a sequence of MentorCard, as its return type, directly or through a Task.
  • Reason: a record of yours could be drawn as a card, with a link that someone else wrote as a button, and its other fields never reached the model (BUG-212).
  • What to do: declare the return type of the methods that return cards: see the fix.
  • APIs affected: the methods that return cards as object, as a JsonElement or as a type of your own, with EnableGenerativeCards on.

This version deprecates nothing.

PackageFromToWhy
Microsoft.Agents.AI and Microsoft.Agents.AI.Workflows1.18.01.26.0The Agent Framework 1.26 line. In 1.26, AgentSessionStore.DeleteSessionAsync is abstract.
Microsoft.Agents.AI.Declarative (in Novior.MentorAgent.Declarative)1.18.0-rc11.26.0-rc1The same line.
Microsoft.Extensions.AI10.7.010.11.0It moves with Agent Framework.
ModelContextProtocol, and ModelContextProtocol.AspNetCore in Novior.MentorAgent.Server1.3.02.3.0The MCP endpoint asks for the stateful transport, so it answers as before. A server that lists a tool without inputSchema is no longer connected.
A2A, and A2A.AspNetCore in Novior.MentorAgent.Server1.0.0-preview21.0.0-preview3See A2A preview3 with MentorAgent 1.1.0.

The defects that are still open in 1.2.0, with what to do about each one, are on the Known issues page, together with the prerelease packages and the package versions to pin. The limits that are not defects are in Known limitations.

  1. Upgrade the five packages together. Each package requires the others at exactly its own version.
  2. Check the methods that return cards. A method that returns generative cards must declare MentorCard or IEnumerable<MentorCard> as its return type: see Generative cards need a declared type.
  3. Register ReplyStarted in your hub clients. A JavaScript, React, Angular or Vue client, or a .NET client of the hub, adds connection.on("ReplyStarted", () => {}), or hub.On<MentorReplyStarted>("ReplyStarted", _ => { }). Without it, SignalR logs a warning for every reply.
  4. Decide about the AI notice. The widget now shows it by default. Keep it, write your own text with AiDisclosureMessage, or turn it off with ShowAiDisclosure = false.
  5. Update the links you write by hand. If a page links MentorAgent.css and MentorAgent.js itself, as the index.html of a Blazor WebAssembly client or of a .NET MAUI app does, add ?v=1.2.0 to both links, and change it on every upgrade (BUG-209).
  6. Check Proactive. If you use MentorshipLevel.Proactive and want the suggested next steps, set ProactiveNextSteps = true.
  7. Check your own session store and MCP servers. In Agent Framework 1.26, AgentSessionStore.DeleteSessionAsync is abstract: an AgentSessionStore of your own implements it. An MCP server that lists a tool without inputSchema is no longer connected, and a warning names it.
  8. Remote agents on A2A. A 1.1.0 host that moves A2A or A2A.AspNetCore to 1.0.0-preview3 on its own cannot answer A2A tasks. Upgrade MentorAgent with them.

If you upgrade from 1.0.0, first read Upgrade from 1.0.0 in the 1.1.0 notes: 1.1.0 fixed a vulnerable SignalR dependency of the Blazor client (BUG-173). If you still use the MentorAgent.* packages, start from Move to the Novior.MentorAgent packages.

Every entry of this version is in the 1.2.0 section of CHANGELOG.md (external site), which is also the release notes on NuGet (external site).

  • Release notes — Every stable MentorAgent version, newest first, with its date and what it brings; how versions are numbered and supported, and how dependencies are followed.
  • MentorAgent 1.1.0 release notes — What MentorAgent 1.1.0 adds and fixes: your own confirmation text, rules per method, safer page changes and team approval, and how to upgrade.
  • Known issues — The open defects of the current MentorAgent version and how to avoid them, the prerelease packages it depends on, and the package versions to pin.
  • Known limitations — What MentorAgent does not do by design, as opposed to a defect: platforms, hosted tools, open endpoints, default checks and A2A chains.
  • Troubleshooting — Fix common problems with MentorAgent: startup errors, blocked requests, roles, confirmation, local models. And where to get help or report a bug.