The sidecar's fire-and-forget event handler pattern
(invokeRunTurnHandler) causes events to arrive out of order:
message-complete and status:idle can arrive before all
message-delta events have been emitted. Additionally, deltas
from multiple messages (thinking, response, sub-agent) were
naively concatenated into a single string, producing garbled
output.
Replace the single-string accumulation with a proper message
tracking model (Map<messageId, TrackedMessage>) that mirrors the
main app's SessionRecord.messages approach:
- Track each message independently by messageId
- Use message-complete event's content field as the authoritative
final text, replacing any garbled intermediate streaming state
- Mark messages as finalized on message-complete to skip late
arriving deltas that would corrupt the final content
- Derive display from the last non-thinking message's content
- Only transition to 'complete' when all tracked messages are
not-pending AND at least one non-thinking message has content,
preventing premature completion from setup/init events
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The session emits completion signals (agent-activity:completed,
message-complete, status:idle) during initialization before any
response content arrives. These early events were incorrectly
transitioning the popup to 'complete' phase, causing the
Nothing → In Progress → Nothing cycling pattern.
Track whether response content has been received via a ref and
only transition to 'complete' when hasContentRef is true. This
ensures setup/init completion events are ignored and the popup
stays in streaming until actual content has arrived and a
terminal event follows.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The popup stayed in 'streaming' phase because it only transitioned
to 'complete' on message-complete or status:idle when prev was
exactly 'streaming'. If other events (agent-activity, run-updated)
arrived between the last delta and idle, the phase guard could miss.
Broaden completion detection:
- status:idle now completes from any non-idle/non-complete phase
- agent-activity:completed also triggers completion
- Covers all paths the sidecar uses to signal turn end
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The popup window has its own document and never received the
data-theme attribute. Pass the current theme setting from the main
process via the show event, resolve 'system' to effective dark/light,
and apply to document.documentElement.dataset.theme so all CSS
variables match the main app's appearance.
- Add getCurrentTheme() to AryxAppService
- Pass theme in toggleQuickPromptWindow / showQuickPromptWindow
- Update onShow listener signature to receive theme string
- Apply theme to documentElement on each popup activation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When no explicit default model is configured in settings, fall back
to the first available model so the input bar always displays a
selected model name instead of 'Select model'. Also always reset
model/reasoning to defaults on each new prompt activation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Rewrite ModelSelector to group models by AI provider (OpenAI,
Anthropic, Google) using providerMeta and ProviderIcon instead of
grouping by tier; show tier as compact badge per model row
- Remove all box-shadow from qp-panel and border state variants;
on Windows transparent BrowserWindows, shadows paint on the
transparent canvas creating a visible dark rectangle behind the
rounded panel
- Add transparent background to html element in quickprompt.html
and CSS override via :has(body[data-quickprompt]) to ensure no
ancestor paints a visible background
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Replace translucent glass background with solid surface-1 color;
backdrop-filter does not work with transparent BrowserWindows on
Windows, leaving the panel nearly invisible
- Add data-quickprompt body attribute to quickprompt.html and CSS
overrides to ensure body/root are truly transparent, eliminating
the visible rectangle behind the rounded panel
- Remove overflow-hidden from qp-panel so model selector dropdown
is no longer clipped; the dropdown was positioned absolute and
rendered entirely outside the panel bounds
- Change ModelSelector to open downward (top-full) instead of upward
(bottom-full) for natural command-bar flow
- Update dropdown animation direction to match downward opening
- Increase blur handler delay to 200ms for safer dropdown interactions
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix window lifecycle: await renderer load before showing, debounce
blur handler to prevent dropdown-induced dismiss
- Increase window height from 72px to 520px for response display
- Rewrite ModelSelector with tier-grouped upward-opening dropdown,
reasoning effort controls, and Brain icon for reasoning models
- Rewrite QuickPromptInput with visible model trigger, animated
chevron, auto-resize textarea, and proper stop button
- Improve QuickPromptResponse with compact thinking block, markdown
code/pre/link styling, and refined spacing
- Polish QuickPromptActions with consistent sizing and hover states
- Redesign settings QuickPromptSettingsSection: replace flat radio
list with compact grouped dropdown selector, only show reasoning
effort when selected model supports it, combine hotkey toggle
into single row
- Add markdown code block, inline code, and link CSS styles
- Fix dropdown animation direction for upward-opening selector
- Handle message-complete event in QuickPromptApp
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Change default hotkey from Super+Shift+A to Alt+Shift+A — Win key
combinations are frequently reserved by the OS and fail to register
- Make quick prompt window creation lazy (first hotkey press) so it
cannot interfere with main window startup
- Track failed accelerators to prevent error log spam from repeated
workspace-updated events
- Fix process.platform crash in renderer SettingsPanel — use isMac
from @renderer/lib/platform instead (renderer has no node globals)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a system-wide global hotkey (Win+Shift+A / Cmd+Shift+A) that summons
a floating, frameless popup window for quick AI interactions from any app.
Main process:
- GlobalHotkeyService for registering/unregistering system-wide shortcuts
- Frameless, transparent, always-on-top BrowserWindow factory
- IPC handlers for send, discard, close, continue-in-aryx, cancel
- Session event routing from sidecar to quick prompt window
- Settings persistence for default model, hotkey, and reasoning effort
Renderer (separate lightweight entry):
- QuickPromptApp with session state, streaming, keyboard shortcuts
- QuickPromptInput with model selector trigger and cancel support
- QuickPromptResponse with streamed markdown and thinking blocks
- QuickPromptActions (Discard / Close / Continue in Aryx)
- ModelSelector dropdown with tier badges and reasoning effort
- Glass Command Bar aesthetic with animated gradient border
Settings:
- Quick Prompt section in SettingsPanel with enable toggle, hotkey
display, default model selector, and reasoning effort picker
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Renderer code splitting:
- Lazy-load 15 components with React.lazy() and Suspense (ChatPane,
ActivityPanel, SettingsPanel, TerminalPanel, WelcomePane, BottomPanel,
GitPanel, CommandPalette, CommitComposer, WorkflowPicker, BookmarksPanel,
SessionSearchPanel, KeyboardShortcutsPanel, DiscoveredToolingModal,
ProjectSettingsPanel)
- This moves ~1.2 MB of optional dependencies (Lexical, @xyflow/react,
@xterm/xterm, highlight.js, motion) out of the critical bundle
- Critical bundle reduced from ~1.5 MB to ~759 KB
- Defer JetBrains Mono font load to TerminalPanel (saves ~80 KB at startup)
Main process optimizations:
- Use show: false + ready-to-show on BrowserWindow to eliminate blank flash
- Decouple workspace loading from bootstrap — no longer blocks window, tray,
and auto-update setup
- Defer sidecar-dependent approval tool pruning to run in background after
workspace is returned to renderer (removes sidecar spawn from critical path)
- Parallelize independent workspace sync operations (user tooling, project
tooling, project customization) with Promise.all()
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Deep agent resolution in the sidecar now walks sub-workflow nodes so
nested agents carry subworkflowNodeId and subworkflowName on activity
events. New subworkflow-started / subworkflow-completed activity types
let the frontend track sub-workflow lifecycle.
The Activity panel groups nested agents under collapsible sub-workflow
cards with status badges, accent-colored left borders, and smooth
expand/collapse transitions. Cards auto-expand when a sub-workflow
starts running. Workflows without sub-workflow nodes render identically
to before.
Extracted AgentRow, SubWorkflowGroup, and shared accent constants to
a new components/activity/ feature directory. Added
resolveWorkflowAgentHierarchy and buildGroupedActivityRows for
hierarchical activity grouping with dynamic fallback for unresolved
sub-workflow agents.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add toolArguments to normalizeRunTimelineEvent output so persisted
sessions retain tool argument data across app restarts
- Add toolArguments field to AgentActivityState and propagate it in
applySessionEventActivity for real-time activity labels
- Add tests for normalization round-trip with and without toolArguments
- Add test for activity state including toolArguments
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When toolArguments doesn't contain the expected key (e.g. 'path' for
view), fall back to the first usable string value from any argument
key instead of showing misleading placeholders like 'Viewed a file'.
When no arguments exist at all, show just the tool name.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the flat, repetitive 'Agent used X' activity list with a
structured narrative timeline:
- Group consecutive same-tool calls into collapsible rows with context
(e.g. 'Viewed 4 files' with file names listed below)
- Use verb-based labels with arguments: 'Viewed ChatPane.tsx:148-250',
'Searched for tool-call', 'Edited runTimeline.ts'
- Promote report_intent events to phase dividers that segment the
timeline into labeled stages of work
- Show latest intent text in the collapsed header summary
- Use category-specific icons (Eye, Search, Pencil, Terminal, etc.)
instead of the universal wrench
- Render thinking steps as quoted blocks with 'N more' toggle for
consecutive groups
All data was already available in RunTimelineEventRecord.toolArguments;
this change surfaces it prominently instead of hiding it behind
expandable detail panels.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Thread toolArguments from the sidecar through shared contracts, main-process pipeline, and into the renderer. Add ToolCallDetailPanel with inline summaries (command, path, pattern, etc.) and expandable argument details for every tool-call event in the activity timeline.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
In sequential workflows, multiple collapsible turn-activity panels shared
the same SessionRunRecord and displayed identical aggregate metrics (tool
calls, approvals, etc.) instead of per-agent counts.
Root cause: ChatPane grouped thinking messages correctly per agent, but
passed the full unfiltered run object to every TurnActivityPanel, so
summarizeActivity() counted all events from the entire run for each panel.
Changes:
- Extract filterEventsByAgent() and summarizeActivity() to
runTimelineFormatting.ts as pure, testable helpers
- Add agentNames (derived from thinking message authors) and isLastRunPanel
to the DisplayItem turn-activity variant in ChatPane
- TurnActivityPanel now filters run.events through filterEventsByAgent()
so each panel only counts events belonging to its agents
- Git summary / discard actions only render on the last panel of a run
- Per-agent timing derived from scoped events instead of run-level start
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
For builder-based orchestration modes (group-chat, handoff), the loop edge
maxIterations, workflow settings.maxIterations, and mode-specific settings
(e.g. groupChat.maxRounds) were independently editable but only the mode
settings controlled runtime behavior. This caused confusion when changing
one did not update the others.
Changes:
- Add syncBuilderModeEdgeIterations() to keep loop edge maxIterations in
sync with the authoritative mode settings for builder-based modes
- Wire sync into normalizeWorkflowDefinition so edges are consistent on
load/normalization
- Update scaffoldGraphForMode to accept optional settings instead of
hardcoded iteration values (4 for handoff, 5 for group-chat)
- Sync settings.maxIterations <-> groupChat.maxRounds bidirectionally
when either changes in the UI
- Make loop edge controls read-only in the graph inspector for
builder-based modes with explanatory text
- Add 8 consistency tests validating built-in workflows, scaffold
behavior, and normalization sync
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The optimistic fold previously required prior thinking messages in the
pending group (pendingThinking.length > 0) before it would absorb an
unclassified pending assistant message. When the first assistant message
in a turn arrived before any thinking messages were classified, it
briefly rendered as a full chat message, then snapped into the
collapsible panel once the message-reclassified event arrived.
Remove the pendingThinking.length guard so any trailing pending,
unclassified assistant message during an active turn is folded into
the turn-activity panel immediately, regardless of whether prior
thinking messages exist.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The panel was only emitted as a DisplayItem when classified thinking
messages existed. During most of the turn, thinking messages haven't
been reclassified yet, so the panel appeared late with all activities
already populated.
Two fixes:
- After the message loop, inject a turn-activity item for any active
run that wasn't already attached to a thinking group. This makes the
panel appear as soon as the run record exists.
- Remove the guard that required run events to exist before rendering.
The panel now shows in its 'Working' state immediately when the run
starts, and populates progressively as events arrive.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the separate RunTimeline in the ActivityPanel with a generalized
TurnActivityPanel that renders inline in the chat pane between user
messages and assistant responses.
The new component:
- Shows all turn activities (thinking steps, tool calls, approvals,
handoffs, run status) in a single collapsible panel
- Merges chat thinking messages with run timeline events into a
chronological activity stream
- Auto-expands when a turn starts, auto-collapses on completion
- Displays a compact summary header (elapsed time, tool call count,
handoff count, etc.) when collapsed
- Includes post-run git change summary with discard/commit actions
- Uses subtle entrance animations for rows
Removed components:
- ThinkingProcess.tsx (subsumed by TurnActivityPanel)
- RunTimeline.tsx (no longer needed as a standalone component)
The ActivityPanel retains its agents, session usage, and turn events
(diagnostics/hooks) sections.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove the OpenTelemetry and Filter Sensitive Data toggles from the
workflow settings panel. The backend does not consume these settings,
so exposing them is misleading. The domain model and DTOs are retained
so existing workflow definitions remain valid.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add inline descriptions to workflow settings, orchestration mode,
edge inspector, node inspectors, and condition editor properties
so users understand what each option does at a glance.
- Execution Mode: explains off-thread vs lockstep behavior
- Max Iterations: clarifies it prevents runaway loops
- State Scopes: describes shared state containers
- Edge Kind: explains direct, fan-out, fan-in semantics
- Loop Edge: describes iterative cycle behavior
- Reasoning: explains effort vs answer quality tradeoff
- Triage Agent, Tool-Call Filtering, Max Rounds, etc.
- Result Variable, Require Approval, Port ID, Condition
Also enhances FormField component with optional description prop.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Show agent activity label in chat header (Thinking…, Using bash…, etc.)
instead of an anonymous pulsing dot
- Fix ThinkingProcess elapsed timer to tick live every second using
useElapsedTimer hook instead of stale useMemo computation
- Show completed/failed subagents for 3s grace period with fade-out
transition instead of instantly hiding them
- Transition activity labels to Completed state for 1.5s grace period
on session idle instead of abrupt removal
- Eliminate message flash during thinking reclassification by folding
trailing pending assistant messages into the thinking group
optimistically when they follow existing thinking messages
- Stabilize ThinkingProcess isActive flag by checking for pending
messages in the group rather than relying solely on array position
- Skip rendering empty pending messages inside ThinkingProcess steps
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Scratchpad sessions were hardcoded to only use single-mode workflows.
The handleCreateScratchpad handler filtered for orchestrationMode === 'single'
and auto-selected the first match without any user choice.
Now clicking 'New Scratchpad' opens the WorkflowPicker (same as regular
sessions), letting users choose from all available workflows including
sequential, concurrent, handoff, and group-chat. When only one workflow
exists, it still auto-creates for a seamless single-click experience.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When creating a new session with >1 workflow available, a searchable
popover appears letting users choose which workflow to use. Workflows
are grouped by orchestration mode with mode badges, agent counts,
and favorite indicators. Keyboard navigation (↑↓ Enter Escape) and
fuzzy search are supported.
If only one workflow exists, session creation works immediately
without showing the picker. Scratchpad auto-selects as before.
Wired into all session creation paths:
- Sidebar 'New Session' button
- Ctrl/Cmd+N keyboard shortcut
- Command palette 'New Session' action
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The center column was a flex column with the canvas as flex-1 min-h-0
and the settings panel as shrink-0. When settings content grew tall
(especially with the orchestration mode panel expanded), the canvas
collapsed to zero height.
Switch the center column to a scrollable container with the canvas
having a guaranteed minimum height via clamp(360px, 50vh, 100%).
Settings flow naturally below and the whole column scrolls.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Delete src/shared/domain/pattern.ts and all pattern-specific renderer
components (PatternEditor, NewSessionModal, pattern-graph directory,
patternGraph lib) and their tests
- Migrate all renderer imports from @shared/domain/pattern to
@shared/domain/workflow (ReasoningEffort, reasoningEffortOptions,
WorkflowOrchestrationMode, AgentNodeConfig, WorkflowDefinition)
- Update App.tsx: remove workflowToPattern bridge, createDraftPattern,
NewSessionModal; sessions now created directly with default workflow
- Update ChatPane, ActivityPanel, Sidebar to accept WorkflowDefinition
instead of PatternDefinition and derive agents/mode from workflow
- Update SettingsPanel: remove PatternsSection nav item, pattern editing
state, and pattern-related props; update text references
- Update RunTimeline and modeAccent/modeVisuals records to use
WorkflowOrchestrationMode (drop magentic mode entry)
- Update sessionActivity.ts to use generic agent interface
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove the invented code-executor node type entirely. Rename
function-executor to invoke-function to match Agent Framework's
InvokeFunctionTool declarative action. Update InvokeFunctionConfig to
use functionName, arguments, requireApproval, and resultVariable
properties matching the upstream schema.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Prevents users from adding duplicate start or end nodes, which made the
workflow invalid with no way to recover since those node kinds are
protected from deletion.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Create CodeExecutorInspector with implementation directive editor,
input/output type fields, and directive help callout
- Create FunctionExecutorInspector with built-in function selector
and key-value parameter editor
- Create RequestPortInspector with port ID, request/response type
fields, prompt editor, and info callout
- Wire all three inspectors into WorkflowGraphInspector routing,
keeping PlaceholderNodeInspector as fallback for unknown kinds
- Enhance WorkflowSettingsPanel with OpenTelemetry and sensitive
data filtering toggles, and a state scopes editor with initial
values support
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Align settings terminology with Agent Framework docs where 'Workflows'
is the top-level concept. 'Orchestration' now only appears as
'Orchestration Mode' in the pattern editor, correctly referring to the
specific mode (sequential, concurrent, etc.) within a workflow.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove the restriction that prevented users from deleting built-in
patterns (those with IDs prefixed 'pattern-'). Built-in patterns are
intended as starting points, not permanent fixtures.
Changes:
- Add deletedBuiltinPatternIds field to WorkspaceState to track which
built-in patterns the user has removed
- Update mergePatterns to skip deleted built-ins during workspace load,
preventing them from being re-added
- Remove the isBuiltinPattern guard from AryxAppService.deletePattern;
when a built-in is deleted, its ID is recorded in the tracking list
- Enable the delete button in SettingsPanel and PatternEditor for all
patterns (keep the 'Built-in pattern' label for context)
- Add 3 tests covering built-in deletion, custom deletion, and
selectedPatternId fallback
Users can still restore deleted built-ins via workspace reset.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Introduce a workspace-level agent library that allows users to define
agents once and reference them from multiple orchestration patterns.
Domain model:
- Add WorkspaceAgentDefinition type with name, model, instructions, etc.
- Extend PatternAgentDefinition with optional workspaceAgentId and overrides
- Add resolution helpers that merge workspace agent base with per-pattern overrides
- Extend WorkspaceSettings with agents array and normalize on load
IPC & main process:
- Add saveWorkspaceAgent/deleteWorkspaceAgent IPC channels and handlers
- Resolve workspace agent references in buildEffectivePattern before
sending to sidecar (no C# changes needed)
Settings UI:
- Add 'Agents' tab under Orchestration in the Settings panel
- Create WorkspaceAgentEditor component using ToolingEditorShell
- Show usage count (which patterns reference each agent)
Pattern editor integration:
- Add agent picker dropdown: 'New inline agent' or 'From library'
- Show linked badge (chain icon) on referenced agent graph nodes
- Show linked workspace agent banner in the inspector
- Add 'Save to Agent Library' action to promote inline agents
- Add 'Unlink' action to convert referenced agents back to inline
Tests:
- Add unit tests for resolution helpers (resolvePatternAgent,
resolvePatternAgents, findWorkspaceAgentUsages, normalize)
- Update existing tooling test for new agents field
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When a prompt file overrides the turn model, the original agent's
reasoning effort was carried through even when the target model does
not support it. This caused session.create failures for models like
Claude Opus 4.5 which reject reasoning effort configuration.
Now: if the target model's reasoning capabilities are unknown
(supportedReasoningEfforts undefined) or it's an unresolved model
reference, reasoning effort is stripped entirely.
Also adds a model override badge on assistant messages when the run
used a different model than the session's primary agent. The badge
reads the actual model from the run record (SessionRunRecord.agents)
rather than inferring from the prompt invocation metadata.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Surface prompt model and argumentHint metadata across the UI:
- InlinePromptPill: send model in promptInvocation, show model badge
in dropdown, show argumentHint preview, support armed-prompt state
for prompts with argumentHint (prompt stays pinned, hint becomes
composer placeholder, user types additional context before sending)
- ChatPane: armed-prompt state management, argumentHint-driven
placeholder, send button enabled when prompt is armed, clear armed
state on session change
- PromptInvocationChrome: model badge in transcript, parent-repo badge
and shortened source path display for ancestor-relative paths
- ProjectSettingsPanel: model and argumentHint on prompt cards,
parent-repo badge on instructions and prompts with ancestor paths,
.claude/rules mentioned in instructions description and empty state
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Update InlinePromptPill to build ProjectPromptInvocation from
selected prompt file and send it via onSubmit instead of pasting
raw resolved text as chat content
- Thread promptInvocation through ChatPane.onSend and App.tsx to
the sendSessionMessage IPC call
- Add PromptInvocationChrome component for user messages that were
triggered by a prompt file, showing prompt name, agent badge,
tool count, source path, and expandable resolved body
- Surface prompt agent and tools metadata in InlinePromptPill
dropdown list items and ProjectSettingsPanel PromptCard
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Show application mode badges (always/file/task/manual) on instruction cards
- Display instruction name, description, and applyTo glob when present
- Update section descriptions to reflect recursive scanning paths
- Update empty states to list all discovery locations
- Add InstructionModeBadge component for visual mode indicators
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Surface workflow-diagnostic session events (warnings and errors from
Agent Framework workflows) in the turn-event log of the Activity Panel.
- Add workflow-diagnostic case to formatTurnEventEntry with label/detail
formatting that includes executor ID, subworkflow ID, exception type,
and diagnostic message when present
- Add AlertTriangle icon for diagnostic events with error/warning color
- Add 5 tests covering executor-failed, workflow-warning, subworkflow-error,
workflow-error, and missing diagnosticKind fallback
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>