* test(#443): RED unified effort + fast_mode + resolve-execution All 68 tests failing as expected — no implementation yet. Covers: effort cascade (tier defaults, overrides, invalid fallthrough), fast_mode cascade (boolean-only, tier defaults), resolveEffortForTier escalation, renderEffortForRuntime clamping, resolve-execution CLI, config schema new keys, QA hostile-input matrix. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(#443): unified cross-provider effort + fast_mode knobs and resolve-execution query Adds config-driven effort control (universal ladder: minimal<low<medium<high<xhigh<max) and fast_mode propagation knobs, with per-runtime rendering that clamps the unique tail values (max=Anthropic-only clamps to xhigh on Codex; minimal=Codex-only clamps to low on Claude). Key changes: - config-schema.manifest.json: add effort.default, fast_mode.enabled as validKeys; add 4 dynamicKeyPatterns for effort.routing_tier_defaults, effort.agent_overrides, fast_mode.routing_tier_defaults, fast_mode.agent_overrides; fix stale _comment - config-defaults.manifest.json: add effort and fast_mode blocks with tier defaults - model-catalog.cjs: add EFFORT_RENDERING map, renderEffortForRuntime(), RUNTIMES_WITH_FAST_MODE - model-profiles.cjs: re-export new catalog exports - core.cjs: add resolveEffortInternal, resolveFastModeInternal, resolveEffortForTier, VALID_EFFORTS, EFFORT_SET, nextEffort; pass effort/fast_mode through loadConfig - commands.cjs: replace reasoning_effort in cmdResolveModel with unified effort; add cmdResolveExecution (superset command with effort_rendered, effort_param, effort_propagation, fast_mode, fast_mode_supported) - gsd-tools.cjs: add resolve-execution case with --effort/--fast-mode/--attempt flags - tests/feat-443: 69 tests covering cascade, rendering, escalation, CLI, schema, QA matrix - tests/commands.test.cjs: convert 3 reasoning_effort assertions to unified effort - docs/CONFIGURATION.md: document effort + fast_mode + resolve-execution sections - settings-advanced.md: list new effort/fast_mode keys in confirmation table Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(#443): remove dead catalog effort lane; unify codex effort through renderEffortForRuntime - Remove resolveReasoningEffortInternal (catalog-driven effort function) from core.cjs and its export; remove from commands.cjs destructure import - Convert tests/issue-2517-runtime-aware-profiles.test.cjs: all 11 effort assertions now use resolveEffortInternal + renderEffortForRuntime; Claude effort is first-class (output_config.effort); unknown runtimes assert param===null - Convert tests/feat-3023-model-phase-types.test.cjs: replace the entire resolveReasoningEffortInternal describe with unified effort assertions; effort derives from AGENT_DEFAULT_TIERS routing tier, not phase-type tier Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs(#443): ADR for unified cross-provider effort + fast-mode routing Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * test(#443): architecture-level QA invariants + test-strategy doc Add 48-test integration suite (feat-443-effort-fast-mode.integration.test.cjs) covering 8 architectural invariants: cross-provider validity (never emit a value the real API would 400 on), param/channel contract stability, resolve-execution JSON contract (all 8 keys + correct types), totality across the full 33-agent registry, fast-mode honesty (claude always fast_mode_supported=false), precedence first-valid-wins matrix for both effort and fast_mode cascades, dynamic-routing composition (effort escalation independent of model tier), and config-set round-trip for all new effort/* and fast_mode/* key namespaces. Append test-strategy section with invariant rationale and E2E gap documentation to docs/TESTING-SUITES.md. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(#443): add failing install-wiring tests for effort per-runtime injection (RED) TDD RED: 10 failing tests covering: - Claude .md gets effort: injected per tier (planner=xhigh, mapper=low, executor=high) - Gemini .md does NOT get effort: (already passing — Gemini-safe) - Codex .toml gets model_reasoning_effort via unified resolver - Config-driven: effort.agent_overrides drives both Claude .md and Codex .toml - Source purity: agents/*.md have no effort: key (already passing) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(#443): wire effort per-runtime at install (Claude .md frontmatter + Codex .toml unified) - Import AGENT_DEFAULT_TIERS and renderEffortForRuntime from model-catalog.cjs - Add readGsdEffectiveEffortConfig(targetDir): reads merged effort config from .planning/config.json (per-project wins) + ~/.gsd/defaults.json (global fallback), same probe pattern as readGsdRuntimeProfileResolver - Add resolveInstallTimeEffort(effortCfg, agentName): pure function matching resolveEffortInternal() precedence (agent_overrides > routing_tier_defaults > default > 'high') without loadConfig side-effects (no sub-repo detection, no migration writes) - Claude agent copy loop: inject `effort: <value>` into frontmatter ONLY for runtime === 'claude'; all other .md runtimes (Gemini, Qwen, Hermes, etc.) stay effort-free (Gemini-safe source contract preserved in agents/*.md) - generateCodexAgentToml: add effortCfg param; emit model_reasoning_effort from unified resolver (replaces old catalog entry.reasoning_effort); Codex clamps max → xhigh via renderEffortForRuntime('codex', ...) - installCodexConfig: pass readGsdEffectiveEffortConfig(targetDir) to generateCodexAgentToml so per-project config wins for Codex .toml too - Update failing tests to GREEN: 12/12 pass; all 17 install tests pass; 2847/2848 unit tests pass (1 pre-existing failure: policy-shell-pinning) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(#443): source install effort defaults from manifest (kill drift) + guard test Replace hardcoded _GSD_EFFORT_MANIFEST_TIER_DEFAULTS and the 'high' fallback in resolveInstallTimeEffort with values read from config-defaults.manifest.json at module init, using the same __dirname-relative path install.js already uses for all shared manifests. Add feat-443-effort-defaults-drift.test.cjs to assert equality between install.js's runtime constants and the manifest on every CI run. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(#443): reconcile Codex TOML tests with unified effort design The #443 unified effort resolver makes generateCodexAgentToml always emit model_reasoning_effort (driven by resolveInstallTimeEffort, not model_profile_overrides). The test 'generated TOML omits reasoning_effort when runtime has none' had an obsolete premise — model_profile_overrides.reasoning_effort:'' no longer suppresses unified effort. Convert it to assert the new invariant: Codex TOML always carries a valid model_reasoning_effort from the agent's routing tier (xhigh for gsd-planner, a heavy-tier agent), while model_profile_overrides model override is still respected. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(#443): make install.js effort resolution lazy (no load-time side effects breaking launcher-parity) Replace module-load-time IIFE + hard throw (config-defaults.manifest.json read) and top-level require of model-catalog.cjs with a lazy _getGsdEffortCatalog() getter that initialises on first call from resolveInstallTimeEffort / generateCodexAgentToml / Claude .md effort injection. Requiring install.js in unrelated test contexts (e.g. runtime-launcher-parity) no longer triggers manifest IO or throws, eliminating the load-time side effect that changed subprocess exit codes / stderr on the bench. Drift-guard exports (_GSD_EFFORT_MANIFEST_TIER_DEFAULTS / _GSD_EFFORT_MANIFEST_DEFAULT) preserved as lazy getter properties on module.exports so feat-443-effort-defaults-drift still validates them without forcing eager load. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(#443): isolate install-wiring test HOME to stop \$HOME/.claude pollution breaking launcher-parity runGlobalInstall() now redirects HOME to a per-call isolated tmpdir in addition to the existing runtime-specific env-var redirects (CLAUDE_CONFIG_DIR, GEMINI_CONFIG_DIR, CODEX_HOME). This ensures install.js code that uses os.homedir() directly — including the ~/.cache/gsd update-check deletion, ~/.gsd/defaults.json reads, and any HOME-relative npm subprocess writes — never touches the real \$HOME during the test. Without the HOME isolation the install test (which is new to this branch and is now picked up by Docker's raw \`tests/*.test.cjs\` glob) could write or delete files under the real \$HOME, causing runtime-launcher-parity test (D) to fail: (D) asserts a loud non-zero exit when \$RUNTIME_DIR/gsd-tools.cjs is absent and gsd-tools is not on PATH, but the launcher's \$HOME/.claude fallback arm succeeds if \$HOME/.claude/get-shit-done/bin/gsd-tools.cjs exists. Also sets GSD_SKIP_STALE_SDK_CHECK=1 to suppress the \`npm ls -g\` subprocess that the global installer spawns — irrelevant to effort-wiring assertions, slow, and potentially writes to ~/.npm cache. All 12 feat-443 install-wiring assertions preserved. Drift-guard 5/5. Unit suite 2848/2850 (pre-existing policy-shell-pinning.test.cjs failure on next). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(#443): add changeset fragment for effort + fast-mode routing Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(#443): set GSD_TEST_MODE before requiring install.js in drift-guard test to prevent HOME leak Without GSD_TEST_MODE=1, require('bin/install.js') runs the module's main install block (guarded by !GSD_TEST_MODE), performing a real global Claude install into $HOME/.claude/. On CI ubuntu where node is on standard PATH, the launcher's $HOME/.claude fallback arm then finds gsd-tools.cjs, causing runtime-launcher-parity test (D) to exit zero when it must exit non-zero. Root cause: feat-443-effort-defaults-drift.test.cjs (unit suite) runs alphabetically before runtime-launcher-parity.test.cjs in the same node --test invocation. Each runs in a separate worker process but shares the same HOME. The drift test's install leaks gsd-tools.cjs into that HOME, then the launcher test's bash subprocess finds it via the $HOME/.claude arm. Fix: add process.env.GSD_TEST_MODE = '1' at the top of the drift-guard test, before the require(installPath) call. This matches the pattern used by feat-443-effort-fast-mode.test.cjs and feat-443-effort-install-wiring .install.test.cjs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(#443): deterministic resolve-execution arg parsing + validate install-time effort (Codex adversarial findings) Finding 1: resolve-execution --effort low gsd-planner misrouted 'low' as the agent. Replace find(non-dash) with a proper flag-consuming loop that collects a single positional; validate missing/extra positionals and malformed --attempt values. Finding 2: resolveInstallTimeEffort returned unvalidated effort strings (e.g. "ultra") verbatim. Each precedence layer now checks GSD_EFFORT_SET (imported once from core.cjs) before accepting a value, mirroring resolveEffortInternal exactly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(#443): newline-agnostic effort frontmatter injection (Windows CRLF) + CRLF-safe assertions Extracts injectEffortFrontmatter(content, effortValue) pure helper that detects EOL (LF vs CRLF) from the opening '---' line and inserts 'effort: <value>' before the closing '---' delimiter using the same EOL as the surrounding frontmatter. Regex now uses /^---\r?\n([\s\S]*?)^---\r?$/m instead of the LF-only /^(---\n[\s\S]*?)(---)(\n|$)/ that silently skipped CRLF files on Windows (git core.autocrlf=true checkout). Also adds 7 unit tests covering LF, CRLF, idempotency, no-frontmatter, and complex frontmatter cases. Exports injectEffortFrontmatter from module.exports. Fixes 6 CI failures in tests/feat-443-effort-install-wiring.install.test.cjs on windows-latest runners (lines 138, 145, 152, 261, 345, 356). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: CI Rebase Check <ci@gsd-redux> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -538,6 +538,81 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
||||
break;
|
||||
}
|
||||
|
||||
case 'resolve-execution': {
|
||||
// Deterministic flag parsing: consume --flag <value> pairs first,
|
||||
// then the AGENT is the single remaining positional.
|
||||
// Supports both orderings: <agent> --flag val AND --flag val <agent>.
|
||||
// Also supports --flag=value form (same convention as --cwd= above).
|
||||
const execArgs = args.slice(1);
|
||||
let effortOverride;
|
||||
let fastModeOverride;
|
||||
let attempt;
|
||||
const positionals = [];
|
||||
for (let i = 0; i < execArgs.length; i++) {
|
||||
const a = execArgs[i];
|
||||
// --effort=<val> form
|
||||
if (a.startsWith('--effort=')) {
|
||||
effortOverride = a.slice('--effort='.length);
|
||||
continue;
|
||||
}
|
||||
// --fast-mode=<val> form
|
||||
if (a.startsWith('--fast-mode=')) {
|
||||
const v = a.slice('--fast-mode='.length);
|
||||
fastModeOverride = v === 'true' ? true : v === 'false' ? false : undefined;
|
||||
continue;
|
||||
}
|
||||
// --attempt=<val> form
|
||||
if (a.startsWith('--attempt=')) {
|
||||
const v = a.slice('--attempt='.length);
|
||||
const n = parseInt(v, 10);
|
||||
if (!Number.isInteger(n) || n < 0) error('--attempt requires a non-negative integer', ERROR_REASON.USAGE);
|
||||
attempt = n;
|
||||
continue;
|
||||
}
|
||||
// --effort <val>
|
||||
if (a === '--effort') {
|
||||
const val = execArgs[i + 1];
|
||||
if (val === undefined || val.startsWith('--')) error('Missing value for --effort', ERROR_REASON.USAGE);
|
||||
effortOverride = val;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
// --fast-mode <val>
|
||||
if (a === '--fast-mode') {
|
||||
const val = execArgs[i + 1];
|
||||
if (val === undefined || val.startsWith('--')) error('Missing value for --fast-mode', ERROR_REASON.USAGE);
|
||||
fastModeOverride = val === 'true' ? true : val === 'false' ? false : undefined;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
// --attempt <val>
|
||||
if (a === '--attempt') {
|
||||
const val = execArgs[i + 1];
|
||||
if (val === undefined || val.startsWith('--')) error('Missing value for --attempt', ERROR_REASON.USAGE);
|
||||
const n = parseInt(val, 10);
|
||||
if (!Number.isInteger(n) || n < 0) error('--attempt requires a non-negative integer', ERROR_REASON.USAGE);
|
||||
attempt = n;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
// --raw is handled by top-level arg processing; skip it here
|
||||
if (a === '--raw') continue;
|
||||
// Unknown flag
|
||||
if (a.startsWith('-')) error(`Unknown flag for resolve-execution: ${a}`, ERROR_REASON.USAGE);
|
||||
// Positional
|
||||
positionals.push(a);
|
||||
}
|
||||
if (positionals.length === 0) error('agent-type required', ERROR_REASON.USAGE);
|
||||
if (positionals.length > 1) error(`resolve-execution requires exactly one agent-type argument; got: ${positionals.join(', ')}`, ERROR_REASON.USAGE);
|
||||
const agentTypeArg = positionals[0];
|
||||
commands.cmdResolveExecution(cwd, agentTypeArg, raw, {
|
||||
effortOverride,
|
||||
fastModeOverride,
|
||||
attempt,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case 'find-phase': {
|
||||
// Phase 6 (#3575): dispatch via SDK executeForCjs when available.
|
||||
// SDK handler: findPhase in sdk/src/query/phase.ts.
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execGit, platformWriteSync, platformReadSync, platformEnsureDir } = require('./shell-command-projection.cjs');
|
||||
const { loadConfig, isGitIgnored, normalizePhaseName, comparePhaseNum, getArchivedPhaseDirs, generateSlugInternal, getMilestoneInfo, getMilestonePhaseFilter, resolveModelInternal, resolveReasoningEffortInternal, stripShippedMilestones, extractCurrentMilestone, toPosixPath, output, error, findPhaseInternal, extractOneLinerFromBody, getRoadmapPhaseInternal } = require('./core.cjs');
|
||||
const { loadConfig, isGitIgnored, normalizePhaseName, comparePhaseNum, getArchivedPhaseDirs, generateSlugInternal, getMilestoneInfo, getMilestonePhaseFilter, resolveModelInternal, resolveEffortInternal, resolveFastModeInternal, resolveEffortForTier, stripShippedMilestones, extractCurrentMilestone, toPosixPath, output, error, findPhaseInternal, extractOneLinerFromBody, getRoadmapPhaseInternal } = require('./core.cjs');
|
||||
const { renderEffortForRuntime, RUNTIMES_WITH_FAST_MODE } = require('./model-catalog.cjs');
|
||||
const { planningDir, planningPaths } = require('./planning-workspace.cjs');
|
||||
const { extractFrontmatter } = require('./frontmatter.cjs');
|
||||
const { MODEL_PROFILES } = require('./model-profiles.cjs');
|
||||
@@ -241,16 +242,71 @@ function cmdResolveModel(cwd, agentType, raw) {
|
||||
const config = loadConfig(cwd);
|
||||
const profile = config.model_profile || 'balanced';
|
||||
const model = resolveModelInternal(cwd, agentType);
|
||||
const reasoningEffort = resolveReasoningEffortInternal(cwd, agentType);
|
||||
const effort = resolveEffortInternal(cwd, agentType);
|
||||
|
||||
const agentModels = MODEL_PROFILES[agentType];
|
||||
const result = agentModels
|
||||
? { model, profile }
|
||||
: { model, profile, unknown_agent: true };
|
||||
if (reasoningEffort) result.reasoning_effort = reasoningEffort;
|
||||
? { model, profile, effort }
|
||||
: { model, profile, effort, unknown_agent: true };
|
||||
output(result, raw, model);
|
||||
}
|
||||
|
||||
/**
|
||||
* #443 — Superset execution query: model + unified effort + fast_mode.
|
||||
*
|
||||
* Emits JSON:
|
||||
* { model, profile, effort, effort_rendered, effort_param, effort_propagation,
|
||||
* fast_mode, fast_mode_supported, [unknown_agent] }
|
||||
*
|
||||
* Flags: --effort <level>, --fast-mode <true|false>, --attempt <n>
|
||||
*
|
||||
* @param {string} cwd
|
||||
* @param {string} agentType
|
||||
* @param {boolean} raw
|
||||
* @param {{ effortOverride?: string, fastModeOverride?: boolean, attempt?: number }} [opts]
|
||||
*/
|
||||
function cmdResolveExecution(cwd, agentType, raw, opts) {
|
||||
if (!agentType) {
|
||||
error('agent-type required');
|
||||
}
|
||||
|
||||
opts = opts || {};
|
||||
const config = loadConfig(cwd);
|
||||
const profile = config.model_profile || 'balanced';
|
||||
const model = resolveModelInternal(cwd, agentType);
|
||||
|
||||
const effortOpts = {};
|
||||
if (typeof opts.effortOverride === 'string') effortOpts.override = opts.effortOverride;
|
||||
|
||||
const fastModeOpts = {};
|
||||
if (typeof opts.fastModeOverride === 'boolean') fastModeOpts.override = opts.fastModeOverride;
|
||||
|
||||
const effort = (opts.attempt !== undefined && opts.attempt !== null)
|
||||
? resolveEffortForTier(cwd, agentType, opts.attempt)
|
||||
: resolveEffortInternal(cwd, agentType, effortOpts);
|
||||
|
||||
const fastMode = resolveFastModeInternal(cwd, agentType, fastModeOpts);
|
||||
|
||||
const runtime = config.runtime || 'claude';
|
||||
const rendered = renderEffortForRuntime(runtime, effort);
|
||||
|
||||
const fastModeSupported = RUNTIMES_WITH_FAST_MODE.has(runtime);
|
||||
|
||||
const agentModels = MODEL_PROFILES[agentType];
|
||||
const result = {
|
||||
model,
|
||||
profile,
|
||||
effort,
|
||||
effort_rendered: rendered.value,
|
||||
effort_param: rendered.param,
|
||||
effort_propagation: rendered.channel,
|
||||
fast_mode: fastMode,
|
||||
fast_mode_supported: fastModeSupported,
|
||||
};
|
||||
if (!agentModels) result.unknown_agent = true;
|
||||
output(result, raw, effort);
|
||||
}
|
||||
|
||||
function cmdCommit(cwd, message, files, raw, amend, noVerify) {
|
||||
if (!message && !amend) {
|
||||
error('commit message required');
|
||||
@@ -1117,6 +1173,7 @@ module.exports = {
|
||||
cmdVerifyPathExists,
|
||||
cmdHistoryDigest,
|
||||
cmdResolveModel,
|
||||
cmdResolveExecution,
|
||||
cmdCommit,
|
||||
cmdCommitToSubrepo,
|
||||
cmdSummaryExtract,
|
||||
|
||||
@@ -7,7 +7,7 @@ const os = require('os');
|
||||
const path = require('path');
|
||||
const { execGit, platformWriteSync, platformReadSync, platformEnsureDir } = require('./shell-command-projection.cjs');
|
||||
const { MODEL_PROFILES, AGENT_TO_PHASE_TYPE, VALID_PHASE_TYPES, AGENT_DEFAULT_TIERS, VALID_AGENT_TIERS, nextTier } = require('./model-profiles.cjs');
|
||||
const { MODEL_ALIAS_MAP, RUNTIME_PROFILE_MAP, KNOWN_RUNTIMES, RUNTIMES_WITH_REASONING_EFFORT } = require('./model-catalog.cjs');
|
||||
const { MODEL_ALIAS_MAP, RUNTIME_PROFILE_MAP, KNOWN_RUNTIMES, RUNTIMES_WITH_REASONING_EFFORT, renderEffortForRuntime, RUNTIMES_WITH_FAST_MODE } = require('./model-catalog.cjs');
|
||||
const {
|
||||
resolveWorktreeContext,
|
||||
parseWorktreePorcelain: parseWorktreePorcelainPolicy,
|
||||
@@ -410,7 +410,7 @@ function loadConfig(cwd, options = {}) {
|
||||
// Dynamic-pattern top-level containers (e.g. review, model_profile_overrides)
|
||||
...DYNAMIC_KEY_PATTERNS.map(p => p.topLevel),
|
||||
// Internal keys loadConfig reads but config-set doesn't expose
|
||||
'model_overrides', 'context_window', 'resolve_model_ids', 'claude_md_path',
|
||||
'model_overrides', 'context_window', 'resolve_model_ids', 'claude_md_path', 'effort', 'fast_mode',
|
||||
// Deprecated keys (still accepted for migration, not in config-set)
|
||||
// 'branching_strategy' is kept here as a safety net: it is migrated to
|
||||
// git.branching_strategy above (#3523), but on the first read of a root
|
||||
@@ -514,6 +514,10 @@ function loadConfig(cwd, options = {}) {
|
||||
// unknown runtime/tier names at load time, not silently (review finding #10).
|
||||
runtime: parsed.runtime || null,
|
||||
model_profile_overrides: parsed.model_profile_overrides || null,
|
||||
// #443 — effort/fast_mode: pass through from config.json; resolvers handle
|
||||
// defaults + tier lookups internally.
|
||||
effort: parsed.effort || null,
|
||||
fast_mode: parsed.fast_mode || null,
|
||||
agent_skills: parsed.agent_skills || {},
|
||||
manager: parsed.manager || {},
|
||||
response_language: get('response_language') || null,
|
||||
@@ -558,6 +562,8 @@ function loadConfig(cwd, options = {}) {
|
||||
model_overrides: globalDefaults.model_overrides || null,
|
||||
models: globalDefaults.models || null,
|
||||
dynamic_routing: globalDefaults.dynamic_routing || null,
|
||||
effort: globalDefaults.effort || null,
|
||||
fast_mode: globalDefaults.fast_mode || null,
|
||||
agent_skills: globalDefaults.agent_skills || {},
|
||||
response_language: globalDefaults.response_language || null,
|
||||
};
|
||||
@@ -1248,9 +1254,9 @@ function _resetRuntimeWarningCacheForTests() {
|
||||
/**
|
||||
* #2517 — Resolve the runtime-aware tier entry for (runtime, tier).
|
||||
*
|
||||
* Single source of truth shared by core.cjs (resolveModelInternal /
|
||||
* resolveReasoningEffortInternal) and bin/install.js (Codex/OpenCode TOML emit
|
||||
* paths). Always merges built-in defaults with user overrides at the field
|
||||
* Single source of truth shared by core.cjs (resolveModelInternal)
|
||||
* and bin/install.js (Codex/OpenCode TOML emit paths). Always merges
|
||||
* built-in defaults with user overrides at the field
|
||||
* level so partial overrides keep the unspecified fields:
|
||||
*
|
||||
* `{ codex: { opus: "gpt-5-pro" } }` keeps reasoning_effort: 'xhigh'
|
||||
@@ -1287,7 +1293,7 @@ function resolveTierEntry({ runtime, tier, overrides }) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience wrapper used by resolveModelInternal / resolveReasoningEffortInternal.
|
||||
* Convenience wrapper used by resolveModelInternal.
|
||||
* Pulls runtime + overrides out of a loaded config and delegates to resolveTierEntry.
|
||||
*/
|
||||
function _resolveRuntimeTier(config, tier) {
|
||||
@@ -1475,66 +1481,209 @@ function resolveModelForTier(cwd, agentType, attempt) {
|
||||
return alias;
|
||||
}
|
||||
|
||||
// ─── #443 — Unified effort + fast_mode resolvers ─────────────────────────────
|
||||
//
|
||||
// Universal effort ladder (ordered):
|
||||
const VALID_EFFORTS = ['minimal', 'low', 'medium', 'high', 'xhigh', 'max'];
|
||||
const EFFORT_SET = new Set(VALID_EFFORTS);
|
||||
|
||||
/**
|
||||
* #2517 — Resolve runtime-specific reasoning_effort for an agent.
|
||||
* Returns null unless:
|
||||
* - `runtime` is explicitly set in config,
|
||||
* - the runtime supports reasoning_effort (currently: codex),
|
||||
* - profile is not 'inherit',
|
||||
* - the resolved tier entry has a `reasoning_effort` value.
|
||||
*
|
||||
* Never returns a value for Claude — keeps reasoning_effort out of Claude spawn paths.
|
||||
* Walk one step up the effort ladder from `e`. Returns the next level, or
|
||||
* the same level if already at the top.
|
||||
*/
|
||||
function resolveReasoningEffortInternal(cwd, agentType) {
|
||||
function nextEffort(e) {
|
||||
const i = VALID_EFFORTS.indexOf(e);
|
||||
if (i < 0) return null;
|
||||
return VALID_EFFORTS[Math.min(i + 1, VALID_EFFORTS.length - 1)];
|
||||
}
|
||||
|
||||
/**
|
||||
* #443 — Resolve a universal effort string for (cwd, agentType).
|
||||
*
|
||||
* Precedence (first valid wins; invalid/wrong-type values are IGNORED and fall
|
||||
* through — mirrors the VALID_TIERS gate pattern in resolveModelInternal):
|
||||
* 1. opts.override (if in EFFORT_SET)
|
||||
* 2. config.effort.agent_overrides[agentType] (if valid)
|
||||
* 3. config.effort.routing_tier_defaults[ AGENT_DEFAULT_TIERS[agentType] ] (agent known + valid)
|
||||
* 4. config.effort.default (if valid)
|
||||
* 5. 'high' (Anthropic Opus 4.8 universal default)
|
||||
*
|
||||
* Handles: config.effort missing; effort.* non-object/malformed; unknown
|
||||
* agentType skips step 3; numeric/boolean garbage ignored.
|
||||
*
|
||||
* @param {string} cwd - Project directory.
|
||||
* @param {string} agentType - Agent name.
|
||||
* @param {{ override?: string }} [opts]
|
||||
* @returns {string} A valid effort string.
|
||||
*/
|
||||
function resolveEffortInternal(cwd, agentType, opts) {
|
||||
// Step 1: invocation override
|
||||
if (opts && typeof opts.override === 'string' && EFFORT_SET.has(opts.override)) {
|
||||
return opts.override;
|
||||
}
|
||||
|
||||
const config = loadConfig(cwd);
|
||||
if (!config.runtime) return null;
|
||||
// Strict allowlist: reasoning_effort only propagates for runtimes whose
|
||||
// install path actually accepts it. Adding a new runtime here is the only
|
||||
// way to enable effort propagation — overrides cannot bypass the gate.
|
||||
// Without this, a typo in `runtime` (e.g. `"codx"`) plus a user override
|
||||
// for that typo would leak `xhigh` into a Claude or unknown install
|
||||
// (review finding #3).
|
||||
if (!RUNTIMES_WITH_REASONING_EFFORT.has(config.runtime)) return null;
|
||||
// Per-agent override means user supplied a fully-qualified ID; reasoning_effort
|
||||
// for that case must be set via per-agent mechanism, not tier inference.
|
||||
if (config.model_overrides?.[agentType]) return null;
|
||||
const effortCfg = (config.effort && typeof config.effort === 'object' && !Array.isArray(config.effort))
|
||||
? config.effort
|
||||
: null;
|
||||
|
||||
const profile = String(config.model_profile || 'balanced').toLowerCase();
|
||||
const agentModels = MODEL_PROFILES[agentType];
|
||||
if (!agentModels) return null;
|
||||
// Step 2: agent_overrides
|
||||
if (effortCfg) {
|
||||
const ao = effortCfg.agent_overrides;
|
||||
if (ao && typeof ao === 'object' && !Array.isArray(ao)) {
|
||||
const v = ao[agentType];
|
||||
if (typeof v === 'string' && EFFORT_SET.has(v)) return v;
|
||||
}
|
||||
}
|
||||
|
||||
// #3023 (CR Major): mirror the phase-type tier lookup from
|
||||
// resolveModelInternal. Without this, `model` and `reasoning_effort`
|
||||
// derive from different tier sources on Codex when models.<phase_type>
|
||||
// overrides the profile.
|
||||
//
|
||||
// #3030 CR follow-up: do NOT short-circuit on profile === 'inherit'
|
||||
// before reading the phase-type tier. A config like
|
||||
// { model_profile: 'inherit', models: { execution: 'opus' } }
|
||||
// must produce the opus runtime effort, not null. Compute tier from
|
||||
// phase-type first; only fall back to profile when there's no valid
|
||||
// phase-type override; only return null when the resolved tier is
|
||||
// 'inherit' or unknown.
|
||||
const phaseType = AGENT_TO_PHASE_TYPE[agentType];
|
||||
const phaseTypeTier = (phaseType && config.models && typeof config.models === 'object')
|
||||
? config.models[phaseType]
|
||||
: undefined;
|
||||
// Explicit phase-type 'inherit' is the user opting out of tier-based
|
||||
// effort for this phase — return null instead of falling through to
|
||||
// profile (which would silently emit the profile's effort and
|
||||
// contradict the user's choice).
|
||||
if (phaseTypeTier === 'inherit') return null;
|
||||
const VALID_TIERS = new Set(['opus', 'sonnet', 'haiku']);
|
||||
const tier = (phaseTypeTier && VALID_TIERS.has(phaseTypeTier))
|
||||
? phaseTypeTier
|
||||
: (profile === 'inherit'
|
||||
? 'inherit'
|
||||
: (agentModels[profile] || agentModels['balanced']));
|
||||
// 'inherit' (from profile fallback) yields no runtime effort.
|
||||
if (!tier || tier === 'inherit') return null;
|
||||
// Step 3: routing_tier_defaults by agent's default tier.
|
||||
// Manifest tier defaults are only used when there is NO effort config block at all
|
||||
// (effortCfg === null). When the user explicitly sets an effort block, we respect
|
||||
// their explicit routing_tier_defaults (if set) and fall through to effort.default
|
||||
// if they didn't set them. This prevents the manifest tier defaults from silently
|
||||
// overriding a user's `effort: { default: "medium" }`.
|
||||
const agentTier = AGENT_DEFAULT_TIERS[agentType];
|
||||
if (agentTier) {
|
||||
if (effortCfg && effortCfg.routing_tier_defaults &&
|
||||
typeof effortCfg.routing_tier_defaults === 'object' &&
|
||||
!Array.isArray(effortCfg.routing_tier_defaults)) {
|
||||
// User provided routing_tier_defaults — honor them
|
||||
const v = effortCfg.routing_tier_defaults[agentTier];
|
||||
if (typeof v === 'string' && EFFORT_SET.has(v)) return v;
|
||||
} else if (!effortCfg) {
|
||||
// No effort config at all — use manifest tier defaults
|
||||
const manifestDefaults = CANONICAL_CONFIG_DEFAULTS.effort?.routing_tier_defaults;
|
||||
if (manifestDefaults && typeof manifestDefaults === 'object') {
|
||||
const v = manifestDefaults[agentTier];
|
||||
if (typeof v === 'string' && EFFORT_SET.has(v)) return v;
|
||||
}
|
||||
}
|
||||
// else: effortCfg exists but no routing_tier_defaults — fall through to effort.default
|
||||
}
|
||||
|
||||
const entry = _resolveRuntimeTier(config, tier);
|
||||
return entry?.reasoning_effort || null;
|
||||
// Step 4: effort.default
|
||||
if (effortCfg) {
|
||||
const d = effortCfg.default;
|
||||
if (typeof d === 'string' && EFFORT_SET.has(d)) return d;
|
||||
}
|
||||
|
||||
// Step 5: hardcoded default
|
||||
return 'high';
|
||||
}
|
||||
|
||||
/**
|
||||
* #443 — Resolve fast_mode boolean for (cwd, agentType).
|
||||
*
|
||||
* Accepts ONLY real booleans at each level. Strings like "true" are NOT accepted
|
||||
* and fall through.
|
||||
*
|
||||
* Precedence:
|
||||
* 1. opts.override (typeof boolean)
|
||||
* 2. config.fast_mode.agent_overrides[agentType] (boolean)
|
||||
* 3. config.fast_mode.routing_tier_defaults[ AGENT_DEFAULT_TIERS[agentType] ] (agent known + boolean)
|
||||
* 4. config.fast_mode.enabled (boolean)
|
||||
* 5. false
|
||||
*
|
||||
* @param {string} cwd
|
||||
* @param {string} agentType
|
||||
* @param {{ override?: boolean }} [opts]
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function resolveFastModeInternal(cwd, agentType, opts) {
|
||||
// Step 1: invocation override
|
||||
if (opts && typeof opts.override === 'boolean') {
|
||||
return opts.override;
|
||||
}
|
||||
|
||||
const config = loadConfig(cwd);
|
||||
const fmCfg = (config.fast_mode && typeof config.fast_mode === 'object' && !Array.isArray(config.fast_mode))
|
||||
? config.fast_mode
|
||||
: null;
|
||||
|
||||
// Step 2: agent_overrides
|
||||
if (fmCfg) {
|
||||
const ao = fmCfg.agent_overrides;
|
||||
if (ao && typeof ao === 'object' && !Array.isArray(ao)) {
|
||||
const v = ao[agentType];
|
||||
if (typeof v === 'boolean') return v;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: routing_tier_defaults by agent's default tier.
|
||||
// Manifest tier defaults are only used when there is no fast_mode config block at all
|
||||
// (fmCfg === null). When the user explicitly set a fast_mode block (even with just
|
||||
// `enabled`), manifest routing_tier_defaults do not fire — we fall through to enabled (step 4).
|
||||
// This ensures `fast_mode: { enabled: true }` works intuitively without the user having
|
||||
// to also spell out all three tier defaults.
|
||||
const agentTier = AGENT_DEFAULT_TIERS[agentType];
|
||||
if (agentTier) {
|
||||
if (fmCfg && fmCfg.routing_tier_defaults &&
|
||||
typeof fmCfg.routing_tier_defaults === 'object' &&
|
||||
!Array.isArray(fmCfg.routing_tier_defaults)) {
|
||||
// User provided routing_tier_defaults — honor them
|
||||
const v = fmCfg.routing_tier_defaults[agentTier];
|
||||
if (typeof v === 'boolean') return v;
|
||||
} else if (!fmCfg) {
|
||||
// No fast_mode config at all — use manifest defaults for tier
|
||||
const manifestDefaults = CANONICAL_CONFIG_DEFAULTS.fast_mode?.routing_tier_defaults;
|
||||
if (manifestDefaults && typeof manifestDefaults === 'object') {
|
||||
const v = manifestDefaults[agentTier];
|
||||
if (typeof v === 'boolean') return v;
|
||||
}
|
||||
}
|
||||
// else: fmCfg exists but no routing_tier_defaults — fall through to enabled
|
||||
}
|
||||
|
||||
// Step 4: fast_mode.enabled
|
||||
if (fmCfg && typeof fmCfg.enabled === 'boolean') {
|
||||
return fmCfg.enabled;
|
||||
}
|
||||
|
||||
// Step 5: hardcoded default
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* #443 — Resolve effort for a dynamic-routing attempt (with escalation).
|
||||
*
|
||||
* MUST NOT modify resolveModelForTier behavior.
|
||||
* base = resolveEffortInternal(cwd, agentType).
|
||||
* If config.dynamic_routing missing/enabled!==true OR escalate_on_failure===false
|
||||
* -> return base (attempt ignored).
|
||||
* Else: effectiveAttempt = min(max(0, attempt), max_escalations).
|
||||
* Walk nextEffort effectiveAttempt times from base, clamp at 'max'.
|
||||
*
|
||||
* @param {string} cwd
|
||||
* @param {string} agentType
|
||||
* @param {number} [attempt=0]
|
||||
* @returns {string}
|
||||
*/
|
||||
function resolveEffortForTier(cwd, agentType, attempt) {
|
||||
const base = resolveEffortInternal(cwd, agentType);
|
||||
|
||||
const config = loadConfig(cwd);
|
||||
const dr = config.dynamic_routing;
|
||||
if (!dr || typeof dr !== 'object' || dr.enabled !== true) {
|
||||
return base;
|
||||
}
|
||||
if (dr.escalate_on_failure === false) {
|
||||
return base;
|
||||
}
|
||||
|
||||
const maxEscalations = Number.isInteger(dr.max_escalations) && dr.max_escalations >= 0
|
||||
? dr.max_escalations
|
||||
: 1;
|
||||
|
||||
const attemptN = Number.isInteger(attempt) && attempt > 0 ? attempt : 0;
|
||||
const effectiveAttempt = Math.min(attemptN, maxEscalations);
|
||||
|
||||
let current = base;
|
||||
for (let i = 0; i < effectiveAttempt; i++) {
|
||||
const next = nextEffort(current);
|
||||
if (!next || next === current) break; // already at max
|
||||
current = next;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
// ─── Summary body helpers ─────────────────────────────────────────────────
|
||||
@@ -1913,9 +2062,15 @@ module.exports = {
|
||||
getRoadmapPhaseInternal,
|
||||
resolveModelInternal,
|
||||
resolveModelForTier,
|
||||
resolveReasoningEffortInternal,
|
||||
resolveEffortInternal,
|
||||
resolveFastModeInternal,
|
||||
resolveEffortForTier,
|
||||
VALID_EFFORTS,
|
||||
EFFORT_SET,
|
||||
nextEffort,
|
||||
RUNTIME_PROFILE_MAP,
|
||||
RUNTIMES_WITH_REASONING_EFFORT,
|
||||
RUNTIMES_WITH_FAST_MODE,
|
||||
KNOWN_RUNTIMES,
|
||||
RUNTIME_OVERRIDE_TIERS,
|
||||
resolveTierEntry,
|
||||
|
||||
@@ -118,6 +118,79 @@ function getAgentToModelMapForProfile(normalizedProfile) {
|
||||
return out;
|
||||
}
|
||||
|
||||
// ─── Effort rendering ────────────────────────────────────────────────────────
|
||||
//
|
||||
// Universal effort ladder: minimal < low < medium < high < xhigh < max
|
||||
//
|
||||
// Each runtime supports a subset. The unique tails must be clamped when emitting
|
||||
// to a runtime that does not support them:
|
||||
// - 'max' is Anthropic-only: Codex does not support it -> clamp to 'xhigh'
|
||||
// - 'minimal' is Codex-only: Claude does not support it -> clamp to 'low'
|
||||
//
|
||||
// Rendering maps the universal effort string to the runtime's native parameter.
|
||||
|
||||
const EFFORT_RENDERING = {
|
||||
// Claude Code subagent effort: output_config.effort frontmatter key /
|
||||
// CLAUDE_CODE_EFFORT_LEVEL env. Supports: low, medium, high, xhigh, max.
|
||||
// Does NOT support 'minimal' (Codex-only) -> clamp to 'low'.
|
||||
claude: {
|
||||
param: 'output_config.effort',
|
||||
channel: 'frontmatter',
|
||||
supported: new Set(['low', 'medium', 'high', 'xhigh', 'max']),
|
||||
clamp(level) {
|
||||
if (level === 'minimal') return 'low';
|
||||
return level;
|
||||
},
|
||||
},
|
||||
// Codex Responses API reasoning.effort. Supports: minimal, low, medium, high, xhigh.
|
||||
// Does NOT support 'max' (Anthropic-only) -> clamp to 'xhigh'.
|
||||
codex: {
|
||||
param: 'model_reasoning_effort',
|
||||
channel: 'api',
|
||||
supported: new Set(['minimal', 'low', 'medium', 'high', 'xhigh']),
|
||||
clamp(level) {
|
||||
if (level === 'max') return 'xhigh';
|
||||
return level;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Render a universal effort string for a specific runtime.
|
||||
*
|
||||
* Returns { value (clamped), param, channel } where:
|
||||
* - value: the clamped effort string safe to pass to the runtime
|
||||
* - param: the native parameter name (e.g. 'output_config.effort')
|
||||
* - channel: how the value is propagated ('frontmatter', 'api', null)
|
||||
*
|
||||
* Unknown runtimes return { value: universalEffort, param: null, channel: null }
|
||||
* so callers can always read .value safely.
|
||||
*/
|
||||
function renderEffortForRuntime(runtime, universalEffort) {
|
||||
const spec = EFFORT_RENDERING[runtime];
|
||||
if (!spec) {
|
||||
return { value: universalEffort, param: null, channel: null };
|
||||
}
|
||||
return {
|
||||
value: spec.clamp(universalEffort),
|
||||
param: spec.param,
|
||||
channel: spec.channel,
|
||||
};
|
||||
}
|
||||
|
||||
// ─── Fast mode propagation ───────────────────────────────────────────────────
|
||||
//
|
||||
// RUNTIMES_WITH_FAST_MODE is the set of runtimes where fast_mode=true can be
|
||||
// propagated to a SPAWNED SUBAGENT via a native mechanism.
|
||||
//
|
||||
// Claude Code has NO per-subagent fast-mode mechanism — /fast is a session-level
|
||||
// toggle only. Emitting a `fast_mode: true` frontmatter key on a Claude subagent
|
||||
// would be a SILENT NO-OP, which is why 'claude' is deliberately excluded here.
|
||||
//
|
||||
// Only API-direct runtimes ('api') accept a speed:"fast" field in the request.
|
||||
// Codex and other runtimes do not expose per-call fast_mode either.
|
||||
const RUNTIMES_WITH_FAST_MODE = new Set(['api']);
|
||||
|
||||
module.exports = {
|
||||
catalog,
|
||||
MODEL_PROFILES,
|
||||
@@ -133,4 +206,7 @@ module.exports = {
|
||||
nextTier,
|
||||
formatAgentToModelMapAsTable,
|
||||
getAgentToModelMapForProfile,
|
||||
EFFORT_RENDERING,
|
||||
renderEffortForRuntime,
|
||||
RUNTIMES_WITH_FAST_MODE,
|
||||
};
|
||||
|
||||
@@ -10,6 +10,9 @@ const {
|
||||
nextTier,
|
||||
formatAgentToModelMapAsTable,
|
||||
getAgentToModelMapForProfile,
|
||||
EFFORT_RENDERING,
|
||||
renderEffortForRuntime,
|
||||
RUNTIMES_WITH_FAST_MODE,
|
||||
} = require('./model-catalog.cjs');
|
||||
|
||||
module.exports = {
|
||||
@@ -22,4 +25,7 @@ module.exports = {
|
||||
nextTier,
|
||||
formatAgentToModelMapAsTable,
|
||||
getAgentToModelMapForProfile,
|
||||
EFFORT_RENDERING,
|
||||
renderEffortForRuntime,
|
||||
RUNTIMES_WITH_FAST_MODE,
|
||||
};
|
||||
|
||||
@@ -71,5 +71,23 @@
|
||||
"graphify": {
|
||||
"auto_update": false
|
||||
},
|
||||
"agent_skills": {}
|
||||
"agent_skills": {},
|
||||
"effort": {
|
||||
"default": "high",
|
||||
"routing_tier_defaults": {
|
||||
"light": "low",
|
||||
"standard": "high",
|
||||
"heavy": "xhigh"
|
||||
},
|
||||
"agent_overrides": {}
|
||||
},
|
||||
"fast_mode": {
|
||||
"enabled": false,
|
||||
"routing_tier_defaults": {
|
||||
"light": true,
|
||||
"standard": false,
|
||||
"heavy": false
|
||||
},
|
||||
"agent_overrides": {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"_comment": "Canonical schema manifest for valid config key paths. validKeys is the union of CJS config-schema.cjs and SDK query/config-schema.ts (they are enforced set-equal by tests/config-schema-sdk-parity.test.cjs). runtimeStateKeys mirrors RUNTIME_STATE_KEYS. dynamicKeyPatterns mirrors DYNAMIC_KEY_PATTERNS from SDK (which includes the canonical 'source' regex strings). The .test function is reconstructed at runtime from new RegExp(source).",
|
||||
"_comment": "Canonical schema manifest for valid config key paths. This manifest is the single CJS source of truth for valid config keys; dynamicKeyPatterns source strings are recompiled to RegExp at runtime by config-schema.cjs. runtimeStateKeys mirrors RUNTIME_STATE_KEYS.",
|
||||
"validKeys": [
|
||||
"mode",
|
||||
"granularity",
|
||||
@@ -96,7 +96,9 @@
|
||||
"claude_md_path",
|
||||
"claude_md_assembly.mode",
|
||||
"runtime",
|
||||
"resolve_model_ids"
|
||||
"resolve_model_ids",
|
||||
"effort.default",
|
||||
"fast_mode.enabled"
|
||||
],
|
||||
"runtimeStateKeys": [
|
||||
"workflow._auto_chain_active"
|
||||
@@ -142,6 +144,26 @@
|
||||
"source": "^model_overrides\\.[a-zA-Z0-9_-]+$",
|
||||
"description": "model_overrides.<agent-id>"
|
||||
},
|
||||
{
|
||||
"topLevel": "effort",
|
||||
"source": "^effort\\.routing_tier_defaults\\.(light|standard|heavy)$",
|
||||
"description": "effort.routing_tier_defaults.<light|standard|heavy>"
|
||||
},
|
||||
{
|
||||
"topLevel": "effort",
|
||||
"source": "^effort\\.agent_overrides\\.[a-zA-Z0-9_-]+$",
|
||||
"description": "effort.agent_overrides.<agent-id>"
|
||||
},
|
||||
{
|
||||
"topLevel": "fast_mode",
|
||||
"source": "^fast_mode\\.routing_tier_defaults\\.(light|standard|heavy)$",
|
||||
"description": "fast_mode.routing_tier_defaults.<light|standard|heavy>"
|
||||
},
|
||||
{
|
||||
"topLevel": "fast_mode",
|
||||
"source": "^fast_mode\\.agent_overrides\\.[a-zA-Z0-9_-]+$",
|
||||
"description": "fast_mode.agent_overrides.<agent-id>"
|
||||
},
|
||||
{
|
||||
"topLevel": "review",
|
||||
"source": "^review\\.max_prompt_tokens_per_reviewer\\.[a-zA-Z0-9_-]+$",
|
||||
|
||||
@@ -584,6 +584,16 @@ Display:
|
||||
| model_profile_overrides.<runtime>.opus | {model/built-in/null} |
|
||||
| model_profile_overrides.<runtime>.sonnet | {model/built-in/null} |
|
||||
| model_profile_overrides.<runtime>.haiku | {model/built-in/null} |
|
||||
| effort.default | {low/medium/high/xhigh/max} |
|
||||
| effort.routing_tier_defaults.light | {low/medium/high/xhigh/max} |
|
||||
| effort.routing_tier_defaults.standard | {low/medium/high/xhigh/max} |
|
||||
| effort.routing_tier_defaults.heavy | {low/medium/high/xhigh/max} |
|
||||
| effort.agent_overrides.<agent-id> | {low/medium/high/xhigh/max} |
|
||||
| fast_mode.enabled | {true/false} |
|
||||
| fast_mode.routing_tier_defaults.light | {true/false} |
|
||||
| fast_mode.routing_tier_defaults.standard | {true/false} |
|
||||
| fast_mode.routing_tier_defaults.heavy | {true/false} |
|
||||
| fast_mode.agent_overrides.<agent-id> | {true/false} |
|
||||
|
||||
These settings apply to future /gsd:plan-phase, /gsd:execute-phase, /gsd:discuss-phase,
|
||||
and /gsd:ship runs.
|
||||
|
||||
Reference in New Issue
Block a user