using Nexus.Api.Integrations; using Nexus.Api.Models; namespace Nexus.Api.Services; public interface IOpenClawChatService { Task SendAsync( string message, string conversationId, string agentId, OpenClawInvocationMetadata invocation, CancellationToken cancellationToken = default); } /// /// The single Iris/agent chat dispatch path. Every message becomes a durable /// Nexus run and crosses the Protocol-v4 chat.send boundary; Nexus never calls /// OpenClaw's OpenAI-compatible /v1/chat/completions endpoint. /// public sealed class OpenClawChatService(IOpenClawRunService runs) : IOpenClawChatService { public async Task SendAsync( string message, string conversationId, string agentId, OpenClawInvocationMetadata invocation, CancellationToken cancellationToken = default) { var normalizedAgent = agentId.Trim().ToLowerInvariant(); var operation = await runs.StartAsync( new StartOpenClawRunRequest( message, normalizedAgent, $"agent:{normalizedAgent}:main", Title: $"Chat with {normalizedAgent}"), invocation, cancellationToken); if (!operation.Ok) { throw new OpenClawChatDispatchException( operation.State, operation.Message, operation.Run.Id); } return new AgentChatResult( "OpenClaw Protocol v4", normalizedAgent, conversationId, operation.Message, operation.Run.Id, operation.State, operation.Operation); } } public sealed class OpenClawChatDispatchException( string state, string message, Guid runId) : InvalidOperationException(message) { public string State { get; } = state; public Guid RunId { get; } = runId; }