From ea8527532623417a7a5a65451a5d8d5ca2a44cf0 Mon Sep 17 00:00:00 2001 From: Josh Joseph Date: Fri, 16 Oct 2026 23:41:59 +0000 Subject: [PATCH 1/2] Use custom OAuth profile for Claude model catalogs --- src/ucode/cli.py | 10 ++++++-- tests/README.md | 5 ++++ tests/integration/README.md | 5 ++++ tests/test_cli.py | 48 +++++++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 2 deletions(-) diff --git a/src/ucode/cli.py b/src/ucode/cli.py index 624c18524..e1c1e281b 100644 --- a/src/ucode/cli.py +++ b/src/ucode/cli.py @@ -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 {} @@ -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, @@ -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( diff --git a/tests/README.md b/tests/README.md index 17c67b403..6d10c686b 100644 --- a/tests/README.md +++ b/tests/README.md @@ -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 diff --git a/tests/integration/README.md b/tests/integration/README.md index e9f5f2f75..6dab03012 100644 --- a/tests/integration/README.md +++ b/tests/integration/README.md @@ -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 diff --git a/tests/test_cli.py b/tests/test_cli.py index c7953a1e3..951a0a7f2 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -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"]) From 9df6cb69a3dac6dd9e15bc0209ac053d6166eaa8 Mon Sep 17 00:00:00 2001 From: Josh Joseph Date: Mon, 19 Oct 2026 04:01:49 +0000 Subject: [PATCH 2/2] Remove mocked Claude catalog regression coverage --- tests/README.md | 7 +++--- tests/integration/README.md | 7 +++--- tests/test_cli.py | 48 ------------------------------------- 3 files changed, 6 insertions(+), 56 deletions(-) diff --git a/tests/README.md b/tests/README.md index 6d10c686b..790238836 100644 --- a/tests/README.md +++ b/tests/README.md @@ -217,10 +217,9 @@ 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. +Claude catalog selection with a custom OAuth CLI profile needs a live Isaac check, +including default discovery, model locations, and providers when the ordinary profile +has expired. The automated suite does not cover this combination. `test_mcp_web_search.py` and `test_agent_claude.py` cover custom OAuth search registration, stale registration repair, SDK cache reuse/refresh, CLI profile diff --git a/tests/integration/README.md b/tests/integration/README.md index 6dab03012..9bffd03a1 100644 --- a/tests/integration/README.md +++ b/tests/integration/README.md @@ -91,10 +91,9 @@ 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. +Custom OAuth startup with an expired ordinary profile has no automated integration +coverage. Validate Claude's default, model-location, and provider catalogs in Isaac +with the custom OAuth 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 `--`. diff --git a/tests/test_cli.py b/tests/test_cli.py index 951a0a7f2..c7953a1e3 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -800,54 +800,6 @@ 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"])