| Registration | Cross-device migration, fresh machine setup, or user asks for cross-vendor MCP install | Use python3 ~/.agents/install_mcps.py opencode_secure --scope user --vendor codex for Codex-only restore; use --all or additional --vendor flags only for intentional broader rollout | codex mcp list --json shows opencode_secure enabled with the entry script under ~/.agents/skills/opencode-secure-mcp/scripts/ | Fall back to scripts/register_opencode_secure_mcp.sh for Codex-only registration, then repair the top-level installer/manifest | branch |
| Probe | Caller omits model, or passes auto / default / stable | Resolve to the first validated entry in ~/.local/state/opencode/model.json recent; return the chosen full id as resolved_model with resolution_source=recent_default | MCP result shows the expected resolved_model, and stdout/stderr/artifacts confirm opencode ran with that full id | If there is no local validated candidate, return a structured model_resolution_failed error instead of guessing | replace |
| Probe | Caller passes a short alias such as kimi or a provider-less validated suffix such as moonshot/kimi-k2.6 | Match the selector against validated recent/variant entries server-side, then pass the full provider/model id to the wrapper | MCP result shows resolved_model plus resolution_source=alias_builtin or recent_match, and opencode logs run on that full id | If there is no unambiguous local match, return candidate_models from the error payload and let the caller choose a pin | replace |
| Probe | Caller passes a full provider/model string | Pass it through unchanged and record resolution_source=explicit | MCP result echoes the same resolved_model, and the command line/artifacts use that full id | If the explicit full id fails live after launch, classify it as provider/runtime failure rather than resolver failure | continue |
| Probe | Provider returns quota, billing, or token-limit errors after the model has already resolved to the intended display name | Keep the validated full model string unchanged and treat the result as a provider quota/billing blocker; retry later instead of changing model ids | stderr/artifacts show the intended display name such as tongyi/deepseek-v4-flash before the quota failure text | Only revisit model selection when the error is model-resolution related, such as ProviderModelNotFoundError | branch |
| Probe | opencode_run_task returns error.code=timeout with job_id and timeout_context.saw_output=true | Treat the sync timeout as a handoff; keep the job alive and poll with opencode_get_task / opencode_collect_artifacts | Job later reaches succeeded or failed, and artifact tails show model progress | Cancel the job only if it keeps running without converging or exceeds the caller's patience budget | branch |
| Probe | opencode_run_task returns error.code=timeout with timeout_context.saw_output=false | Suspect wrapper/startup/local orchestration first; inspect stderr/stdout tails and compare with direct wrapper execution | Direct wrapper probe reproduces or avoids the silent startup | Check stdio transport and wrapper startup assumptions before blaming the provider | branch |
| Probe | Long research, online source lookup, multi-agent, or complex prompt is expected to exceed the outer MCP tool-call wait window | Start with opencode_submit_task instead of synchronous opencode_run_task, set persist_artifacts=true, then poll the returned job_id | opencode_get_task reports running/terminal state and opencode_collect_artifacts shows progress or final output | If a prior synchronous tool call timed out without returning a job_id, resubmit once via opencode_submit_task and treat the original call as unknown/untracked unless artifacts identify it | replace |
| Validation | Background job is still running after sync handoff | Keep using opencode_get_task and opencode_collect_artifacts instead of re-submitting the same prompt immediately | Artifact tail changes or final status arrives | Cancel stale work and retry with adjusted timeout only after capturing evidence | replace |
| Recovery | MCP server exits or is OOM-killed mid-job | Restart the server and re-query the same job_id first; rely on durable completion evidence under the state directory instead of assuming the task was lost | opencode_get_task returns a terminal status or updated artifact tails for the original job_id | If there is no completion evidence and the process is gone, treat the job as terminal finished_unknown, inspect artifacts, then decide whether to retry | branch |