Skip to content

[AIGTWY-4573] Apply the managed config at launch and version-gate it - #593

Open
david-siqi-liu wants to merge 1 commit into
mainfrom
david/AIGTWY-4573
Open

david-siqi-liu wants to merge 1 commit into
mainfrom
david/AIGTWY-4573

Conversation

@david-siqi-liu

@david-siqi-liu david-siqi-liu commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator

With the config parsed (the parent PR), apply the managed configuration to every enabled agent at launch, but only when it changed since the last apply (an update_time watermark) or on --refresh, so an unchanged launch never re-writes the OS-managed files or prompts for a password. Also wire the managed MCP servers and skills, drop the post-configure probe validation, and rename user-facing strings.

Changes:

  • Gate the launch apply on update_time. refresh_managed_config always fetches; a launch re-applies the config (the only step that writes the OS-managed files and can prompt for a password) only when update_time is newer than the applied watermark, or on ug <agent> --refresh, and records the new watermark. An unchanged launch skips the apply and never prompts. ug configure always applies. The per-tool launch write is wrapped in suppressed_managed_writes() so the apply-all owns the single OS write.
  • Apply the managed config's mcp_servers and skills to each enabled agent. Both are UC-services-only selectors: register the absolute names (as UC mcp-services) or download the named skills, plus everything discovered under a unity_catalog_location (a schema's mcp-services, or its skills), unioned. Nothing on the launch path turned the workspace's published servers or skills into agent registrations before.
  • ug configure wires the managed MCP servers and skills at configure time (not only at launch), and suppresses the interactive MCP/skills/plugins prompt when the managed config already defines them (and never installs under --dry-run).
  • Stop ug configure from validating agents with a probe message: remove the post-configure validation and the --skip-validate flag (the probe reverted a freshly-written config on failure, and the gateway enforces model access regardless). validate_tool stays for headless and e2e callers.
  • When a config we previously applied is gone (deleted, or the feature turned off), the launch does not auto-revert; it points the developer to ug revert, but only when the config is definitively gone (NOT_FOUND or feature off), not on a transient fetch failure. Keyed on the applied watermark.
  • Drop the bare-ug launch banner and redundant "Starting " line; rename "CLI Managed Configuration" to "managed configuration".

This pull request and its description were written by Isaac.

@david-siqi-liu
david-siqi-liu added this pull request to stack #595 September 13, 2026 12:37
@david-siqi-liu
david-siqi-liu force-pushed the david/AIGTWY-4573 branch 2 times, most recently from 9be4c55 to 3670fe6 Compare September 13, 2026 14:43
@david-siqi-liu
david-siqi-liu force-pushed the david/AIGTWY-4573 branch 4 times, most recently from 5f70142 to 83138ae Compare September 13, 2026 18:15
david-siqi-liu added a commit that referenced this pull request Sep 13, 2026
Resolve the managed config's per-agent inputs into each agent's own config shape: the model
allow-list or discovery location into Claude's /model picker and Codex's model catalog, and the
custom http_headers into each agent's outbound requests. Stacked on the launch-apply PR (#593).

- managed_resolve: add managed_static_models, managed_model_service_location, and
  managed_custom_headers; extend managed_state_overrides to layer claude_static_models /
  codex_static_models, the per-agent model_service_location, and {tool}_custom_headers into state.
  For codex a discovery location alone does not count as supplying models (no reader for it), so
  discovery still runs; for claude it does.
- claude: a static list becomes availableModels + enforceAvailableModels + a modelPicker (exactly
  the named ids, non-Claude included); a discovery location turns on gateway discovery instead.
  Ownership markers prune only ug-owned picker keys on a static-to-discovery transition, and clear
  only when the OS-managed file was written. Managed http_headers append to ANTHROPIC_CUSTOM_HEADERS,
  with ug's fixed headers winning on conflict; values that would break the header serialization
  (newlines, and commas for Gemini) are dropped.
- codex: a static list is written as a model_catalog_json catalog of full ModelPresets; a fetch
  failure preserves an existing catalog (or falls back to discovery when none exists yet), and a
  successful but empty result falls back to discovery.
  Managed http_headers merge into the provider http_headers, ug's fixed headers winning.
- gemini: managed http_headers merge into GEMINI_CLI_CUSTOM_HEADERS, ug's fixed headers winning.
- README: document the Claude picker, the Codex catalog, and the ucode-models.json managed file.

