Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ On 2026-10-15 the dual-export primary plugins loaded once each and the turn-end
| Interrupt | Double Escape; it is known to be flaky while a long shell command runs, so use `../../../bin/fm-control.sh <task-id> relaunch` for a wedged pane. |
| Skill invocation | No separate verified form beyond normal slash-command behavior; use natural language when the exact command is uncertain. |
| Resume | Relaunch with `--continue` to resume the most recent session for the current directory, then send the next instruction after the TUI is ready because `--prompt` does not auto-submit alongside `--continue`. |
| Model flag | Version-sensitive, keyed to the detected major. OpenCode 2.0.21 has no interactive top-level `--model` (`opencode run --model` remains only on the non-interactive path), so Firstmate pins the interactive model through `OPENCODE_CONFIG_CONTENT` as global `model` plus `agent.build.model`. OpenCode 1.x still takes the interactive `--model` flag, so there Firstmate passes `--model` and does not write the 2.x global/build model pin. An undetected or unparseable version (opencode unresolvable, or `--version` without a parseable x.y.z) falls back to the 1.x shape, so only a parsed major >= 2 takes the 2.x pin. |
| Effort flag | None for Firstmate's interactive `opencode --prompt` launch; `opencode run` has model variant syntax, but that is not this path. The effort instead rides the launch's `OPENCODE_CONFIG_CONTENT` JSON as the `build` agent's `variant` keyed to the resolved model. It is emitted only when the resolved model's provider is known to expose that effort as a variant (`anthropic/*`: high, max; `openai/*`: low, medium, high, xhigh); with no model resolved, another provider, or an effort outside its family's list, the variant is omitted. On 2.x the global/build model pin remains even when the variant is omitted; on 1.x, where the model arrives via `--model`, omitting the variant leaves the permission-only config. |
| Model selection | When `--model` is requested, Firstmate probes the resolved OpenCode executable's version and top-level help, then uses the advertised top-level `--model` flag when present or writes `.opencode/opencode.jsonc` in the worker's worktree when absent. It refuses if the version/help surface cannot be read, never overwrites a project-owned config unless that config already selects the requested model, and verifies the selected model through OpenCode's config readback before launch and the worker session's first assistant message after launch. The worktree pin is task-owned and retired with the task; an unowned or mismatching project config is left untouched. `provider/model#variant` is unsupported; use `provider/model` with `--effort`. |
| Effort | Effort is supplied with Firstmate's `--effort` option, not inline in the model identifier. For known provider variants (`anthropic/*`: high, max; `openai/*`: low, medium, high, xhigh), Firstmate includes the variant beside the model in project-config selection or in the launch config for a flag-based build. For other providers or unsupported effort values it omits the variant rather than guessing. |
| Model discovery | Run `opencode models` to list available provider/model identifiers; OpenCode 2.0.21 no longer accepts a provider positional argument. |
| Trust dialog | None. |
| Marker | None; OpenCode publishes no identity marker, so `../../../bin/fm-harness.sh` identifies it from process ancestry. |
Expand Down
25 changes: 24 additions & 1 deletion bin/fm-control-lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,21 @@ fm_control_harness_wiring_paths() { # <harness> <worktree> <state-dir> <id> [ki
printf '%s\n' "$wt/.claude/settings.local.json"
fi
;;
opencode) printf '%s\n' "$wt/.opencode/plugins/fm-busy-state.js" ;;
opencode)
printf '%s\n' "$wt/.opencode/plugins/fm-busy-state.js"
# A build without the top-level --model flag is pinned through the
# worktree's project configuration, and that file is retired only while
# this task's own record names it: a project's own
# .opencode/opencode.jsonc predates firstmate and is never its to remove.
# A stale pin must not outlive the task either, because a returned pool
# slot is reused and a later launch that names no model would silently
# inherit it.
if [ -f "$state/$id.opencode-model-config" ] &&
[ "$(cat "$state/$id.opencode-model-config" 2>/dev/null)" = "$wt/.opencode/opencode.jsonc" ]; then
printf '%s\n' "$wt/.opencode/opencode.jsonc"
printf '%s\n' "$state/$id.opencode-model-config"
fi
;;
pi|pi-signed) printf '%s\n' "$state/$id.pi-ext.ts" ;;
omp) printf '%s\n' "$state/$id.omp-ext.ts" ;;
grok)
Expand Down Expand Up @@ -622,3 +636,12 @@ fm_control_harness_turnend_auth_path() { # <harness> <token>
*) return 0 ;;
esac
}

fm_control_retire_opencode_model_pin() {
local id=$1 state=$3 record="$3/$1.opencode-model-config" pin="$2/.opencode/opencode.jsonc" owned
[ -e "$record" ] || [ -L "$record" ] || return 0
owned=$(cat "$record") || return 1
[ "$owned" = "$pin" ] || return 1
rm -f -- "$pin" || return 1
rm -f -- "$state/$id.opencode-model-config"
}
Loading
Loading