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"; }