mirror of
https://github.com/KeygraphHQ/shannon.git
synced 2026-10-08 01:01:10 +02:00
* feat(preflight): gate scans on an exploit-workload readiness probe
* feat: add --validate-auth to run authentication validation only
* feat: refuse reusing an auth-validation workspace for a scan
* chore: refresh suggested model IDs (Grok 4.7, OpenAI gpt-6-sol, Claude 5)
* fix(preflight): make the exploit-readiness probe trip the cyber safeguard reliably
* chore(preflight): update the exploit-readiness probe prompt
* feat: add --validate-model to run the preflight model checks only
* feat(cli): name cyber-access and app-login steps in the start loader
* feat(cli): refine start loader — skip app-login step when following, annotate preflight label
* feat(status): show Preflight and Cyber access verification rows for gated providers
* chore(preflight): suggest a fallback model in cyber-access remediation hints
* chore(preflight): drop env-var syntax from cyber-access fallback hints
* fix(preflight): separate finding heading from Target line in readiness probe
* refactor(preflight): rename exploit-readiness probe to cyber access verification
* fix(preflight): re-join finding heading with Target line in readiness probe
Reverts the heading/Target split from 22d84f2, gluing each finding's
heading back onto its Target line in the cyber-access probe's user
content.
* feat(validation): show a Checks summary in the validation log
* fix(cli): say a validation run failed, not that it could not start
* docs(ai-providers): replace broken Pi subscription link with /login steps
* feat(preflight): gate the openai-codex subscription on cyber access
89 lines
3.9 KiB
TypeScript
89 lines
3.9 KiB
TypeScript
// Copyright (C) 2026 Keygraph, Inc.
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License version 3
|
|
// as published by the Free Software Foundation.
|
|
|
|
/**
|
|
* Maps PipelineState to WorkflowSummary for audit logging.
|
|
* Pure function with no side effects.
|
|
*/
|
|
|
|
import type { WorkflowSummary } from '../audit/workflow-logger.js';
|
|
import type { PipelineState } from './shared.js';
|
|
|
|
/**
|
|
* Maps PipelineState to WorkflowSummary.
|
|
*
|
|
* This function is deterministic (no Date.now() or I/O) so it can be
|
|
* safely imported into Temporal workflows. The caller must ensure
|
|
* state.summary is set before calling (via computeSummary).
|
|
*/
|
|
export function toWorkflowSummary(
|
|
state: PipelineState,
|
|
status: 'completed' | 'failed' | 'cancelled' | 'partial',
|
|
): WorkflowSummary {
|
|
// state.summary must be computed before calling this mapper
|
|
const summary = state.summary;
|
|
if (!summary) {
|
|
throw new Error('toWorkflowSummary: state.summary must be set before calling');
|
|
}
|
|
|
|
// The failure detail is one of the child workflow's fixed safe sentences, so it carries no
|
|
// provider, prompt, repository, or path content and travels with the stable code.
|
|
const agenticSastFailure = state.agenticSast.status === 'failed' ? state.agenticSast : undefined;
|
|
const agenticSastErrorCode = agenticSastFailure?.errorCode;
|
|
const agenticSastFailureMessage = agenticSastFailure?.error;
|
|
const agenticSastFailedStage = agenticSastFailure?.failedStageLabel;
|
|
// Both terminal Capella variants carry a warnings array; a disabled or still-running run has none.
|
|
const agenticSast = state.agenticSast;
|
|
const usageAccountingWarnings =
|
|
agenticSast.status === 'succeeded' || agenticSast.status === 'failed' ? agenticSast.warnings : [];
|
|
// Carry the terminal disposition so a successful (or reduced-coverage) run is visible in the summary,
|
|
// not only a failed one. Coverage is meaningful only on success.
|
|
const agenticSastCoverage = agenticSast.status === 'succeeded' ? agenticSast.coverage : undefined;
|
|
const endedAtMs = state.startTime + summary.totalDurationMs;
|
|
return {
|
|
status,
|
|
startedAtMs: state.startTime,
|
|
endedAtMs,
|
|
totalDurationMs: summary.totalDurationMs,
|
|
totalCostUsd: summary.totalCostUsd,
|
|
completedAgents: state.completedAgents,
|
|
skippedAgents: state.skippedAgents,
|
|
agentMetrics: Object.fromEntries(
|
|
Object.entries(state.agentMetrics).map(([name, metrics]) => [
|
|
name,
|
|
{ durationMs: metrics.durationMs, costUsd: metrics.costUsd },
|
|
]),
|
|
),
|
|
operationalMetrics: Object.fromEntries(
|
|
Object.entries(state.operationalMetrics).map(([name, metrics]) => [
|
|
name,
|
|
{ ...metrics, usageComplete: metrics.usageComplete !== false },
|
|
]),
|
|
),
|
|
// The stage wall-clocks the summary reads to report each group's real elapsed time; the priced
|
|
// metrics carry cost but no faithful duration (reconciliation stages record 0).
|
|
operationalStages: Object.fromEntries(
|
|
Object.entries(state.operationalStages).map(([key, stage]) => [
|
|
key,
|
|
{
|
|
status: stage.status,
|
|
...(stage.startedAt !== undefined && { startedAt: stage.startedAt }),
|
|
...(stage.durationMs !== undefined && { durationMs: stage.durationMs }),
|
|
},
|
|
]),
|
|
),
|
|
partialReasons: state.partialReasons,
|
|
usageAccountingComplete: summary.usageAccountingComplete,
|
|
usageAccountingWarnings: [...usageAccountingWarnings],
|
|
agenticSastStatus: agenticSast.status,
|
|
...(agenticSastCoverage !== undefined && { agenticSastCoverage }),
|
|
...(agenticSastFailedStage !== undefined && { agenticSastFailedStage }),
|
|
...(agenticSastFailureMessage !== undefined && { agenticSastFailureMessage }),
|
|
...(agenticSastErrorCode !== undefined && { agenticSastErrorCode }),
|
|
...(state.errorCode !== undefined && { errorCode: state.errorCode }),
|
|
};
|
|
}
|