From c2c4301a98e83de60fe77449b7377e8b1454ab67 Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Wed, 18 Mar 2026 11:57:42 -0400 Subject: [PATCH] feat: model alias-to-full-ID resolution for Task API compatibility (#991) (#1141) Claude Code's Task tool sometimes doesn't resolve short aliases (opus, sonnet, haiku) and passes them directly to the API, causing 404s. Tasks then inherit the parent session's model instead of the configured one. Added: - MODEL_ALIAS_MAP in core.cjs mapping aliases to full model IDs - resolve_model_ids config option (default: false for backward compat) - resolveModelInternal() maps aliases when resolve_model_ids is true Usage: { "resolve_model_ids": true } This causes gsd-tools resolve-model to return 'claude-sonnet-4-5' instead of 'sonnet', which the Task tool passes to the API without needing alias resolution on Claude Code's side. The alias map is maintained per release. Users can also use model_overrides for full control. All 755 tests pass. Fixes #991 --- get-shit-done/bin/lib/core.cjs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/get-shit-done/bin/lib/core.cjs b/get-shit-done/bin/lib/core.cjs index 16d6e2726..5038dc247 100644 --- a/get-shit-done/bin/lib/core.cjs +++ b/get-shit-done/bin/lib/core.cjs @@ -64,6 +64,7 @@ function loadConfig(cwd) { nyquist_validation: true, parallelization: true, brave_search: false, + resolve_model_ids: false, // when true, resolve aliases (opus/sonnet/haiku) to full model IDs }; try { @@ -106,6 +107,7 @@ function loadConfig(cwd) { nyquist_validation: get('nyquist_validation', { section: 'workflow', field: 'nyquist_validation' }) ?? defaults.nyquist_validation, parallelization, brave_search: get('brave_search') ?? defaults.brave_search, + resolve_model_ids: get('resolve_model_ids') ?? defaults.resolve_model_ids, model_overrides: parsed.model_overrides || null, }; } catch { @@ -557,6 +559,19 @@ function getRoadmapPhaseInternal(cwd, phaseNum) { } } +// ─── Model alias resolution ─────────────────────────────────────────────────── + +/** + * Map short model aliases to full model IDs. + * Updated each release to match current model versions. + * Users can override with model_overrides in config.json for custom/latest models. + */ +const MODEL_ALIAS_MAP = { + 'opus': 'claude-opus-4-0', + 'sonnet': 'claude-sonnet-4-5', + 'haiku': 'claude-haiku-3-5', +}; + function resolveModelInternal(cwd, agentType) { const config = loadConfig(cwd); @@ -571,7 +586,15 @@ function resolveModelInternal(cwd, agentType) { const agentModels = MODEL_PROFILES[agentType]; if (!agentModels) return 'sonnet'; if (profile === 'inherit') return 'inherit'; - return agentModels[profile] || agentModels['balanced'] || 'sonnet'; + const alias = agentModels[profile] || agentModels['balanced'] || 'sonnet'; + + // If resolve_model_ids is true, map alias to full model ID + // This prevents 404s when the Task tool passes aliases directly to the API + if (config.resolve_model_ids) { + return MODEL_ALIAS_MAP[alias] || alias; + } + + return alias; } // ─── Misc utilities ─────────────────────────────────────────────────────────── @@ -685,4 +708,5 @@ module.exports = { extractCurrentMilestone, replaceInCurrentMilestone, toPosixPath, + MODEL_ALIAS_MAP, };