OpenHands / OpenHands/software-agent-sdk

[Agent Profile] The profile model isn't self-describing — clients hardcode version numbers to guess which fields a server accepts

Open
#4,964 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

architecture enhancement
Dominant language
Python
Stars
1.1k
Forks
539
Avg merge
1d 19h
Merged PRs (30d)
137

Description

The problem

AgentSettingsConfig is self-describing. export_agent_settings_schema() walks both variants and GET /api/settings/agent-schema serves the result, so a client can render a settings form off the server's own description — prominence tiers, depends_on, choices, coercion, defaults.

AgentProfile is not. agent_profiles_router has list / get / create / delete / rename / activate / materialize, and nothing that describes the model.

The mechanism is already generic — export_settings_schema(model) accepts any Pydantic model — it simply returns nothing for a profile, because no profile field carries a SETTINGS_METADATA annotation:

export_settings_schema(OpenHandsAgentProfile) -> sections: 0  fields: 0
export_settings_schema(ACPAgentProfile)       -> sections: 0  fields: 0
OpenHandsAgentProfile.model_fields            -> 16

What clients do instead

They hardcode a version number per field. AgentProfileBase is extra="forbid", so posting a field an older server doesn't know 422s and loses the entire save — the gate can't be optimistic, and every new profile field needs one.

Canvas's src/api/agent-profiles-service/profile-field-support.ts today:

field gate where the number comes from
enable_switch_llm_tool 1.31.0 sdk#3870
tools 1.31.2 sdk#3968
secret_refs 1.47.0 sdk#4931 — unreleased; this is a forward guess
mcp_server_refs none on AgentProfileBase since 1.29.0, below the minimum supported server

The secret_refs row is the sharp end. OpenHands/OpenHands#17237 ships a constant naming a release that does not exist yet, to be corrected by hand once sdk#4931 actually releases. If it lands on 1.48.0 instead, the control silently disappears for a release and nothing fails loudly.

Why the existing settings schema can't answer this

Worth stating, because it's the obvious first idea and it's wrong:

tools is in the agent settings schema today (general section, array, MAJOR) — and has been since long before it reached OpenHandsAgentProfile in 1.31.2. A 1.30 server advertises tools in the schema and 422s a profile that sets it. The settings schema describes AgentSettingsConfig; the profile is a separate model with its own history. Schema presence there is necessary, not sufficient.

And for most of the fields there is nothing to consult at all: llm_profile_ref, mcp_server_refs, secret_refs and disabled_skills have no settings analogue, and system_message_suffix lives on AgentContext, whose schema section emits only load_memory.

Proposal

export_agent_profile_schema() beside export_agent_settings_schema(), served at GET /api/agent-profiles/schema, merging the two variants with the same variant tagging. Annotating the 16 fields with SettingsFieldMetadata is most of the work; the export and transport already exist.

A client's question then becomes "is secret_refs in the schema?" — a fact from the server — instead of "is this server newer than a number I guessed."

Deliberately not in scope

Rendering. Generic rendering of an array field is a JSON textarea, which can't express the profile editor's tri-state (null = server default vs [] vs explicit list) or enumerate pickable values. The tool/MCP/secret pickers should stay purpose-built — exactly the arrangement SPECIALLY_RENDERED_KEYS already encodes for llm.model / llm.api_key. This is for capability detection, not for auto-generating the editor.

What tools an agent will actually get — that's #4958, a question about values at resolve time. This one is about which fields the model accepts. Neither subsumes the other.

Cloud. A schema endpoint answers "will this save be accepted", not "will the restriction hold". Canvas excludes cloud from agentProfileSupportsSecretRefs for the second reason — cloud resolves profiles itself and sends a resolved agent, so the agent-server's filtering branch never runs — and that short-circuit stays until OpenHands/enterprise#364 ships.

Payoff

Deletes the version table, removes the forward guess, and makes every future profile field self-announcing instead of requiring a coordinated constant in each client.

Related: #4953, #4956, #4958.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with AgentProfileBase, OpenHandsAgentProfile, ACPAgentProfile, agent_profiles_router, and export_settings_schema. Trace the existing settings-schema export and profile routes, then identify how the 16 profile fields and both variants are represented. Done means GET /api/agent-profiles/schema exposes the profile fields with variant tagging and clients can detect accepted fields without version gates.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
api, backend, backend-api-design
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
62/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.