Skip to content

Support the OpenCode 2 plugin API for Databricks auth refresh - #906

Open
mats16 wants to merge 7 commits into
databricks:mainfrom
mats16:fix/opencode-v2-auth-plugin
Open

mats16 wants to merge 7 commits into
databricks:mainfrom
mats16:fix/opencode-v2-auth-plugin

Conversation

@mats16

@mats16 mats16 commented Sep 30, 2026

Copy link
Copy Markdown

What

The generated OpenCode auth plugin (ucode-auth.js) now also supports the OpenCode 2 plugin API, so the Databricks token keeps being refreshed on OpenCode 2. The installed OpenCode major version picks the entrypoint. OpenCode 1.x output is unchanged.

Why

OpenCode 2 rejects plugin modules without a default { id, setup } export, so the existing plugin failed to load:

PluginModule.LoadError: Plugin must export a default definition with an id and an effect or setup function.

OpenCode 2 also has no config hook for installing a provider fetch. So once the token written at configure time expired, it was never refreshed.

How

  • render_auth_plugin is split into a shared prelude and a per-version entrypoint. The prelude holds token caching, the ug auth-token call, and the single-flighted refresh. write_auth_plugin writes the V2 entrypoint when agent_version("opencode") reports major version 2 or later. Otherwise, including when the version is unknown, it writes the existing V1 entrypoint.
  • The V2 entrypoint registers session hooks for each ucode-managed provider:
    • http.request refreshes the token when it is missing or about to expire, then sets Authorization.
    • http.response records the Authorization that got a 401.
    • retry retries a 401 once, on the first retry (attempt 2), because OpenCode does not retry 401s by default. It refreshes only when the rejected token is still the current one. This matches the V1 accessToken === attemptedToken guard, so concurrent 401s on the same token run ug auth-token --force-refresh only once.
  • The plugin stays in the plugin/ directory, which OpenCode 2 still discovers.

Testing

  • .venv/bin/pytest tests/test_agent_opencode.py: 66 passed.
  • ruff check and ruff format --check on both changed files, and ty check src/ucode/agents/opencode.py: all clean.
  • The rendered V2 plugin passes node --check.
  • Ran the rendered V2 plugin's hooks in node with a stub auth command:
    • Two concurrent 401s on the same token trigger one refresh. Before this change they triggered two.
    • A 401 on the refreshed token triggers another refresh.
    • attempt 3 and later are not retried.
  • The full suite has 5 failures: 2 in tests/test_claude_smart_routing_v2.py and 3 in tests/test_e2e_user_agent.py. The same 5 fail on main, so they are unrelated to this change.

OpenCode 2 rejects plugin modules without a default { id, setup } export and has no config hook for injecting a provider fetch, so the generated ucode-auth.js failed to load and the configured token was never refreshed. Render an OpenCode 2 entrypoint that attaches a fresh bearer through session http.request hooks and retries a 401 once through the retry hook. OpenCode 1.x output is unchanged.
Concurrent requests rejected with the same token each ran `ug auth-token --force-refresh` from the retry hook. Record the Authorization rejected with a 401 in an http.response hook and refresh only when it is still the current token, matching the OpenCode 1.x fetch guard.
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.

1 participant