@@ -40,6 +40,13 @@ REVIEWER_MODEL=""
4040PLANNER_TIER=" "
4141PLANNER_PROVIDER=" "
4242PLANNER_MODEL=" "
43+ # v0.10.4 small_model: top-level fast/cheap fallback distinct from
44+ # agent.plan.model. Community signal: 35-40% of configs set this. Same
45+ # triplet shape, writes the top-level `small_model` field plus the
46+ # small-model provider block (deep-merged with coder/reviewer/planner).
47+ SMALL_TIER=" "
48+ SMALL_PROVIDER=" "
49+ SMALL_MODEL=" "
4350# v0.10.1 Per-agent permission sandboxing. Boolean flags inject canonical
4451# permission.write blocks for the two highest-signal agents from May-2026
4552# community-patterns research (9/15 configs use this): test-writer scoped
@@ -76,6 +83,12 @@ while [ $# -gt 0 ]; do
7683 --planner-provider=* ) PLANNER_PROVIDER=" ${1#* =} " ;;
7784 --planner-model) shift ; PLANNER_MODEL=" ${1:- } " ;;
7885 --planner-model=* ) PLANNER_MODEL=" ${1#* =} " ;;
86+ --small-tier) shift ; SMALL_TIER=" ${1:- } " ;;
87+ --small-tier=* ) SMALL_TIER=" ${1#* =} " ;;
88+ --small-provider) shift ; SMALL_PROVIDER=" ${1:- } " ;;
89+ --small-provider=* ) SMALL_PROVIDER=" ${1#* =} " ;;
90+ --small-model) shift ; SMALL_MODEL=" ${1:- } " ;;
91+ --small-model=* ) SMALL_MODEL=" ${1#* =} " ;;
7992 --sandbox-test-writer) SANDBOX_TEST_WRITER=1 ;;
8093 --sandbox-docs) SANDBOX_DOCS=1 ;;
8194 -h|--help) usage; exit 0 ;;
@@ -110,6 +123,16 @@ if [ "$PL_SET" -ne 0 ] && [ "$PL_SET" -ne 3 ]; then
110123 exit 2
111124fi
112125
126+ # v0.10.4 small-model validation.
127+ SM_SET=0
128+ [ -n " $SMALL_TIER " ] && SM_SET=$(( SM_SET+ 1 ))
129+ [ -n " $SMALL_PROVIDER " ] && SM_SET=$(( SM_SET+ 1 ))
130+ [ -n " $SMALL_MODEL " ] && SM_SET=$(( SM_SET+ 1 ))
131+ if [ " $SM_SET " -ne 0 ] && [ " $SM_SET " -ne 3 ]; then
132+ echo " --small-tier / --small-provider / --small-model must all be set together (or none)" >&2
133+ exit 2
134+ fi
135+
113136CONFIG_PATH=" $TARGET_DIR /opencode.json"
114137
115138# Build the provider-specific fragment as a JSON string. We keep this in a
@@ -118,20 +141,23 @@ CONFIG_PATH="$TARGET_DIR/opencode.json"
118141node - " $TIER " " $PROVIDER " " $MODEL " " $CONFIG_PATH " " $FORCE " " $PRINT_ONLY " \
119142 " $REVIEWER_TIER " " $REVIEWER_PROVIDER " " $REVIEWER_MODEL " \
120143 " $SANDBOX_TEST_WRITER " " $SANDBOX_DOCS " \
121- " $PLANNER_TIER " " $PLANNER_PROVIDER " " $PLANNER_MODEL " << 'NODE '
144+ " $PLANNER_TIER " " $PLANNER_PROVIDER " " $PLANNER_MODEL " \
145+ " $SMALL_TIER " " $SMALL_PROVIDER " " $SMALL_MODEL " << 'NODE '
122146const fs = require("node:fs");
123147const [
124148 tier, providerArg, model, configPath, forceStr, printOnlyStr,
125149 reviewerTier, reviewerProviderArg, reviewerModel,
126150 sandboxTestWriterStr, sandboxDocsStr,
127151 plannerTier, plannerProviderArg, plannerModel,
152+ smallTier, smallProviderArg, smallModel,
128153] = process.argv.slice(2);
129154const force = forceStr === "1";
130155const printOnly = printOnlyStr === "1";
131156const mixedMode = Boolean(reviewerTier && reviewerProviderArg && reviewerModel);
132157const sandboxTestWriter = sandboxTestWriterStr === "1";
133158const sandboxDocs = sandboxDocsStr === "1";
134159const plannerMode = Boolean(plannerTier && plannerProviderArg && plannerModel);
160+ const smallMode = Boolean(smallTier && smallProviderArg && smallModel);
135161
136162// Canonical provider IDs. Detector emits user-friendly aliases; we accept both
137163// and emit the canonical OpenCode/models.dev ID in the written config so model
@@ -451,6 +477,17 @@ if (plannerMode) {
451477 });
452478}
453479
480+ // v0.10.4 small_model: top-level field (not nested under agent). Distinct
481+ // from agent.plan.model — small_model is OpenCode's cross-cutting hint
482+ // for "use this when the call is cheap" (title generation, summary
483+ // blurbs, etc.). 35-40% of community configs set this.
484+ if (smallMode) {
485+ const smallProvider = PROVIDER_ALIASES[smallProviderArg] || smallProviderArg;
486+ const smallFragment = fragmentFor(smallTier, smallProvider, smallModel);
487+ merged.provider = deepMerge(merged.provider, smallFragment.provider || {});
488+ merged.small_model = smallFragment.model;
489+ }
490+
454491// v0.10.1 Per-agent permission sandboxing. Each flag injects the canonical
455492// permission.write pattern for that agent — deep-merged so it composes
456493// with Mixed-Mode (--reviewer-*) and any user-set sibling fields. Patterns
@@ -489,7 +526,10 @@ function sortKeysCanonical(obj, topLevel = false) {
489526 const keys = Object.keys(obj);
490527 let ordered;
491528 if (topLevel) {
492- const preferred = ["$schema", "model", "provider"];
529+ // v0.10.4: small_model lives right after `model` per the joelhooks
530+ // and ppries community configs we surveyed — keeps the two top-level
531+ // pins visually adjacent.
532+ const preferred = ["$schema", "model", "small_model", "provider"];
493533 const front = preferred.filter((k) => keys.includes(k));
494534 const rest = keys.filter((k) => !preferred.includes(k)).sort();
495535 ordered = [...front, ...rest];
0 commit comments