mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 22:14:25 +01:00
Outside-review consent (codex_reviews) is enforced in gstack-codex-probe run-with-timeout, gstack-claude-code, gstack-outside-voice run and the autoplan runner (not_run / consent_missing, exit 3; #3102). Codex reasoning effort resolves GSTACK_CODEX_EFFORT, then codex_effort, then each gate's default inside run-with-timeout --effort (D1-A; #3098, closes #2934). README and telemetry privacy disclosures corrected (#1081, #1080, #1150). Co-authored-by: Lockyer <135391289+Lockyer228@users.noreply.github.com> Co-authored-by: Lubos Buracinsky <159481718+lubosxyz@users.noreply.github.com>
47 lines
2.4 KiB
TypeScript
47 lines
2.4 KiB
TypeScript
import { createHash } from 'node:crypto';
|
|
|
|
export type ModelProvider = 'anthropic' | 'openai';
|
|
export type ModelTier = 'frontier' | 'smart';
|
|
|
|
export const MODEL_PROVIDERS: readonly ModelProvider[] = Object.freeze(['anthropic', 'openai'] as const);
|
|
export const MODEL_TIERS: readonly ModelTier[] = Object.freeze(['frontier', 'smart'] as const);
|
|
export const MODEL_CATALOG_VERSION = 1 as const;
|
|
/** Codex model_reasoning_effort levels gstack accepts (union of Codex config levels and the API's minimal; model-dependent). bin/gstack-codex-probe (run-with-timeout --effort, check-effort) and gstack-config codex_effort hold the same list. */
|
|
export const CODEX_EFFORT_LEVELS = Object.freeze(['minimal', 'low', 'medium', 'high', 'xhigh', 'max', 'ultra'] as const);
|
|
export type CodexEffort = typeof CODEX_EFFORT_LEVELS[number];
|
|
|
|
export interface ModelCatalogEntry {
|
|
readonly provider: ModelProvider;
|
|
readonly tier: ModelTier;
|
|
readonly model: string;
|
|
readonly sourceUrl: string;
|
|
readonly verifiedAt: string;
|
|
}
|
|
|
|
const ANTHROPIC_SOURCE = 'https://platform.claude.com/docs/en/models/overview';
|
|
const OPENAI_SOURCE = 'https://developers.openai.com/api/docs/models';
|
|
|
|
export const MODEL_CATALOG: readonly ModelCatalogEntry[] = Object.freeze([
|
|
{ provider: 'anthropic', tier: 'frontier', model: 'claude-fable-5-1', sourceUrl: ANTHROPIC_SOURCE, verifiedAt: '2026-10-07' },
|
|
{ provider: 'openai', tier: 'frontier', model: 'gpt-6-astra', sourceUrl: OPENAI_SOURCE, verifiedAt: '2026-10-07' },
|
|
{ provider: 'anthropic', tier: 'smart', model: 'claude-opus-5-5', sourceUrl: ANTHROPIC_SOURCE, verifiedAt: '2026-10-07' },
|
|
{ provider: 'openai', tier: 'smart', model: 'gpt-6.1-sol', sourceUrl: OPENAI_SOURCE, verifiedAt: '2026-10-07' },
|
|
].map(entry => Object.freeze(entry as ModelCatalogEntry)));
|
|
|
|
export function catalogEntry(provider: ModelProvider, tier: ModelTier): ModelCatalogEntry {
|
|
const entry = MODEL_CATALOG.find(e => e.provider === provider && e.tier === tier);
|
|
if (!entry) throw new Error(`model catalog has no ${tier} entry for ${provider}`);
|
|
return entry;
|
|
}
|
|
|
|
export function modelCatalogCanonicalJson(): string {
|
|
return JSON.stringify({
|
|
version: MODEL_CATALOG_VERSION,
|
|
entries: MODEL_CATALOG.map(e => ({ provider: e.provider, tier: e.tier, model: e.model, sourceUrl: e.sourceUrl, verifiedAt: e.verifiedAt })),
|
|
});
|
|
}
|
|
|
|
export function modelCatalogSha256(): string {
|
|
return createHash('sha256').update(modelCatalogCanonicalJson()).digest('hex');
|
|
}
|