# AiAgentRunView
## Description
BitAiAgentRunView makes a multi-step agent run legible: what it is trying to do, which step it is on, what each step produced and how long it took, what the whole run cost, and what it needs from the user. Every step is visible with its own state and timing, the clock keeps moving while the run does - so a stalled run looks stalled - and a run blocked on the user shows its question with one-tap suggested answers and a field for an answer of the user's own.
## Parameters
| Name | Type | Default Value | Description |
| :--- | :--- | :------------ | :---------- |
| Classes | `BitAiAgentRunViewClassStyles?` | null | Custom CSS classes for different parts of the agent run view. |
| ClockInterval | `TimeSpan` | TimeSpan.FromSeconds(1) | How often the elapsed time is refreshed while the run is active. The clock starts with the run and stops the moment it ends (completed, failed or cancelled), so a finished run costs nothing to keep on screen; a new interval takes effect at once, also while the run goes on. Each tick is one coalesced render of the view. |
| OnRespond | `EventCallback<string>` | | The callback for when the user answers the question the run is blocked on - a suggested answer or one typed into the field (Enter or the Answer button). When set, the answer is handed to the callback alone and the Session is not called, so one answer is never delivered twice; the callback is then what resolves the run (ResolveInput) and carries it on. The answer buttons are disabled while it runs. |
| Run | `BitAiAgentRun` | | The run to render (required). The view subscribes to its Changed event, and re-renders at most once per render interval as it changes, so the run can be moved from any thread without calling StateHasChanged. |
| Session | `BitAiChatSession?` | null | The session the run belongs to. Without an OnRespond, an answer is handed to its RespondToAgentAsync, which resolves the run, appends the answer as a user message and continues the turn. Its conversation also lets each step show the tool calls it made by name and status. |
| Styles | `BitAiAgentRunViewClassStyles?` | null | Custom CSS styles for different parts of the agent run view. |
| AriaLabel | `string?` | null | Gets or sets the accessible label for the component, used by assistive technologies. |
| Class | `string?` | null | Gets or sets the CSS class name(s) to apply to the rendered element. |
| Dir | `BitDir?` | null | Gets or sets the text directionality for the component's content. |
| Disabled | `bool` | false | Gets or sets a value indicating whether the component is disabled and cannot respond to user interaction. |
| ForceAnimation | `bool` | false | Gets or sets a value indicating whether the component's animations play at their full duration even when reduced motion is requested. |
| HtmlAttributes | `Dictionary<string, object>` | new Dictionary<string, object>() | Captures additional HTML attributes to be applied to the rendered element, in addition to the component's parameters. An attribute written on the component wins over an entry of the same name. |
| Id | `string?` | null | Gets or sets the unique identifier for the component's root element. |
| Style | `string?` | null | Gets or sets the CSS style string to apply to the rendered element. |
| TabIndex | `string?` | null | Gets or sets the tab order index for the component when navigating with the keyboard. |
| Visibility | `BitVisibility` | BitVisibility.Visible | Gets or sets the visibility state (visible, hidden, or collapsed) of the component. |
## Public Members
| Name | Type | Default Value | Description |
| :--- | :--- | :------------ | :---------- |
| UniqueId | `Guid` | Guid.NewGuid() | Gets the readonly unique identifier for the component's root element, assigned when the component instance is constructed. |
| RootElement | `ElementReference` | | Gets the reference to the root HTML element associated with this component. |
## Enums
### BitVisibility Enum
| Name | Value | Description |
| :--- | :--- | :---------- |
| Visible | 0 | The content of the component is visible. |
| Hidden | 1 | The content of the component is hidden, but the space it takes on the page remains (visibility:hidden). |
| Collapsed | 2 | The component is hidden (display:none). |
### BitDir Enum
| Name | Value | Description |
| :--- | :--- | :---------- |
| Ltr | 0 | Ltr (left to right) is to be used for languages that are written from the left to the right (like English). |
| Rtl | 1 | Rtl (right to left) is to be used for languages that are written from the right to the left (like Arabic). |
| Auto | 2 | Auto lets the user agent decide. It uses a basic algorithm as it parses the characters inside the element until it finds a character with a strong directionality, then applies that directionality to the whole element. |
## Sub Classes
### BitAiAgentRunViewClassStyles Properties
| Name | Type | Default Value | Description |
| :--- | :--- | :------------ | :---------- |
| Root | `string?` | null | Custom CSS classes/styles for the root element of the BitAiAgentRunView. |
| Header | `string?` | null | Custom CSS classes/styles for the header of the BitAiAgentRunView. |
| HeaderIcon | `string?` | null | Custom CSS classes/styles for the icon of the header of the BitAiAgentRunView. |
| Title | `string?` | null | Custom CSS classes/styles for the title (the name of the agent, or "Plan") of the BitAiAgentRunView. |
| Usage | `BitAiTokenUsageViewClassStyles?` | null | Custom CSS classes/styles for the BitAiTokenUsageView of the header of the BitAiAgentRunView. |
| StatusBadge | `BitAiAgentStatusBadgeClassStyles?` | null | Custom CSS classes/styles for the BitAiAgentStatusBadge of the header of the BitAiAgentRunView. |
| Body | `string?` | null | Custom CSS classes/styles for the body of the BitAiAgentRunView. |
| Goal | `string?` | null | Custom CSS classes/styles for the goal of the BitAiAgentRunView. |
| Progress | `string?` | null | Custom CSS classes/styles for the progress row (the count, the bar and the clock) of the BitAiAgentRunView. |
| Count | `string?` | null | Custom CSS classes/styles for the count of the completed steps of the BitAiAgentRunView. |
| ProgressBar | `BitProgressClassStyles?` | null | Custom CSS classes/styles for the BitProgress of the BitAiAgentRunView. |
| Elapsed | `string?` | null | Custom CSS classes/styles for the elapsed time of the BitAiAgentRunView. |
| Steps | `string?` | null | Custom CSS classes/styles for the list of the steps of the BitAiAgentRunView, which takes the focus once an answer has taken the question away. |
| StepItem | `string?` | null | Custom CSS classes/styles for each item of the list of the steps of the BitAiAgentRunView. |
| Step | `BitAiAgentStepViewClassStyles?` | null | Custom CSS classes/styles for the BitAiAgentStepView of each step of the BitAiAgentRunView. |
| Prompt | `string?` | null | Custom CSS classes/styles for the prompt (the question the run asks the user, its suggested answers and the answer row) of the BitAiAgentRunView. |
| Question | `string?` | null | Custom CSS classes/styles for the text of the question of the BitAiAgentRunView. |
| SuggestionButton | `BitButtonClassStyles?` | null | Custom CSS classes/styles for the BitButton of each suggested answer of the BitAiAgentRunView. |
| Answer | `string?` | null | Custom CSS classes/styles for the answer row (the field and its button) of the BitAiAgentRunView. |
| AnswerField | `BitTextFieldClassStyles?` | null | Custom CSS classes/styles for the BitTextField of the answer of the BitAiAgentRunView. |
| AnswerButton | `BitButtonClassStyles?` | null | Custom CSS classes/styles for the BitButton sending the answer of the BitAiAgentRunView. |
| Error | `string?` | null | Custom CSS classes/styles for the error (the wrapper of the compact BitAiErrorView of a failed run) of the BitAiAgentRunView. |
### BitAiAgentRun Properties
A multi-step agent execution: a plan, the steps as they run, and the aggregate result. A BitAiChatSession builds one from the agent events of its transport and keeps it in Conversation.Runs; an application driving an agent of its own creates one with new BitAiAgentRun(runId, agentName) - both optional, a run id is generated when none is given - and moves it with the methods below. Every change raises Changed. Thread-safe to read while it is moved from another thread.
| Name | Type | Default Value | Description |
| :--- | :--- | :------------ | :---------- |
| RunId | `string` | | The run identifier - the one the agent events address the run by. |
| AgentName | `string?` | null | The name of the agent, the title of the run view. The view reads "Plan" (BitAiStrings.Plan) without one. |
| Goal | `string?` | null | The goal the run is pursuing, shown above the steps. |
| Status | `BitAiAgentRunStatus` | BitAiAgentRunStatus.Idle | Where the run is in its lifecycle. Moved by SetPlan (Idle to Planning), SetStatus, RequestInput (WaitingForInput), ResolveInput (back to Running) and SetError (Failed or Cancelled). |
| Steps | `IReadOnlyList<BitAiAgentStep>` | [] | The steps, in execution order. |
| CurrentStep | `BitAiAgentStep?` | null | The step currently running, else the one blocked on the user, else null. |
| PendingPrompt | `string?` | null | What the agent is waiting for, while the run is blocked on the user. |
| PendingSuggestions | `IReadOnlyList<string>?` | null | The suggested answers to PendingPrompt, rendered as one-tap choices. |
| StartedAt | `DateTimeOffset` | | When the run started (when it was created). |
| CompletedAt | `DateTimeOffset?` | null | When the run ended: set the first time it moves to Completed, Failed or Cancelled. |
| Elapsed | `TimeSpan` | | How long the run took, or has taken so far. |
| Usage | `BitAiTokenUsage` | | The token usage of every model request made within the run, each counted once with the last usage its stream reported. Shown in the header of the view when BitAiUiOptions.ShowUsage is on. |
| Error | `BitAiError?` | null | The failure that ended the run, when it failed. The view shows it under the steps as a compact BitAiErrorView. |
| CompletedStepCount | `int` | 0 | How many steps have finished successfully. |
| Progress | `double?` | null | Completion in the range 0..1 (the completed steps over all of them), or null while there is no plan - in which case the view shows no progress row. |
| Version | `int` | 0 | Increments on every change, for cheap render checks. |
| Changed | `event Action<BitAiAgentRun>?` | | Raised after the run (or any of its steps) changes. |
| SetPlan | `void` | | SetPlan(IReadOnlyList<BitAiAgentPlanStep> steps): replaces the plan. A step whose StepId survives the revision keeps its state and timings (its title and detail are updated); a repeated id counts once. Moves an Idle run to Planning. |
| GetOrAddStep | `BitAiAgentStep` | | GetOrAddStep(string stepId, string? title = null): finds a step, creating it at the end when the agent starts one that was never planned. A title given renames the step. |
| SetStatus | `void` | | SetStatus(BitAiAgentRunStatus status): moves the run to a new status. Completed, Failed and Cancelled end it: CompletedAt is set and any pending question is cleared. Failed and Cancelled also complete every step the run did not finish (pending, running or blocked) as Skipped. |
| RequestInput | `void` | | RequestInput(string prompt, IReadOnlyList<string>? suggestions = null): blocks the run on the user until they answer; the run moves to WaitingForInput, and the steps running with it to Blocked. |
| ResolveInput | `void` | | ResolveInput(): clears the pending question, moves a waiting run back to Running, and starts the blocked steps again (keeping when they first started). |
| AddUsage | `void` | | AddUsage(BitAiTokenUsage usage): adds the final token usage of one request to the run total. |
| SetError | `void` | | SetError(BitAiError error): records the failure that ended the run, and ends it as Failed - or Cancelled, for an error whose Kind is Cancelled. |
### BitAiAgentPlanStep Properties
One entry of an agent plan, handed to BitAiAgentRun.SetPlan or carried by a BitAiAgentPlanEvent: new BitAiAgentPlanStep(stepId, title, detail). A readonly record struct.
| Name | Type | Default Value | Description |
| :--- | :--- | :------------ | :---------- |
| StepId | `string` | | The stable identifier of the step, preserved across plan revisions. |
| Title | `string` | | What the step does. |
| Detail | `string?` | null | An optional elaboration, shown under the title of the step. |
## CSS Variables
Public CSS custom properties read off the component's root. Set one on `:root` to restyle every instance, on any ancestor to restyle the ones inside it, or on the `Style` of one instance to restyle it and every instance nested inside it, which inherits the value too - a parameter changes one alone, as does the real CSS property on its `Style` / `Styles` when that property does not inherit (`opacity`); an inherited one (`color`, `font-family`) reaches the nested instances that do not set it themselves.
| Name | Default Value | Description |
| :--- | :------------ | :---------- |
| `--bit-AiAgentRunView-background` | var(--bit-clr-bg-sec) | Surface of the panel and its header. |
| `--bit-AiAgentRunView-body-background` | var(--bit-clr-bg-pri) | Surface of the body under the header. |
| `--bit-AiAgentRunView-border-color` | var(--bit-clr-brd-sec) | Edge of the panel, and the line under its header. |
| `--bit-AiAgentRunView-border-width` | var(--bit-shp-brd-width) | Width of that edge and line. |
| `--bit-AiAgentRunView-radius` | var(--bit-shp-radius-surface) | Corner of the panel. |
| `--bit-AiAgentRunView-header-color` | var(--bit-clr-fg-sec) | Icon and words of the header. |
| `--bit-AiAgentRunView-text-color` | var(--bit-clr-fg-sec) | The goal, the count of the steps and the clock. |
| `--bit-AiAgentRunView-prompt-background` | var(--bit-clr-wrn-tint) | Surface of the question the run asks the user. |
| `--bit-AiAgentRunView-progress-max-width` | spacing(24) | Width the progress bar stops growing at. |
## Examples
\n**Basic**:
```razor
Start the run
@if (basicRun is not null)
{
}
```
```csharp
private BitAiAgentRun? basicRun;
private bool isBasicRunning;
private async Task StartBasicRun()
{
isBasicRunning = true;
// A new run is Idle; its plan moves it to Planning, and the page moves it on to Running.
var run = new BitAiAgentRun(agentName: "Research agent") { Goal = "Find out why the three studies disagree" };
basicRun = run;
await Task.Delay(1000);
run.SetPlan(
[
new BitAiAgentPlanStep("search", "Search for primary sources"),
new BitAiAgentPlanStep("read", "Read the three most relevant"),
new BitAiAgentPlanStep("reconcile", "Reconcile the disagreements"),
new BitAiAgentPlanStep("write", "Write the summary"),
]);
await Task.Delay(1000);
run.SetStatus(BitAiAgentRunStatus.Running);
string[] summaries =
[
"11 results, 3 look primary",
"Two agree on the mechanism, one does not",
"The disagreement is about scope, not direction",
"Summary written",
];
for (var i = 0; i < run.Steps.Count; i++)
{
run.Steps[i].Start();
await Task.Delay(1200);
run.Steps[i].Complete(summary: summaries[i]);
}
run.SetStatus(BitAiAgentRunStatus.Completed);
isBasicRunning = false;
}
```
\n**Failure**:
```razor
Deploy the release
@if (failingRun is not null)
{
}
```
```csharp
private BitAiAgentRun? failingRun;
private bool isFailingRunning;
private async Task StartFailingRun()
{
isFailingRunning = true;
var run = new BitAiAgentRun(agentName: "Deploy agent") { Goal = "Ship the release" };
failingRun = run;
run.SetPlan(
[
new BitAiAgentPlanStep("build", "Build"),
new BitAiAgentPlanStep("test", "Run the test suite"),
new BitAiAgentPlanStep("deploy", "Deploy"),
]);
run.SetStatus(BitAiAgentRunStatus.Running);
var build = run.Steps[0];
build.Start();
await Task.Delay(1000);
build.Complete(summary: "Build succeeded in 41s");
var test = run.Steps[1];
test.Start();
await Task.Delay(1500);
test.Complete(BitAiAgentStepStatus.Failed, error: new BitAiError("3 of 812 tests failed."));
// Ending the run as failed completes the steps it will no longer reach (Deploy) as Skipped.
run.SetError(new BitAiError("The run stopped before deploying.")
{
Detail = "Checkout.AppliesDiscount\nCheckout.RoundsTax\nCheckout.HandlesRefund",
});
isFailingRunning = false;
}
```
\n**Cancellation**:
```razor
Back up the databases
Stop
@if (cancellableRun is not null)
{
}
```
```csharp
private BitAiAgentRun? cancellableRun;
private CancellationTokenSource? cancelSource;
private async Task StartCancellableRun()
{
using var source = new CancellationTokenSource();
cancelSource = source;
var run = new BitAiAgentRun(agentName: "Backup agent") { Goal = "Back up the three databases" };
cancellableRun = run;
run.SetPlan(
[
new BitAiAgentPlanStep("orders", "Back up the orders database"),
new BitAiAgentPlanStep("customers", "Back up the customers database"),
new BitAiAgentPlanStep("analytics", "Back up the analytics database"),
]);
run.SetStatus(BitAiAgentRunStatus.Running);
try
{
foreach (var step in run.Steps)
{
step.Start();
await Task.Delay(2000, source.Token);
step.Complete(summary: "Backed up");
}
run.SetStatus(BitAiAgentRunStatus.Completed);
}
catch (OperationCanceledException)
{
// Ending the run as cancelled completes the step it was on, and the ones it will no longer reach, as Skipped.
run.SetStatus(BitAiAgentRunStatus.Cancelled);
}
finally
{
cancelSource = null;
}
}
private void StopCancellableRun() => cancelSource?.Cancel();
```
\n**Asking the user**:
```razor
Start the migration
@if (askingRun is not null)
{
}
```
```csharp
private BitAiAgentRun? askingRun;
private bool isAskingRunning;
private async Task StartAskingRun()
{
isAskingRunning = true;
var run = new BitAiAgentRun(agentName: "Migration agent") { Goal = "Move the database to the new schema" };
askingRun = run;
run.SetPlan(
[
new BitAiAgentPlanStep("inspect", "Inspect the current schema"),
new BitAiAgentPlanStep("window", "Choose a downtime window"),
new BitAiAgentPlanStep("migrate", "Run the migration"),
]);
run.SetStatus(BitAiAgentRunStatus.Running);
var inspect = run.Steps[0];
inspect.Start();
await Task.Delay(1000);
inspect.Complete(summary: "14 tables, 2 need rewriting");
var window = run.Steps[1];
window.Start();
window.Block();
run.RequestInput("Two tables need a rewrite, which needs downtime. When should I take it?",
["Tonight after 22:00", "Saturday morning"]);
isAskingRunning = false;
}
private async Task RespondToAskingRun(string answer)
{
var run = askingRun!;
run.ResolveInput();
run.Steps[1].Complete(summary: $"Window agreed: {answer}");
var migrate = run.Steps[2];
migrate.Start();
await Task.Delay(1200);
migrate.Complete(summary: "14 tables migrated");
run.SetStatus(BitAiAgentRunStatus.Completed);
}
```
\n**With a session**:
```razor
@implements IAsyncDisposable
Research
Ask the user
Fail
@if (sessionRun is not null)
{
}
```
```csharp
// AiDemoAgentTransports are the scripted agents of this site (an IBitAiTransport each):
// https://github.com/bitfoundation/bitplatform/blob/develop/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Services/AI/AiDemoAgentTransports.cs
private BitAiChatSession? session;
private BitAiAgentRun? sessionRun;
private async Task StartSessionRun(IBitAiTransport transport)
{
if (session is not null) await session.DisposeAsync();
var current = new BitAiChatSession(transport);
session = current;
sessionRun = null;
// The run appears in the conversation once the transport starts it; from then on the view follows it.
current.StateChanged += () =>
{
var run = current.Conversation.Runs.Values.FirstOrDefault();
if (ReferenceEquals(run, sessionRun) || ReferenceEquals(current, session) is false) return;
sessionRun = run;
InvokeAsync(StateHasChanged);
};
await current.SubmitAsync("Find out why the three studies disagree");
}
public async ValueTask DisposeAsync()
{
if (session is not null) await session.DisposeAsync();
}
```
\n**Usage & clock**:
```razor
Start the run
@if (usageRun is not null)
{
}
```
```csharp
private readonly BitAiUiOptions usageOptions = BitAiUiOptions.Default with { ShowUsage = true };
private BitAiAgentRun? usageRun;
private bool isUsageRunning;
private async Task StartUsageRun()
{
isUsageRunning = true;
var run = new BitAiAgentRun(agentName: "Research agent") { Goal = "Summarize the release notes" };
usageRun = run;
run.SetPlan(
[
new BitAiAgentPlanStep("fetch", "Fetch the release notes"),
new BitAiAgentPlanStep("summarize", "Summarize them"),
]);
run.SetStatus(BitAiAgentRunStatus.Running);
foreach (var step in run.Steps)
{
step.Start();
await Task.Delay(1500);
// One model request per step, priced by the transport.
run.AddUsage(new BitAiTokenUsage { InputTokens = 1_850, OutputTokens = 420, Cost = 0.0119m, Currency = "USD" });
step.Complete();
}
run.SetStatus(BitAiAgentRunStatus.Completed);
isUsageRunning = false;
}
```
\n**Disabled**:
```razor
```
```csharp
private readonly BitAiAgentRun disabledRun = CreateDisabledRun();
private static BitAiAgentRun CreateDisabledRun()
{
var run = new BitAiAgentRun(agentName: "Deploy agent") { Goal = "Ship the release" };
run.SetPlan(
[
new BitAiAgentPlanStep("test", "Run the test suite"),
new BitAiAgentPlanStep("deploy", "Deploy"),
]);
run.SetStatus(BitAiAgentRunStatus.Running);
run.Steps[0].Start();
run.Steps[0].Complete(BitAiAgentStepStatus.Failed, error: new BitAiError("3 of 812 tests failed.")
{
Detail = "Checkout.AppliesDiscount\nCheckout.RoundsTax\nCheckout.HandlesRefund",
});
// The question blocks the step running with it (Deploy).
run.Steps[1].Start();
run.RequestInput("Three tests of the checkout failed. Deploy anyway?", ["Deploy anyway", "Stop here"]);
return run;
}
```
\n**Cascading parameters**:
```razor
Start the run
@if (cascadingRun is not null)
{
}
```
```csharp
private readonly BitAiAgentRunViewParams[] runViewParams =
[
new()
{
ClockInterval = TimeSpan.FromMilliseconds(250),
Styles = new() { Goal = "font-style: italic", Title = "color: var(--bit-clr-pri)" },
}
];
private BitAiAgentRun? cascadingRun;
private bool isCascadingRunning;
private async Task StartCascadingRun()
{
isCascadingRunning = true;
var run = new BitAiAgentRun(agentName: "Writer") { Goal = "Draft the announcement" };
cascadingRun = run;
run.SetPlan(
[
new BitAiAgentPlanStep("outline", "Outline the post"),
new BitAiAgentPlanStep("draft", "Draft it"),
]);
run.SetStatus(BitAiAgentRunStatus.Running);
foreach (var step in run.Steps)
{
step.Start();
await Task.Delay(1200);
step.Complete();
}
run.SetStatus(BitAiAgentRunStatus.Completed);
isCascadingRunning = false;
}
```
\n**Style & Class**:
```razor
Ask again
```
```csharp
private BitAiAgentRun styledRun = CreateStyledRun();
private static BitAiAgentRun CreateStyledRun()
{
var run = new BitAiAgentRun(agentName: "Travel agent") { Goal = "Book the team offsite" };
run.SetPlan(
[
new BitAiAgentPlanStep("dates", "Agree on the dates"),
new BitAiAgentPlanStep("venue", "Book the venue"),
]);
run.SetStatus(BitAiAgentRunStatus.Running);
run.Steps[0].Start();
run.Steps[0].Block();
run.RequestInput("Which week suits the team?", ["The first week of June", "The last week of June"]);
return run;
}
private async Task RespondToStyledRun(string answer)
{
// The run answered, which Ask again may replace while it goes on.
var run = styledRun;
run.ResolveInput();
run.Steps[0].Complete(summary: answer);
var venue = run.Steps[1];
venue.Start();
await Task.Delay(1200);
venue.Complete(summary: "The lakeside hall, for 40");
run.SetStatus(BitAiAgentRunStatus.Completed);
}
private void ResetStyledRun() => styledRun = CreateStyledRun();
```
\n**RTL**:
```razor
دوباره بپرس
```
```csharp
private readonly BitAiUiOptions persianOptions = BitAiUiOptions.Default with { Strings = new PersianAiStrings() };
private BitAiAgentRun rtlRun = CreateRtlRun();
private static BitAiAgentRun CreateRtlRun()
{
var run = new BitAiAgentRun(agentName: "دستیار سفر") { Goal = "رزرو سفر کاری تیم" };
run.SetPlan(
[
new BitAiAgentPlanStep("dates", "توافق بر سر تاریخ"),
new BitAiAgentPlanStep("venue", "رزرو محل"),
]);
run.SetStatus(BitAiAgentRunStatus.Running);
run.Steps[0].Start();
run.Steps[0].Block();
run.RequestInput("کدام هفته برای تیم مناسب است؟", ["هفته اول خرداد", "هفته آخر خرداد"]);
return run;
}
private async Task RespondToRtlRun(string answer)
{
var run = rtlRun;
run.ResolveInput();
run.Steps[0].Complete(summary: answer);
var venue = run.Steps[1];
venue.Start();
await Task.Delay(1200);
venue.Complete(summary: "سالن کنار دریاچه، برای ۴۰ نفر");
run.SetStatus(BitAiAgentRunStatus.Completed);
}
private void ResetRtlRun() => rtlRun = CreateRtlRun();
// The words of the view, in Persian.
private sealed class PersianAiStrings : BitAiStrings
{
public override string Plan => "برنامه";
public override string AgentRunning => "در حال اجرا";
public override string WaitingForYou => "منتظر پاسخ شما";
public override string AgentCompleted => "انجام شد";
public override string AnswerPlaceholder => "پاسخ خود را بنویسید...";
public override string Answer => "پاسخ";
public override string Elapsed => "زمان سپریشده";
public override string ToolCalls => "فراخوانی ابزارها";
public override string StepsCompleted(int done, int total) => $"{done} از {total}";
public override string AgentStepStatus(BitAiAgentStepStatus status) => status switch
{
BitAiAgentStepStatus.Running => "در حال انجام",
BitAiAgentStepStatus.Blocked => "منتظر شما",
BitAiAgentStepStatus.Completed => "انجام شد",
BitAiAgentStepStatus.Failed => "ناموفق",
BitAiAgentStepStatus.Skipped => "کنار گذاشته شد",
_ => "شروع نشده",
};
public override string FormatDuration(TimeSpan duration) => duration.TotalSeconds < 1
? $"{duration.TotalMilliseconds:F0} میلیثانیه"
: $"{duration.TotalSeconds:0} ثانیه";
}
```