From 6edd2b7aa17aff8363bdab9489483ccc87c3b07a Mon Sep 17 00:00:00 2001 From: Nivesh353 Date: Tue, 21 Jul 2026 15:12:00 +0530 Subject: [PATCH 1/2] Scaffold gitagent agent --- src/auto-detect-model.ts | 47 ++++++++++++++++++++++++++++++++++++++++ src/loader.ts | 14 +++++++++--- 2 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 src/auto-detect-model.ts diff --git a/src/auto-detect-model.ts b/src/auto-detect-model.ts new file mode 100644 index 0000000..3f57b6a --- /dev/null +++ b/src/auto-detect-model.ts @@ -0,0 +1,47 @@ +// Zero-config model auto-detection (issue #14). +// +// When no model is configured via --model, agent.yaml, or env config override, +// check known provider API keys (via pi-ai's getEnvApiKey, so Bedrock / Vertex / +// Azure / Copilot ambient credentials are picked up too, not just a hardcoded +// env var list) and fall back to a sensible default model for the first +// provider that has credentials available. + +import { getEnvApiKey } from "@mariozechner/pi-ai"; + +// Checked in this order; first provider with credentials available wins. +// Versionless aliases only, so this never goes stale as new point releases ship. +// +// Direct API-key providers first (explicit, deliberate signal); ambient/ +// enterprise credential sources (Bedrock, Vertex, Azure, Copilot) checked +// after, since those can be present incidentally (e.g. a leftover AWS_PROFILE +// or an active gcloud login unrelated to this agent). +const DEFAULT_MODEL_BY_PROVIDER: Array<{ provider: string; model: string }> = [ + { provider: "anthropic", model: "claude-sonnet-4-6" }, + { provider: "openai", model: "gpt-4o" }, + { provider: "google", model: "gemini-2.0-flash" }, + { provider: "xai", model: "grok-4-fast" }, + { provider: "groq", model: "llama-3.3-70b-versatile" }, + { provider: "mistral", model: "mistral-large-latest" }, + { provider: "amazon-bedrock", model: "anthropic.claude-sonnet-4-6" }, + { provider: "google-vertex", model: "gemini-2.0-flash" }, + { provider: "azure-openai-responses", model: "gpt-4o" }, + { provider: "github-copilot", model: "claude-sonnet-4.6" }, +]; + +export interface AutoDetectedModel { + modelStr: string; + provider: string; +} + +/** + * Find the first provider with a usable API key/credential in the environment + * and return its default model, or undefined if none are configured. + */ +export function autoDetectModel(): AutoDetectedModel | undefined { + for (const { provider, model } of DEFAULT_MODEL_BY_PROVIDER) { + if (getEnvApiKey(provider)) { + return { modelStr: `${provider}:${model}`, provider }; + } + } + return undefined; +} diff --git a/src/loader.ts b/src/loader.ts index b8d193e..e2865bd 100644 --- a/src/loader.ts +++ b/src/loader.ts @@ -19,6 +19,7 @@ import { loadExamples, formatExamplesForPrompt } from "./examples.js"; import type { ExampleEntry } from "./examples.js"; import { validateCompliance, loadComplianceContext, formatComplianceWarnings } from "./compliance.js"; import type { ComplianceWarning } from "./compliance.js"; +import { autoDetectModel } from "./auto-detect-model.js"; import { discoverAndLoadPlugins } from "./plugins.js"; import type { LoadedPlugin } from "./plugin-types.js"; import type { PluginConfig } from "./plugin-types.js"; @@ -379,11 +380,18 @@ Do NOT track trivial single-command tasks (e.g. "what time is it"). But DO check const systemPrompt = parts.join("\n\n"); - // Resolve model — env config model_override > CLI flag > manifest preferred - const modelStr = envConfig.model_override || modelFlag || manifest.model.preferred; + // Resolve model — env config model_override > CLI flag > manifest preferred > auto-detected from API key + let modelStr = envConfig.model_override || modelFlag || manifest.model?.preferred; + if (!modelStr) { + const detected = autoDetectModel(); + if (detected) { + console.log(`Using ${detected.modelStr} (auto-detected from ${detected.provider} credentials)`); + modelStr = detected.modelStr; + } + } if (!modelStr) { throw new Error( - 'No model configured. Either:\n - Set model.preferred in agent.yaml (e.g., "anthropic:claude-sonnet-4-5-20250929")\n - Pass --model provider:model on the command line', + 'No model configured. Either:\n - Set model.preferred in agent.yaml (e.g., "anthropic:claude-sonnet-4-5-20250929")\n - Pass --model provider:model on the command line\n - Set an API key for a known provider (e.g. ANTHROPIC_API_KEY) to auto-select a default model', ); } From 3b0417c2dc1057a10d68f94be1c35cdcb59f53a0 Mon Sep 17 00:00:00 2001 From: Nivesh353 Date: Tue, 21 Jul 2026 15:47:40 +0530 Subject: [PATCH 2/2] fix: normalize manifest.model so downstream code can't crash on undefined MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When model: is absent from agent.yaml entirely (not just missing preferred), code reading manifest.model.constraints elsewhere (index.ts, sdk.ts) would throw "Cannot read properties of undefined". Previously masked because a missing model always hit the "No model configured" error first — now that auto-detection can resolve a model even without a model: block, execution reaches further and needs this to hold. Normalizes manifest.model to { fallback: [] } right after parsing agent.yaml, and marks model.preferred as optional in the type, since it's genuinely allowed to be absent now. --- src/loader.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/loader.ts b/src/loader.ts index e2865bd..b548635 100644 --- a/src/loader.ts +++ b/src/loader.ts @@ -34,7 +34,7 @@ export interface AgentManifest { tags?: string[]; metadata?: Record; model: { - preferred: string; + preferred?: string; fallback: string[]; constraints?: { temperature?: number; @@ -242,6 +242,9 @@ export async function loadAgent( // Parse agent.yaml const manifestRaw = await readFile(join(agentDir, "agent.yaml"), "utf-8"); let manifest = yaml.load(manifestRaw) as AgentManifest; + // model: is optional in agent.yaml (auto-detection can fill it in later) — + // normalize here so downstream code can always assume manifest.model exists. + manifest.model ??= { fallback: [] }; // Load environment config const envConfig = await loadEnvConfig(agentDir, envFlag);