namespace Nexus.Api.Data;
///
/// Durable, secret-free proposal for creating one OpenClaw-owned agent.
/// Nexus owns the approval workflow only; the resulting agent remains owned by
/// OpenClaw.
///
public sealed class AgentProposal
{
public Guid Id { get; init; } = Guid.NewGuid();
public required string Source { get; set; }
public required string RequestedName { get; set; }
public required string RequestedAgentId { get; set; }
public string? Role { get; set; }
public string? Description { get; set; }
public string? Model { get; set; }
public string? Emoji { get; set; }
public string? Avatar { get; set; }
public required string Workspace { get; set; }
public required string StandardFilesJson { get; set; }
public required string StandardFilesHash { get; set; }
public string Status { get; set; } = AgentProposalStates.AwaitingApproval;
public required string RequestedBy { get; set; }
public string? ApprovedBy { get; set; }
public string? RejectedBy { get; set; }
public string? RejectionReason { get; set; }
public string? OpenClawAgentId { get; set; }
public string? OpenClawWorkspace { get; set; }
public string? LastErrorCode { get; set; }
public string? LastErrorMessage { get; set; }
public int Revision { get; set; } = 1;
public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow;
public DateTimeOffset UpdatedAt { get; set; } = DateTimeOffset.UtcNow;
public DateTimeOffset? ApprovedAt { get; set; }
public DateTimeOffset? RejectedAt { get; set; }
public DateTimeOffset? CompletedAt { get; set; }
public ICollection ProvisionRequests { get; set; } =
new List();
}
///
/// One explicitly authorized provisioning attempt. A request that reached the
/// dispatch boundary is never silently re-queued after a restart.
///
public sealed class AgentProvisionRequest
{
public Guid Id { get; init; } = Guid.NewGuid();
public Guid ProposalId { get; set; }
public AgentProposal Proposal { get; set; } = null!;
public int Attempt { get; set; }
public required string Stage { get; set; }
public string Status { get; set; } = AgentProvisionRequestStates.Queued;
public required string IdempotencyKeyHash { get; set; }
public required string Actor { get; set; }
public required string CorrelationId { get; set; }
public string? TraceParent { get; set; }
public string? OpenClawAgentId { get; set; }
public string? LastErrorCode { get; set; }
public string? LastErrorMessage { get; set; }
public string? LeaseOwner { get; set; }
public DateTimeOffset? LeaseUntil { get; set; }
public int Revision { get; set; } = 1;
public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow;
public DateTimeOffset UpdatedAt { get; set; } = DateTimeOffset.UtcNow;
public DateTimeOffset? DispatchStartedAt { get; set; }
public DateTimeOffset? CompletedAt { get; set; }
}
///
/// Hash-only idempotency claim. Raw idempotency keys and request content are
/// deliberately not persisted.
///
public sealed class OperationClaim
{
public Guid Id { get; init; } = Guid.NewGuid();
public required string Operation { get; set; }
public required string IdempotencyKeyHash { get; set; }
public required string RequestHash { get; set; }
public Guid? ResourceId { get; set; }
public required string State { get; set; }
public string? ResultCode { get; set; }
public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow;
public DateTimeOffset? CompletedAt { get; set; }
public DateTimeOffset ExpiresAt { get; set; } =
DateTimeOffset.UtcNow.AddDays(7);
}
///
/// Transactional, content-minimized domain event. The payload may contain
/// identifiers and states, but never proposal markdown or credentials.
///
public sealed class OutboxEvent
{
public long Sequence { get; init; }
public Guid EventId { get; init; } = Guid.NewGuid();
public required string Type { get; set; }
public required string AggregateType { get; set; }
public required string AggregateId { get; set; }
public int AggregateRevision { get; set; }
public required string PayloadJson { get; set; }
public DateTimeOffset OccurredAt { get; set; } = DateTimeOffset.UtcNow;
public DateTimeOffset? PublishedAt { get; set; }
public int PublishAttempts { get; set; }
public string? LastErrorCode { get; set; }
}
public static class AgentProposalStates
{
public const string Draft = "draft";
public const string AwaitingApproval = "awaiting_approval";
public const string Provisioning = "provisioning";
public const string Ready = "ready";
public const string Partial = "partial";
public const string Failed = "failed";
public const string InDoubt = "in_doubt";
public const string Rejected = "rejected";
}
public static class AgentProvisionStages
{
public const string CreateAgent = "create_agent";
public const string ReconcileAgent = "reconcile_agent";
public const string FinalizeFiles = "finalize_files";
}
public static class AgentProvisionRequestStates
{
public const string Queued = "queued";
public const string Dispatching = "dispatching";
public const string Completed = "completed";
public const string Failed = "failed";
public const string Partial = "partial";
public const string InDoubt = "in_doubt";
public const string Blocked = "blocked";
}