Skip to content

fix(providers): keep OpenCode's internal plugins so OAuth logins work - #810

Merged
rng1995 merged 2 commits into
NVIDIA:mainfrom
elliottwaves-20:fix/809-opencode-oauth-plugins
Oct 14, 2026
Merged

rng1995 merged 2 commits into
NVIDIA:mainfrom
elliottwaves-20:fix/809-opencode-oauth-plugins

Conversation

@elliottwaves-20

Copy link
Copy Markdown
Contributor

Summary

Fixes #809. With the opencode_cli provider, OpenCode logins that use OAuth (ChatGPT Plus/Pro via opencode auth login → OpenAI, GitHub Copilot, GitLab, …) fail on every call with Unexpected server error. The sandbox env sets OPENCODE_DISABLE_DEFAULT_PLUGINS=1, which in OpenCode 1.18.33 skips all internal plugins — and the OAuth logins are internal plugins. This PR stops setting that one variable.

Why this keeps the isolation

At OpenCode v1.18.33, packages/opencode/src/plugin/index.ts loads internalPlugins() only when the flag is unset:

for (const plugin of flags.disableDefaultPlugins ? [] : internalPlugins(flags)) {

The list is CodexAuthPlugin, CopilotAuthPlugin, ModalPlugin, GitlabAuthPlugin, PoeAuthPlugin, CloudflareWorkersAuthPlugin, CloudflareAIGatewayAuthPlugin, AzureAuthPlugin, DigitalOceanAuthPlugin, SnowflakeCortexAuthPlugin, XaiAuthPlugin, CerebrasPlugin. None of them registers a tool hook; they provide auth plus chat.headers / chat.params for their own provider. Everything else stays as it is:

  • tool access: per-invocation deny-all agent + OPENCODE_PERMISSION (unchanged)
  • external / config plugins: --pure and OPENCODE_PURE=1 (unchanged, still asserted in the test)
  • project config, external skills, Claude Code import, auto-share, autoupdate, LSP download: unchanged

The _prepare_opencode_env docstring already states the intent ("The real OpenCode authentication store remains available so this CLI provider can use the user's existing login"); it now also says why internal plugins stay enabled.

Changes

Testing

  • pytest tests/provider/ → 43 passed, 3 skipped (the changed assertion failed before the fix).
  • ruff check / ruff format --check on both files: clean.
  • Live, Windows 11, OpenCode 1.18.33 logged in with ChatGPT, model openai/gpt-6-luna, using SkillSpector's own _build_opencode_argv + _prepare_opencode_env: before the fix exit 1, "Unexpected server error"; after the fix exit 0, the model answers.

elliottwaves-20 and others added 2 commits October 8, 2026 23:59
OPENCODE_DISABLE_DEFAULT_PLUGINS=1 skips every internal OpenCode plugin.
In OpenCode 1.18.33 those plugins implement the OAuth logins (ChatGPT via
CodexAuthPlugin, Copilot, GitLab, ...) and only register auth and
chat.headers/chat.params hooks, no tools. With the flag set, an OAuth
login is never attached and every call fails with "Unexpected server
error", although the sandbox is meant to keep the user's existing login
usable. Tool access stays denied by the deny-all agent and
OPENCODE_PERMISSION, and external plugins stay off through --pure /
OPENCODE_PURE.

Fixes NVIDIA#809

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: elliottwaves-20 <pail1217@web.de>
…scrub

The _build_opencode_argv docstring still said the runner disables plugins;
only external plugins are off, the internal OAuth/provider plugins stay
enabled. The isolation test now also feeds an ambient
OPENCODE_DISABLE_DEFAULT_PLUGINS=1, which the sandbox must scrub so a user
environment cannot switch the OAuth logins back off.

Refs NVIDIA#809

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: elliottwaves-20 <pail1217@web.de>
@elliottwaves-20

Copy link
Copy Markdown
Contributor Author

Follow-up after an independent review of this change (pushed as 22cec46):

  • The _build_opencode_argv docstring now says external plugins are disabled and the internal OAuth/provider plugins stay enabled.
  • test_policy_overrides_hostile_ambient_configuration also feeds an ambient OPENCODE_DISABLE_DEFAULT_PLUGINS=1 and asserts it is scrubbed, so a user environment cannot switch the OAuth logins back off.

Verified against OpenCode v1.18.33 (plus opencode-gitlab-auth@2.1.0, opencode-poe-auth@0.0.1): the 12 internal plugins register only auth, provider, chat.params, chat.headers, experimental.provider.small_model (Codex also event/dispose) — no tool, tool.execute.*, permission.ask, shell.env, config or command.execute.before, and none uses the Bun shell. Interactive login flows run only through authorize() (opencode auth login), never opencode run.

One known limitation, not addressed here so the PR stays minimal: with OAuth working, a token refresh writes the real auth.json (expected — rotating refresh tokens must be persisted, and the docstring keeps the auth store available on purpose). OpenCode deduplicates refreshes only within one process, so if the access token expires while several opencode run processes start in parallel (DEFAULT_MAX_LLM_CONCURRENCY), they can refresh concurrently with the same rotating refresh token. A follow-up could run one serialized call before the fan-out for opencode_cli. Happy to open a separate issue/PR if you want that.

@elliottwaves-20

Copy link
Copy Markdown
Contributor Author

End-to-end check with a real ChatGPT OAuth login (OpenCode 1.18.33, openai/gpt-6-luna, Windows). It runs the same get_chat_model().invoke(...) path a scan uses, through opencode_cli:

  • This branch (22cec46): the call succeeds and the model replies OK.
  • main: the call fails with AgentCLIError: opencode exited with code 1; stderr=''. OpenCode's stdout shows {"type":"error",...,"error":{"name":"UnknownError","data":{"message":"Unexpected server error. ..."}}}.

The same login works with a plain opencode run -m openai/gpt-6-luna outside the sandbox, so the only difference is OPENCODE_DISABLE_DEFAULT_PLUGINS.

A side note for anyone patching this downstream instead of waiting for the merge: run_agent_cli reads the builder from _REGISTRY["opencode"].prepare_env, which is bound at import time. Replacing only the module-level _prepare_opencode_env affects the auth check but not inference. This PR changes the function itself, so it is not affected.

@rng1995 rng1995 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[SkillSpector Review]

@elliottwaves-20 thank you for the precise root cause, the source-level evidence, and the live before/after check with a real ChatGPT login.

Value and readiness

OPENCODE_DISABLE_DEFAULT_PLUGINS=1 makes OpenCode 1.18.33 skip its internal plugins, and those plugins implement the OAuth logins (ChatGPT, Copilot, GitLab, and others), so opencode_cli failed on every call for OAuth users. Dropping only that variable restores the documented intent ("use the user's existing login") and keeps the isolation:

  • External plugins stay off through --pure / OPENCODE_PURE=1, and the policy preflight still requires a resolved plugin: [].
  • Tools stay denied by the per-invocation deny-all agent and OPENCODE_PERMISSION.
  • An inherited OPENCODE_DISABLE_DEFAULT_PLUGINS is dropped by the allowlist scrub, and the test now pins that.

Ready for maintainer review.

Material findings

None blocking. Optional:

  1. Optional: leaving internal plugins enabled is safe only while none of them registers tool, tool.execute.*, permission.ask, shell.env or config hooks. That holds at 1.18.33, but nothing re-checks it when _OPENCODE_SUPPORTED_VERSION moves. A short note next to the pin in _agent_cli.py would carry this into the bump checklist.
  2. Optional: the refresh-token race you describe deserves a tracking issue. With OAuth now working, parallel opencode run processes can rotate the same refresh token. #808 would lower the CLI default fan-out to 2, which narrows the race but does not remove it.

PIC tradeoffs

  • All 12 internal plugins now initialize in every sandboxed call. Two of them (opencode-gitlab-auth, opencode-poe-auth) are third-party npm packages compiled into the OpenCode binary, not fetched at runtime, and their hooks only act for their own providers. Accepting them is the price of OAuth support.

Verification and gaps

  • Read the full diff, _prepare_opencode_env, _preflight_opencode_policy and the _RUNTIME_ENV_NAMES allowlist at the head.
  • Read OpenCode v1.18.33 (anomalyco/opencode) plugin/index.ts: internal plugins are "directly imported (not installed from npm)" and gated only by flags.disableDefaultPlugins, while external plugins are gated by flags.pure. Also read runtime-flags.ts and the nine in-tree plugin files (codex, copilot, modal, cloudflare, azure, digitalocean, xai, cerebras, snowflake-cortex). They register auth, provider and chat.params / chat.headers only; I found no tool, shell, permission or config hooks and no process spawning.
  • Not inspected: the two npm-sourced plugins. I did not run OpenCode.
  • All six CI checks pass, and the PR merges cleanly into main.

@rng1995
rng1995 merged commit 6145ace into NVIDIA:main Oct 14, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

opencode_cli: OPENCODE_DISABLE_DEFAULT_PLUGINS breaks OAuth logins (ChatGPT, Copilot)

2 participants