Co-authored-by: Isaac <no-reply@databricks.com>
@david-siqi-liu
david-siqi-liu removed this pull request from stack #595 September 13, 2026 18:20
@david-siqi-liu
david-siqi-liu added this pull request to stack #598 September 13, 2026 18:20
david-siqi-liu added a commit that referenced this pull request Sep 13, 2026
Resolve the managed config's per-agent inputs into each agent's own config shape: the model
allow-list or discovery location into Claude's /model picker and Codex's model catalog, and the
custom http_headers into each agent's outbound requests. Stacked on the launch-apply PR (#593).

- managed_resolve: add managed_static_models, managed_model_service_location, and
  managed_custom_headers; extend managed_state_overrides to layer claude_static_models /
  codex_static_models, the per-agent model_service_location, and {tool}_custom_headers into state.
  For codex a discovery location alone does not count as supplying models (no reader for it), so
  discovery still runs; for claude it does.
- claude: a static list becomes availableModels + enforceAvailableModels + a modelPicker (exactly
  the named ids, non-Claude included); a discovery location turns on gateway discovery instead.
  Ownership markers prune only ug-owned picker keys on a static-to-discovery transition, and clear
  only when the OS-managed file was written. Managed http_headers append to ANTHROPIC_CUSTOM_HEADERS,
  with ug's fixed headers winning on conflict; values that would break the header serialization
  (newlines, and commas for Gemini) are dropped.
- codex: a static list is written as a model_catalog_json catalog of full ModelPresets; a fetch
  failure preserves an existing catalog (or falls back to discovery when none exists yet), and a
  successful but empty result falls back to discovery.
  Managed http_headers merge into the provider http_headers, ug's fixed headers winning.
- gemini: managed http_headers merge into GEMINI_CLI_CUSTOM_HEADERS, ug's fixed headers winning.
- README: document the Claude picker, the Codex catalog, and the ucode-models.json managed file.

