Compare commits
1 Commits
agent/rex/
...
fb88eab4d1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb88eab4d1 |
@@ -37,6 +37,33 @@ public class AgentStatusHub : Hub<IAgentStatusClient>
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Broadcasts an agent status update to all connected clients.
|
||||
///
|
||||
/// <para>
|
||||
/// Any connected client (or server-side caller) can invoke this method
|
||||
/// to push a status update to every subscriber. The DTO is converted to
|
||||
/// an <see cref="AgentStatusUpdate"/> record and relayed through the
|
||||
/// <see cref="IAgentStatusClient.AgentStatusChanged"/> callback.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="update">The agent status update DTO to broadcast.</param>
|
||||
public async Task SendStatusUpdate(AgentStatusUpdateDto update)
|
||||
{
|
||||
_logger.LogInformation(
|
||||
"Broadcasting status update for agent {AgentId}: {Status}",
|
||||
update.AgentId, update.Status);
|
||||
|
||||
var agentUpdate = update.ToUpdate();
|
||||
|
||||
// Broadcast to all connected clients
|
||||
await Clients.All.AgentStatusChanged(agentUpdate);
|
||||
|
||||
// Also push to the specific agent's group
|
||||
var agentGroup = AgentGroupName(update.AgentId);
|
||||
await Clients.Group(agentGroup).AgentStatusChanged(agentUpdate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the calling connection to the fleet group.
|
||||
/// Once joined, the client will receive all agent status changes
|
||||
|
||||
@@ -72,6 +72,80 @@ public record TaskProgressUpdate(
|
||||
string? Elapsed
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Data transfer object for broadcasting agent status updates
|
||||
/// to all connected SignalR clients via the hub's SendStatusUpdate method.
|
||||
///
|
||||
/// <para>This DTO provides a mutable, serialization-friendly alternative to
|
||||
/// <see cref="AgentStatusUpdate"/> for callers that construct updates
|
||||
/// from external data sources (e.g., HTTP API payloads).</para>
|
||||
/// </summary>
|
||||
public class AgentStatusUpdateDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Agent identifier, e.g. "otto", "dex", "rex".
|
||||
/// </summary>
|
||||
public string AgentId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable display name, e.g. "Otto", "Dex".
|
||||
/// </summary>
|
||||
public string DisplayName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Role description, e.g. "Orchestrator Agent", "Backend Specialist".
|
||||
/// </summary>
|
||||
public string Role { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Current operational status of the agent as lowercase string:
|
||||
/// "active", "idle", "thinking", "error".
|
||||
/// </summary>
|
||||
public string Status { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Description of the agent's current task, if any.
|
||||
/// </summary>
|
||||
public string? CurrentTask { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Full session key, e.g. "agent:otto:telegram:direct:8787451565".
|
||||
/// </summary>
|
||||
public string SessionKey { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Communication channel, e.g. "telegram", "discord", "slack".
|
||||
/// </summary>
|
||||
public string Channel { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// ISO 8601 timestamp of the agent's last activity.
|
||||
/// </summary>
|
||||
public string LastActivity { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Error message when the agent status is "error".
|
||||
/// </summary>
|
||||
public string? ErrorMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Converts this DTO to an immutable <see cref="AgentStatusUpdate"/> record
|
||||
/// for use with the typed SignalR client interface.
|
||||
/// </summary>
|
||||
/// <returns>An <see cref="AgentStatusUpdate"/> with equivalent field values.</returns>
|
||||
public AgentStatusUpdate ToUpdate() => new(
|
||||
AgentId,
|
||||
DisplayName,
|
||||
Role,
|
||||
Status,
|
||||
CurrentTask,
|
||||
SessionKey,
|
||||
Channel,
|
||||
LastActivity,
|
||||
ErrorMessage
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Snapshot of an agent's full card data, sent on initial connection
|
||||
/// or when the fleet state is requested.
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
// ============================================================================
|
||||
// Hub Page — Responsive AgentCard Grid
|
||||
// Desktop (≥1024px): 2×2 grid
|
||||
// Mobile (<1024px): single-column stack
|
||||
// ============================================================================
|
||||
|
||||
.hub-page {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 16px;
|
||||
padding: var(--cc-section-padding, 16px);
|
||||
min-height: 400px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.hub-page__placeholder {
|
||||
color: var(--cc-on-surface-variant);
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
padding: 24px 0;
|
||||
}
|
||||
|
||||
// Desktop / kiosk breakpoint — 2-column grid
|
||||
@media (min-width: 1024px) {
|
||||
.hub-page {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,18 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
<p class="hub-page__placeholder">Command Hub — Fleet status grid will render here</p>
|
||||
</div>
|
||||
`,
|
||||
styleUrl: './hub-page.component.scss',
|
||||
styles: [`
|
||||
.hub-page {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 400px;
|
||||
}
|
||||
.hub-page__placeholder {
|
||||
color: var(--cc-on-surface-variant);
|
||||
font-size: 16px;
|
||||
}
|
||||
`],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class HubPageComponent {}
|
||||
@@ -48,17 +48,6 @@ html {
|
||||
// These are NOT part of the M3 tonal palette; they are semantic overrides.
|
||||
// ---------------------------------------------------------------------------
|
||||
:root {
|
||||
// --- Tactical Dark Mode color palette (CUB-47) ---
|
||||
--color-surface: #0F172A;
|
||||
--color-surface-light: #1E293B;
|
||||
--color-primary: #38BDF8;
|
||||
--color-secondary: #2DD4BF;
|
||||
--color-accent: #A78BFA;
|
||||
--color-danger: #F87171;
|
||||
--color-text-primary: #FFFFFF;
|
||||
--color-text-secondary: #94A3B8;
|
||||
--color-border: #334155;
|
||||
|
||||
// --- Status colors ---
|
||||
--status-active: #38BDF8;
|
||||
--status-idle: #2DD4BF;
|
||||
@@ -101,7 +90,7 @@ html {
|
||||
// Global Body Styles
|
||||
// ---------------------------------------------------------------------------
|
||||
body {
|
||||
background-color: var(--color-surface);
|
||||
background-color: var(--cc-background);
|
||||
color: var(--cc-on-surface);
|
||||
font-family: 'Inter', 'Roboto', sans-serif;
|
||||
margin: 0;
|
||||
|
||||
Reference in New Issue
Block a user