using System.Text.Json; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.EntityFrameworkCore; using TeamUp.Modules.Assembler.Domain; using TeamUp.Modules.Assembler.Persistence; using TeamUp.Modules.Assembler.Queue; using TeamUp.Modules.Assembler.Runtime; using TeamUp.SharedKernel.Modularity; namespace TeamUp.Modules.Assembler.Endpoints; internal static class AssemblerEndpoints { public static void Map(IEndpointRouteBuilder endpoints) { var group = endpoints.MapGroup("/api/assembler").WithTags("Assembler"); group.MapGet("/ping", () => TypedResults.Ok(new ModulePing("assembler"))); group.MapPost("/runs", CreateRun).RequireAuthorization(); group.MapGet("/runs/{id:guid}", GetRun).RequireAuthorization(); } // Dispatch a task to an AI seat: record a queued AgentRun and enqueue the job. The worker // drains it off the request path. (Scope-checking the seat's team is added in Increment 2.) private static async Task CreateRun( CreateRunRequest request, AssemblerDbContext db, JobQueue queue, TimeProvider clock, CancellationToken ct) { var run = new AgentRun(request.SeatId, request.WorkItemId, clock.GetUtcNow()); db.AgentRuns.Add(run); await db.SaveChangesAsync(ct); await queue.EnqueueAsync("agent.run", JsonSerializer.Serialize(new AgentRunPayload(run.Id)), ct); return Results.Ok(ToResponse(run)); } private static async Task GetRun(Guid id, AssemblerDbContext db, CancellationToken ct) { var run = await db.AgentRuns.FirstOrDefaultAsync(r => r.Id == id, ct); return run is null ? Results.NotFound() : Results.Ok(ToResponse(run)); } private static RunResponse ToResponse(AgentRun run) => new( run.Id, run.SeatId, run.WorkItemId, run.AgentId, run.Status.ToString(), run.ActionType, run.ActionRisk, run.Prompt, run.Output, run.Error); }