partly or unimplemented features - now implemented

This commit is contained in:
master
2026-02-09 08:53:51 +02:00
parent 1bf6bbf395
commit 4bdc298ec1
674 changed files with 90194 additions and 2271 deletions

View File

@@ -0,0 +1,137 @@
using StellaOps.AdvisoryAI.Explanation;
using System.ComponentModel.DataAnnotations;
namespace StellaOps.AdvisoryAI.WebService.Contracts;
/// <summary>
/// API request for Codex/Zastava companion explanation generation.
/// </summary>
public sealed record CompanionExplainRequest
{
[Required]
public required string FindingId { get; init; }
[Required]
public required string ArtifactDigest { get; init; }
[Required]
public required string Scope { get; init; }
[Required]
public required string ScopeId { get; init; }
public string ExplanationType { get; init; } = "full";
[Required]
public required string VulnerabilityId { get; init; }
public string? ComponentPurl { get; init; }
public bool PlainLanguage { get; init; }
public int MaxLength { get; init; }
public string? CorrelationId { get; init; }
public IReadOnlyList<CompanionRuntimeSignalRequest> RuntimeSignals { get; init; } = Array.Empty<CompanionRuntimeSignalRequest>();
public CodexCompanionRequest ToDomain()
{
if (!Enum.TryParse<ExplanationType>(ExplanationType, ignoreCase: true, out var parsedType))
{
parsedType = StellaOps.AdvisoryAI.Explanation.ExplanationType.Full;
}
return new CodexCompanionRequest
{
ExplanationRequest = new ExplanationRequest
{
FindingId = FindingId,
ArtifactDigest = ArtifactDigest,
Scope = Scope,
ScopeId = ScopeId,
ExplanationType = parsedType,
VulnerabilityId = VulnerabilityId,
ComponentPurl = ComponentPurl,
PlainLanguage = PlainLanguage,
MaxLength = MaxLength,
CorrelationId = CorrelationId,
},
RuntimeSignals = RuntimeSignals.Select(static signal => new CompanionRuntimeSignal
{
Source = signal.Source,
Signal = signal.Signal,
Value = signal.Value,
Path = signal.Path,
Confidence = signal.Confidence,
}).ToArray(),
};
}
}
/// <summary>
/// Runtime signal request payload.
/// </summary>
public sealed record CompanionRuntimeSignalRequest
{
[Required]
public required string Source { get; init; }
[Required]
public required string Signal { get; init; }
[Required]
public required string Value { get; init; }
public string? Path { get; init; }
public double Confidence { get; init; }
}
/// <summary>
/// API response for Codex/Zastava companion explanation generation.
/// </summary>
public sealed record CompanionExplainResponse
{
public required string CompanionId { get; init; }
public required string CompanionHash { get; init; }
public required ExplainResponse Explanation { get; init; }
public required ExplainSummaryResponse CompanionSummary { get; init; }
public required IReadOnlyList<CompanionRuntimeSignalResponse> RuntimeHighlights { get; init; }
public static CompanionExplainResponse FromDomain(CodexCompanionResponse response)
{
return new CompanionExplainResponse
{
CompanionId = response.CompanionId,
CompanionHash = response.CompanionHash,
Explanation = ExplainResponse.FromDomain(response.Explanation),
CompanionSummary = new ExplainSummaryResponse
{
Line1 = response.CompanionSummary.Line1,
Line2 = response.CompanionSummary.Line2,
Line3 = response.CompanionSummary.Line3,
},
RuntimeHighlights = response.RuntimeHighlights.Select(static signal => new CompanionRuntimeSignalResponse
{
Source = signal.Source,
Signal = signal.Signal,
Value = signal.Value,
Path = signal.Path,
Confidence = signal.Confidence,
}).ToArray(),
};
}
}
/// <summary>
/// Runtime signal response payload.
/// </summary>
public sealed record CompanionRuntimeSignalResponse
{
public required string Source { get; init; }
public required string Signal { get; init; }
public required string Value { get; init; }
public string? Path { get; init; }
public double Confidence { get; init; }
}

