Skip to content
Draft
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
10 changes: 8 additions & 2 deletions src/ucode/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -3278,6 +3278,12 @@ def _launch_tool(
# lookup via resolve_gemini_provider_model (below), and uses no family model map.
provider_models = None
picker_catalog = None
# Custom OAuth setup (e.g. Isaac) authenticates a separate CLI profile.
catalog_profile = (
state["custom_oauth"].get("profile")
if custom_oauth_cli_enabled(state.get("custom_oauth"))
else state.get("profile")
)
relayed = False
coding_agent_config_defaults = (
managed_claude_family_models(managed) or {}
Expand Down Expand Up @@ -3318,7 +3324,7 @@ def _launch_tool(
)
)
if should_fetch_claude_picker_catalog:
token = get_databricks_token(state["workspace"], state.get("profile"))
token = get_databricks_token(state["workspace"], catalog_profile)
picker_catalog = list_anthropic_model_catalog(
state["workspace"],
token,
Expand Down Expand Up @@ -3405,7 +3411,7 @@ def _launch_tool(
if tool == "claude" and managed is None and not provider and not parent_schema:
discovered = list_anthropic_model_catalog(
state["workspace"],
get_databricks_token(state["workspace"], state.get("profile")),
get_databricks_token(state["workspace"], catalog_profile),
)
if discovered.model_ids:
picker_catalog = claude_agent.default_model_picker_catalog(
Expand Down
5 changes: 5 additions & 0 deletions tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,11 @@ Codex browser-login component tests cover invalid refresh tokens, token-only std
network errors, and reuse of a concurrent sign-in within the timeout. Codex config
tests cover the helper flag; real browser consent and desktop behavior are not covered.

`test_cli.py` covers Claude catalog requests using the custom OAuth CLI profile,
including a stale ordinary profile, default discovery, model locations, and providers.
It also checks ordinary-profile selection when CLI custom OAuth is disabled.
These component tests replace authentication and gateway calls.

`test_mcp_web_search.py` and `test_agent_claude.py` cover custom OAuth search
registration, stale registration repair, SDK cache reuse/refresh, CLI profile
selection, and errors without browser consent through the MCP handler. These
Expand Down
5 changes: 5 additions & 0 deletions tests/integration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ absent; use `--help` for the startup-import probe.
Agent-specific bundled instruction selection and prompt/compaction hook content
are covered in `../test_skills.py`; those component checks do not run live agents.

`../test_cli.py` covers custom OAuth CLI profile selection for Claude's default,
model-location, and provider catalogs with authentication and gateway calls replaced.
It also checks the disabled mode. This does not establish live Isaac coverage or
custom OAuth startup with an expired ordinary profile.

`TestChildStdoutLaunch` in `../test_cli.py` covers clean Claude print-mode and
Codex exec/app-server stdout, early launch errors, and forwarding through ug's `--`.
Token membership before the agent's `--` intentionally also matches option values and prompt
Expand Down
48 changes: 48 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,54 @@ def test_unmanaged_claude_launch_with_saved_provider_keeps_native_defaults(self)
calls["resolve_model"].assert_not_called()
calls["launch"].assert_called_once()

@pytest.mark.parametrize("custom_cli_enabled", [False, True])
@pytest.mark.parametrize(
"source_args",
[[], ["--model-location", "main.default"], ["--provider", "main.default.anthropic"]],
ids=["default-catalog", "model-location", "provider"],
)
def test_claude_catalog_uses_launch_auth_profile(
self, monkeypatch, custom_cli_enabled, source_args
):
monkeypatch.setenv("ENABLE_CUSTOM_OAUTH_FROM_CLI", "1" if custom_cli_enabled else "0")
expected_profile = "custom-profile" if custom_cli_enabled else "workspace-profile"

def fetch_token(workspace, profile):
if profile != expected_profile:
raise RuntimeError(f"No cached credentials for {profile}")
return "launch-token"

with (
_launch_policy_patches(None) as calls,
patch("ucode.cli.get_databricks_token", side_effect=fetch_token) as token,
):
calls["state"]["profile"] = "workspace-profile"
calls["state"]["custom_oauth"] = {
"client_id": "custom-client",
"redirect_url": "http://localhost:8020/callback",
"scopes": ["offline_access", "all-apis"],
"profile": "custom-profile",
}
result = runner.invoke(
app,
[
"claude",
"--client-id",
"custom-client",
"--scopes",
"offline_access,all-apis",
*source_args,
],
)

assert result.exit_code == 0, result.output
token.assert_called_once_with(MINIMAL_STATE["workspace"], expected_profile)
assert calls["list_catalog"].call_args.args == (
MINIMAL_STATE["workspace"],
"launch-token",
)
calls["launch"].assert_called_once()

def test_unmanaged_claude_launch_lists_gateway_catalog(self):
with _launch_policy_patches(None) as calls:
result = runner.invoke(app, ["claude"])
Expand Down
Loading