mirror of
https://github.com/KeygraphHQ/shannon.git
synced 2026-08-11 14:00:24 +02:00
feat: multi-provider model support, SARIF output, and exploit-mode fixes (#402)
* feat(worker): record token, cache, and turn usage per agent * feat: replace model tiers with a single SHANNON_AI_MODEL across five providers * feat(cli): rebuild the setup wizard for provider and model selection * docs: document single-model selection and supported providers * feat(worker): use chat completions for OpenAI behind a custom base URL * feat: add SHANNON_AI_OPENAI_FORMAT to pick the wire API for OpenAI gateways * refactor(cli): drop endpoint path hints from the gateway format picker * feat(worker): enable pi in-session provider retry with retry-after backoff * refactor(worker): hand provider error classification to pi and drop the Anthropic ladders * refactor: remove the subscription retry preset and pipeline config section * fix(worker): validate Bedrock credentials with the same live probe as other providers * feat(worker): render the report from structured findings instead of agent-written markdown * fix(worker): dispose the credential probe session on every path * fix(worker): refuse to replace the assembled report with an empty one * refactor(worker): catch post-processing throws across the whole finalization block * revert(worker): drop the report zero-findings guard * docs(worker): correct the retry split and Bedrock credential claims * docs: regenerate llms-full.txt from current sources * feat(cli): build and run the npx flow from a clone * refactor(cli): flatten the setup summary output * feat(cli): reject runs with more than one provider configured * fix(worker): say a rejected bash call never ran * chore(cli): drop grok-4.3 and gpt-5.6-luna from the setup suggestions * feat(worker): capture structured finding locations for SARIF output * fix(worker): enumerate queue confidence so the report inherits it verbatim * feat(worker): give the reporting phase a mode-specific output schema * feat(worker): emit a SARIF 2.1.0 log for exploitative runs * fix(worker): correct SARIF locations and defer fingerprinting to the upload action * fix(worker): drop the confidence suffix from the analysis-mode summary list * feat(worker): give exploit findings a dedicated code location field * feat(worker): carry structured code locations from the vuln queue to the report * fix(worker): join code locations from the vuln queue instead of re-asking agents * fix(worker): spell out the finding_id to category mapping in the tool schema * feat: drop Google/Gemini as a supported AI provider * fix(worker): stop asking the report agent for code locations * docs: correct the provider list and drop the removed rate-limit settings * docs: add provider cyber safeguards and suggested models per provider * docs: document the SARIF output and the report rating thresholds
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* Parsing for the single model setting, `SHANNON_AI_MODEL=<provider>:<model-id>`.
|
||||
*
|
||||
* Mirrors apps/worker/src/ai/models.ts. The CLI cannot import from the worker
|
||||
* package (it ships as a standalone bundle), so the provider list and the parse
|
||||
* rule are duplicated here deliberately and must stay in sync.
|
||||
*/
|
||||
|
||||
/** Providers Shannon can currently reach. Each is a pi-ai provider id. */
|
||||
export const SUPPORTED_PROVIDERS = ['anthropic', 'openai', 'xai', 'amazon-bedrock'] as const;
|
||||
|
||||
export type ProviderId = (typeof SUPPORTED_PROVIDERS)[number];
|
||||
|
||||
/**
|
||||
* Env vars carrying each provider's API key, in precedence order. Any one of them
|
||||
* satisfies the provider. Mirrors PROVIDER_API_KEY_ENV in apps/worker/src/ai/models.ts.
|
||||
*/
|
||||
export const PROVIDER_API_KEY_ENV: Readonly<Record<ProviderId, readonly string[]>> = {
|
||||
anthropic: ['ANTHROPIC_API_KEY', 'CLAUDE_CODE_OAUTH_TOKEN'],
|
||||
openai: ['OPENAI_API_KEY'],
|
||||
xai: ['XAI_API_KEY'],
|
||||
'amazon-bedrock': ['AWS_BEARER_TOKEN_BEDROCK'],
|
||||
};
|
||||
|
||||
/** Additional env vars a provider requires beyond its API key. All must be set. */
|
||||
export const PROVIDER_EXTRA_ENV: Readonly<Record<ProviderId, readonly string[]>> = {
|
||||
anthropic: [],
|
||||
openai: [],
|
||||
xai: [],
|
||||
'amazon-bedrock': ['AWS_REGION'],
|
||||
};
|
||||
|
||||
/** Human-readable credential requirement, used in "nothing configured" errors. */
|
||||
export const PROVIDER_CREDENTIAL_HINT: Readonly<Record<ProviderId, string>> = {
|
||||
anthropic: 'ANTHROPIC_API_KEY (or CLAUDE_CODE_OAUTH_TOKEN)',
|
||||
openai: 'OPENAI_API_KEY',
|
||||
xai: 'XAI_API_KEY',
|
||||
'amazon-bedrock': 'AWS_REGION and AWS_BEARER_TOKEN_BEDROCK',
|
||||
};
|
||||
|
||||
/** Model used when SHANNON_AI_MODEL is unset. */
|
||||
export const DEFAULT_MODEL_SPEC = 'anthropic:claude-sonnet-4-6';
|
||||
|
||||
/**
|
||||
* Values SHANNON_AI_OPENAI_FORMAT accepts, selecting the wire format an
|
||||
* OpenAI-compatible gateway serves. Mirrors OPENAI_FORMATS in
|
||||
* apps/worker/src/ai/models.ts; the worker validates and applies it.
|
||||
*/
|
||||
export const OPENAI_FORMATS = ['chat-completions', 'responses'] as const;
|
||||
|
||||
export type OpenAiFormat = (typeof OPENAI_FORMATS)[number];
|
||||
|
||||
export interface ModelSpec {
|
||||
providerId: ProviderId;
|
||||
modelId: string;
|
||||
}
|
||||
|
||||
function isSupportedProvider(value: string): value is ProviderId {
|
||||
return (SUPPORTED_PROVIDERS as readonly string[]).includes(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a `<provider>:<model-id>` spec. Splits on the first colon only, so colons
|
||||
* inside a model ID survive (`amazon-bedrock:us.anthropic.claude-opus-4-5-20251101-v1:0`).
|
||||
* Returns an error string rather than throwing, for the CLI's validation flow.
|
||||
*/
|
||||
export function parseModelSpec(spec: string): ModelSpec | string {
|
||||
const trimmed = spec.trim();
|
||||
const separator = trimmed.indexOf(':');
|
||||
const malformed = `SHANNON_AI_MODEL must be "<provider>:<model-id>", got "${trimmed}". Example: ${DEFAULT_MODEL_SPEC}`;
|
||||
if (separator === -1) return malformed;
|
||||
|
||||
const providerId = trimmed.slice(0, separator).trim();
|
||||
const modelId = trimmed.slice(separator + 1).trim();
|
||||
if (!providerId || !modelId) return malformed;
|
||||
|
||||
if (!isSupportedProvider(providerId)) {
|
||||
return `Unsupported provider "${providerId}" in SHANNON_AI_MODEL. Supported providers: ${SUPPORTED_PROVIDERS.join(', ')}`;
|
||||
}
|
||||
return { providerId, modelId };
|
||||
}
|
||||
|
||||
/** Resolve the run's model spec from the environment, or an error string. */
|
||||
export function resolveModelSpec(): ModelSpec | string {
|
||||
return parseModelSpec(process.env.SHANNON_AI_MODEL || DEFAULT_MODEL_SPEC);
|
||||
}
|
||||
Reference in New Issue
Block a user