Co-authored-by: Isaac <no-reply@databricks.com>
Comment thread src/ucode/agents/__init__.py Outdated
def check_gateway_endpoint(state: dict, tool: str) -> bool:
"""V2-only: a tool is available iff we discovered models for it."""
def check_gateway_endpoint(state: dict, tool: str, managed: dict | None = None) -> bool:
"""V2-only: a tool is available iff we discovered models for it or the managed config supplies them.

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.

what does v2 only mean here. should we remove it. i hope it's not aigw v2...

Comment thread src/ucode/cli.py
Comment thread src/ucode/cli.py
Comment thread src/ucode/cli.py Outdated
# only when the config changed since last applied here, or on `--refresh`. The launched
# tool's own write below is suppressed, so this apply-all owns the OS write.
applied_ut = get_applied_managed_update_time(existing)
if managed is not None and (refresh or managed_config_is_newer(managed, applied_ut)):

@lilly-luo lilly-luo Sep 14, 2026

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.

can we check not just the timestamp but also that the workspace url is the same? if the workspace url is diff, we should always reapply

in the case below

time = t
workspace A -> updated at t-1 --> no managed config, apply for the first time at time t
workspace B -> updated at t-2 --> managed config but it's not newer than t. so it doesn't get updated 

we currently don't reapply. but we should

Comment thread src/ucode/cli.py
Comment on lines +2315 to +2318
# The OS-managed (sudo) write is owned by the version-gated apply-all above and by `ug
# configure`; a launch only refreshes the user-level config and computes launch params, so
# suppress the OS write here to keep an unchanged launch prompt-free.
with suppressed_managed_writes():

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.

does this mean that we ONLY write to managed settings during ug configure? i think this is what i was worried about re the autoconfigure for ug claude

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

we also write to managed settings during ug/ug claude/ug codex if drift has been detected

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.

is that in this PR or a diff PR? it looks like in this PR we never write to managed settings during configure_tool?

Comment thread src/ucode/cli.py
Comment on lines +899 to +902
if managed is not None:
state = set_applied_managed_update_time(state, managed_update_time(managed))
save_state(state)
_apply_managed_mcp_and_skills(managed, picked, state)

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.

what happens if i only configure 1 agent? ug configure --agents claude. that means that the timestamp is workspace wide but my codex is still using an old workspace config version?

Base automatically changed from david/AIGTWY-4573-parse to main September 14, 2026 23:36
@david-siqi-liu
david-siqi-liu force-pushed the david/AIGTWY-4573 branch 3 times, most recently from d5337d0 to 51202a8 Compare September 15, 2026 01:20
Comment thread src/ucode/cli.py
Comment on lines -2872 to -2880
skip_validate: Annotated[
bool,
typer.Option(
"--skip-validate",
help="Skip the post-configure validation step that sends a quick test "
"message through each agent. Config files are still written with the "
"freshly discovered models.",
),
] = False,

@lilly-luo lilly-luo Sep 15, 2026

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.

im worried that clients will use --skip-validate and break. can we mark as deprecated instead but leave the --skip-validate cmd so that No such option: --skip-validate doesn't throw? it can just do nothing + we can have a deprecation warning

Comment thread src/ucode/mcp.py
Comment on lines +1179 to +1181
parts = name.split(".")
if len(parts) != 3 or not all(parts):
return None

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.

can you use _is_uc_fqn instead and make it public

def _is_uc_fqn(name: str, *, parts: int) -> bool:

Comment thread src/ucode/cli.py
if not refresh and applied_ut is not None and config_changed:
print_note("The managed configuration was updated; re-applying it.")
enabled = managed_enabled_tools(managed)
to_apply = enabled if config_changed else [tool] if tool in enabled else []

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.

is this possible because we're doing all the applies for all tools at once?

1/ admin sets up claude code
2/ user uses claude code
3/ admins sets up codex incorrectly
4/ user goes to run claude code -> is now blocked because codex configuration is wrong

With the config parsed (the stacked parse PR), apply the managed configuration to every enabled
agent at launch, but only when it changed since the last apply (an update_time watermark) or on
--refresh, so an unchanged launch never re-writes the OS-managed files or prompts for a password.
Also remove the post-configure probe validation, wire the managed MCP servers and skills, and rename
user-facing strings.

- Gate the launch apply on update_time. refresh_managed_config always fetches; a launch re-applies
  the managed configuration (the only step that writes the OS-managed files and can prompt for a
  password) only when update_time is newer than the applied watermark, or on ug <agent> --refresh,
  and records the new watermark. An unchanged launch skips the apply and never prompts. ug configure
  always applies and records the watermark. The "was updated; re-applying it" note prints only on a
  genuine version change, not on a no-op --refresh. The per-tool launch write is wrapped in
  suppressed_managed_writes() so the apply-all owns the single OS write.
- Apply the managed config's mcp_servers and skills to each enabled agent. Both are UC-services-only
  selectors: register the absolute names (as UC mcp-services) or download the named skills, plus
  everything discovered under a unity_catalog_location (a schema's mcp-services via list_mcp_services,
  or its skills), unioned. Nothing on the launch path turned the workspace's published servers or
  skills into agent registrations before, so they never showed up.
- `ug configure` wires the managed config's MCP servers and skills at configure time (not only at
  launch), and suppresses the interactive MCP/skills/plugins prompt when the managed config already
  defines them (and never installs anything under --dry-run), so a managed workspace is set up
  without asking the developer to configure MCP.
- Stop ug configure from validating agents with a probe message. Remove the post-configure
  validation and the --skip-validate flag: it is not configure's job to test models, the probe also
  reverted a freshly-written config on failure, and the gateway enforces model access regardless.
  The now-unused validate_all_tools / provider_permission_error / resolve_managed_for_tool helpers
  are removed; validate_tool stays for headless and e2e callers.
- Drop the "Using managed config - launching <agent>" banner and the redundant post-panel "Starting
  <agent>" line from the bare-ug launch, so bare ug matches ug <agent>.
- When a config we previously applied is gone (deleted, or the feature turned off), the launch
  does not auto-revert the OS-managed files; it notes that Unity Gateway managed configurations are
  still applied and points the developer to `ug revert`, but only when the config is definitively
  gone (NOT_FOUND or the feature is off), not on a transient fetch failure. Keyed on the applied
  watermark, so a workspace that never had a config still just uses the developer's own settings.
- Rename the product string "CLI Managed Configuration" to "managed configuration" (lowercase)
  across user-facing lines, tighten the budget-tier recommendation line, and remove the
  --skip-validate docs.

Co-authored-by: Isaac <no-reply@databricks.com>
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.

2 participants