feat: ship agent-first mission control v0.2.57
CI - Build & Test / Backend (.NET) (push) Successful in 42s
CI - Build & Test / Frontend (Vue/TS) (push) Successful in 2m46s
CI - Build & Test / Security Check (push) Successful in 3s
CI - Build & Test / Deploy Nexus (push) Successful in 56s

This commit is contained in:
AzuTear
2026-07-31 22:39:47 +02:00
parent 3bc7622977
commit f5552218bc
535 changed files with 95242 additions and 8791 deletions
@@ -0,0 +1,200 @@
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using Nexus.Api.Models;
namespace Nexus.Api.Services;
public sealed class OpenClawEventProjectionService(IGatewayConnector connector)
: IOpenClawEventProjectionService
{
public OpenClawEventBatch Project(string? lastEventId, int limit = 500)
{
var projectedAt = DateTimeOffset.UtcNow;
var requestedCursor = NormalizeCursor(lastEventId);
var envelopes = connector
.GetRecentEvents(Math.Clamp(limit, 1, 500))
.OrderBy(item => item.ReceivedAt)
.ThenBy(item => item.Sequence)
.ThenBy(OpenClawEventIdentity.Create, StringComparer.Ordinal)
.ToList();
var allEvents = ProjectEvents(envelopes);
var oldestId = allEvents.FirstOrDefault()?.Id;
var latestId = allEvents.LastOrDefault()?.Id;
var replayBoundaryMissed = false;
IReadOnlyList<OpenClawStreamEventDto> selected = allEvents;
if (requestedCursor is not null)
{
var cursorIndex = allEvents.FindIndex(item =>
string.Equals(item.Id, requestedCursor, StringComparison.Ordinal));
if (cursorIndex >= 0)
{
selected = allEvents.Skip(cursorIndex + 1).ToList();
}
else
{
replayBoundaryMissed = true;
}
}
var cursor = selected.LastOrDefault()?.Id
?? (replayBoundaryMissed ? latestId ?? "origin" : requestedCursor);
return new OpenClawEventBatch(
selected,
cursor,
replayBoundaryMissed,
oldestId,
latestId,
projectedAt);
}
public OpenClawStreamEventDto CreateConnectionEvent(string? cursor)
{
var occurredAt = DateTimeOffset.UtcNow;
return new OpenClawStreamEventDto(
NormalizeCursor(cursor) ?? "origin",
"openclaw.connection",
"connection",
"connection",
null,
null,
null,
false,
false,
null,
null,
occurredAt,
new JsonObject
{
["state"] = connector.ConnectionState.ToString().ToLowerInvariant(),
["connected"] = connector.ConnectionState == GatewayConnectionState.Connected,
["gatewayVersion"] = connector.GatewayVersion,
["protocolVersion"] = connector.ProtocolVersion,
["deviceId"] = connector.DeviceId,
["deviceTokenConfigured"] = connector.DeviceTokenConfigured,
["pairingRequired"] = connector.PairingRequired,
["pairingRequestId"] = connector.PairingRequestId,
["lastConnectedAt"] = connector.LastConnectedAt,
["lastEventAt"] = connector.LastEventAt,
["reconnectAttempts"] = connector.ReconnectAttempts,
["message"] = connector.StatusMessage
});
}
public OpenClawStreamEventDto CreateHeartbeatEvent(string? cursor)
{
var occurredAt = DateTimeOffset.UtcNow;
return new OpenClawStreamEventDto(
NormalizeCursor(cursor) ?? "origin",
"openclaw.heartbeat",
"heartbeat",
"heartbeat",
null,
null,
null,
false,
false,
null,
null,
occurredAt,
new JsonObject
{
["connected"] = connector.ConnectionState == GatewayConnectionState.Connected,
["lastEventAt"] = connector.LastEventAt,
["sentAt"] = occurredAt
});
}
private static List<OpenClawStreamEventDto> ProjectEvents(
IReadOnlyList<GatewayEventEnvelope> envelopes)
{
var result = new List<OpenClawStreamEventDto>(envelopes.Count);
long? previousSequence = null;
foreach (var envelope in envelopes)
{
var sequenceGap = envelope.Sequence.HasValue
&& previousSequence.HasValue
&& envelope.Sequence.Value > previousSequence.Value + 1;
var sequenceReset = envelope.Sequence.HasValue
&& previousSequence.HasValue
&& envelope.Sequence.Value < previousSequence.Value;
var missingFrom = sequenceGap ? previousSequence + 1 : null;
var missingTo = sequenceGap ? envelope.Sequence - 1 : null;
var category = Classify(envelope.Event, envelope.Payload);
result.Add(new OpenClawStreamEventDto(
OpenClawEventIdentity.Create(envelope),
$"openclaw.{category}",
envelope.Event,
category,
envelope.Sequence,
envelope.StateVersion,
previousSequence,
sequenceGap,
sequenceReset,
missingFrom,
missingTo,
envelope.ReceivedAt,
OpenClawPayloadSanitizer.Redact(envelope.Payload)));
if (envelope.Sequence.HasValue)
previousSequence = envelope.Sequence;
}
return result;
}
private static string Classify(string eventName, JsonNode? payload)
{
var normalized = eventName.ToLowerInvariant();
if (normalized.Contains("approval", StringComparison.Ordinal))
return "approval";
if (normalized.Contains("artifact", StringComparison.Ordinal))
return "artifact";
if (normalized.Contains("tool", StringComparison.Ordinal))
return "tool";
if (normalized.Contains("session", StringComparison.Ordinal))
return "session";
if (normalized is "chat" or "agent"
|| normalized.Contains("run", StringComparison.Ordinal)
|| payload?["runId"] is not null)
{
return "run";
}
return "gateway";
}
private static string? NormalizeCursor(string? cursor)
{
var trimmed = cursor?.Trim();
return string.IsNullOrWhiteSpace(trimmed)
|| string.Equals(trimmed, "origin", StringComparison.Ordinal)
? null
: trimmed;
}
}
internal static class OpenClawEventIdentity
{
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);
public static string Create(GatewayEventEnvelope envelope)
{
var payload = envelope.Payload?.ToJsonString(JsonOptions) ?? "null";
var fingerprint = string.Join(
"\u001f",
envelope.Event,
envelope.ReceivedAt.UtcDateTime.Ticks,
envelope.Sequence?.ToString() ?? string.Empty,
envelope.StateVersion?.ToString() ?? string.Empty,
payload);
var hash = SHA256.HashData(Encoding.UTF8.GetBytes(fingerprint));
return $"gw-{envelope.ReceivedAt.UtcDateTime.Ticks:x16}-{Convert.ToHexString(hash.AsSpan(0, 8)).ToLowerInvariant()}";
}
}