View File

@@ -42,6 +42,7 @@ builder.Configuration
builder.Services.AddAdvisoryAiCore(builder.Configuration);
builder.Services.AddAdvisoryChat(builder.Configuration);
builder.Services.TryAddSingleton<ICodexCompanionService, CodexZastavaCompanionService>();
// Authorization service
builder.Services.AddSingleton<StellaOps.AdvisoryAI.WebService.Services.IAuthorizationService, StellaOps.AdvisoryAI.WebService.Services.HeaderBasedAuthorizationService>();
@@ -140,6 +141,9 @@ app.MapPost("/v1/advisory-ai/explain", HandleExplain)
app.MapGet("/v1/advisory-ai/explain/{explanationId}/replay", HandleExplanationReplay)
.RequireRateLimiting("advisory-ai");
app.MapPost("/v1/advisory-ai/companion/explain", HandleCompanionExplain)
.RequireRateLimiting("advisory-ai");
// Remediation endpoints (SPRINT_20251226_016_AI_remedy_autopilot)
app.MapPost("/v1/advisory-ai/remediation/plan", HandleRemediationPlan)
.RequireRateLimiting("advisory-ai");
@@ -383,7 +387,9 @@ static bool EnsureExplainAuthorized(HttpContext context)
.SelectMany(value => value?.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) ?? [])
.ToHashSet(StringComparer.OrdinalIgnoreCase);
return allowed.Contains("advisory:run") || allowed.Contains("advisory:explain");
return allowed.Contains("advisory:run")
|| allowed.Contains("advisory:explain")
|| allowed.Contains("advisory:companion");
}
// ZASTAVA-13: POST /v1/advisory-ai/explain
@@ -450,6 +456,40 @@ static async Task<IResult> HandleExplanationReplay(
}
}
// SPRINT_20260208_003: POST /v1/advisory-ai/companion/explain
static async Task<IResult> HandleCompanionExplain(
HttpContext httpContext,
CompanionExplainRequest request,
ICodexCompanionService companionService,
CancellationToken cancellationToken)
{
using var activity = AdvisoryAiActivitySource.Instance.StartActivity("advisory_ai.companion_explain", ActivityKind.Server);
activity?.SetTag("advisory.finding_id", request.FindingId);
activity?.SetTag("advisory.vulnerability_id", request.VulnerabilityId);
activity?.SetTag("advisory.runtime_signal_count", request.RuntimeSignals.Count);
if (!EnsureExplainAuthorized(httpContext))
{
return Results.StatusCode(StatusCodes.Status403Forbidden);
}
try
{
var domainRequest = request.ToDomain();
var result = await companionService.GenerateAsync(domainRequest, cancellationToken).ConfigureAwait(false);
activity?.SetTag("advisory.companion_id", result.CompanionId);
activity?.SetTag("advisory.companion_hash", result.CompanionHash);
activity?.SetTag("advisory.explanation_id", result.Explanation.ExplanationId);
return Results.Ok(CompanionExplainResponse.FromDomain(result));
}
catch (InvalidOperationException ex)
{
return Results.BadRequest(new { error = ex.Message });
}
}
static bool EnsureRemediationAuthorized(HttpContext context)
{
if (!context.Request.Headers.TryGetValue("X-StellaOps-Scopes", out var scopes))

View File

@@ -6,3 +6,6 @@ Source of truth: `docs/implplan/SPRINT_20260130_002_Tools_csproj_remediation_sol
| --- | --- | --- |
| REMED-05 | TODO | Remediation checklist: docs/implplan/audits/csproj-standards/remediation/checklists/src/AdvisoryAI/StellaOps.AdvisoryAI.WebService/StellaOps.AdvisoryAI.WebService.md. |
| REMED-06 | DONE | SOLID review notes captured for SPRINT_20260130_002. |
| SPRINT_20260208_003-WEB | DONE | Companion explain endpoint/contracts for Codex/Zastava flow. |