fix(settings): stop one member's agent settings reconfiguring the org - #241
fix(settings): stop one member's agent settings reconfiguring the org#241jlav wants to merge 7 commits into
Conversation
OHE-3138 Changing your own agent settings changes them for everyone in the org
What happensIf you change your own agent settings - switching your agent from OpenHands to Codex, say, or picking a different model - that change is applied to every other member of your organization, and to the organization's defaults. There's no warning that this will happen, and you don't need to be an admin or owner to do it. Any member can. For everyone else, their agent silently becomes whatever you picked. If your choice needs credentials they don't have, such as a connected Codex account or an API key, then every new conversation they try to start fails with an error like:
They can't fix it for themselves either. If they change their own settings back, that just pushes their choice onto everyone else in turn. What we sawOn our shared beta instance, one person switched their agent to Codex to try it out. That immediately blocked all 17 members of the organization from starting any new conversation, and it stayed broken until someone changed the setting back. Switching the agent also wipes the organization's configured model and connection URL, and switching back doesn't restore them. So recovering isn't just a matter of undoing the change - someone has to know the original values and re-enter them by hand. Expected behavior
ImpactAny organization with more than one member. A single member with no special permissions can stop everyone else from starting conversations by changing a setting on their own account, and the people affected have no way to recover on their own. FixThe first three points are addressed in #241. Restoring the model and connection URL after an agent switch is a separate gap, tracked upstream in OpenHands/OpenHands#14370. |
Coverage reportClick to see where and how coverage changed
This report was generated by python-coverage-comment-action |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
HUMAN:
AGENT:
Why
On a shared install, one member's settings were reconfiguring everyone else. Two things went wrong, both entered through
/settings/agent, both reachable by a plain member.A member switching to codex pushed that choice onto the org default and onto every other member's row. Everyone without codex credentials then got a 400 on every new conversation. This blocked all 17 members of the beta staging instance.
Separately, saving that page detached a member from org-wide defaults. An admin would change the org's model and it reached only the members who had never saved their agent settings. Nothing signalled this in either direction.
Both come from the same mistake. A member's row is meant to hold only their overrides, with everything else resolving through the org. But the save path receives their composed settings, which is the org defaults with their edits already merged in, and it treated every value in there as a deliberate choice. So it persisted values the member never made, outward onto other rows and inward onto their own.
Approach
Write the acting member's row only, and store only what they actually chose.
Separating a real choice from an inherited one takes two checks, because two different things can fake one:
agent_kindrebuilds the variant from SDK defaults, so every field the caller didn't send comes back fabricated.Settings.update()now records what the caller actually supplied on a flip.org.agent_settingsis what identifies it.Neither check covers the other. A fabricated model genuinely differs from the org's, so the comparison keeps it. An ordinary save fabricates nothing, so the flip check never fires.
Org-wide defaults get their own way in:
POST /orgs/{id}/apptakes anagent_settings_diff, gated onEDIT_ORG_SETTINGS.Issue Number
OHE-3138 - https://linear.app/all-hands-ai/issue/OHE-3138
How to Test
With an admin and a member in one org:
/settings/agentand save. The admin should still be able to start a conversation./settings/agent. Change the org's default model as admin. The member should pick it up.Video/Screenshots
Type
Notes
Merge #254 first. It carries the first two commits here on their own, which is the minimum needed to stop one member reconfiguring the org. This PR adds the two follow-on fixes on top. Once #254 lands I'll rebase and the shared commits drop out.
No migration. A row full of values the member never chose thins out on their next save.
Three follow-ups I left out:
store_settingshas no role check, soPOST /api/v1/settingsaccepts an LLM payload from a member the UI correctly treats as read-only. That's a permissions gap rather than part of this fix.agent_context.current_datetimeregenerates on every construction, so it lands in a member's row on each save. The OpenHands agent rebuilds the context at conversation start and discards it, but an ACP harness can read the stored one and be told a stale current date.Enterprise server image for this PR: