Skip to content

Commit 0ccc381

Browse files
Revert "fix(sdk): simplify persisted AgentSettings migrations"
This restores the conversation settings split state for PR #2789.\n\nCo-authored-by: openhands <openhands@all-hands.dev>
1 parent 5ee469c commit 0ccc381

File tree

9 files changed

+552
-83
lines changed

9 files changed

+552
-83
lines changed

AGENTS.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,6 @@ When reviewing code, provide constructive feedback:
107107
- `AgentSettings.tools` is part of the exported settings schema so the schema stays aligned with the settings payload that round-trips through `AgentSettings` and drives `create_agent()`.
108108
- `AgentSettings.mcp_config` now uses FastMCP's typed `MCPConfig` at runtime. When serializing settings back to plain data (e.g. `model_dump()` or `create_agent()`), keep the output compact with `exclude_none=True, exclude_defaults=True` so callers still see the familiar `.mcp.json`-style dict shape.
109109
- Persisted `AgentSettings` now have an SDK-owned canonical migration contract: legacy v1 payloads are the raw unversioned settings mapping, while the current canonical format is v2 `{\"version\": 2, \"settings\": ...}`. Use `AgentSettings.load_persisted()`/`dump_persisted()` so consumers share the same upgrade path and preserve sparse overlays with `exclude_unset=True`.
110-
- Keep the persisted-settings contract wrapper-based and product-neutral; do not fold persistence-only version metadata into the runtime `AgentSettings` model or add consumer-specific patch/diff helpers there.
111-
112110
- Anthropic malformed tool-use/tool-result history errors (for example, missing or duplicated ``tool_result`` blocks) are intentionally mapped to a dedicated `LLMMalformedConversationHistoryError` and caught separately in `Agent.step()`, so recovery can still use condensation while logs preserve that this was malformed history rather than a true context-window overflow.
113111
- AgentSkills progressive disclosure goes through `AgentContext.get_system_message_suffix()` into `<available_skills>`, and `openhands.sdk.context.skills.to_prompt()` truncates each prompt description to 1024 characters because the AgentSkills specification caps `description` at 1-1024 characters.
114112
- Workspace-wide uv resolver guardrails belong in the repository root `[tool.uv]` table. When `exclude-newer` is configured there, `uv lock` persists it into the root `uv.lock` `[options]` section as both an absolute cutoff and `exclude-newer-span`, and `uv sync --frozen` continues to use that locked workspace state.

openhands-agent-server/openhands/agent_server/event_service.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,8 +498,8 @@ async def start(self):
498498
tags=self.stored.tags,
499499
)
500500

501-
# Set confirmation mode if enabled
502501
conversation.set_confirmation_policy(self.stored.confirmation_policy)
502+
conversation.set_security_analyzer(self.stored.security_analyzer)
503503
self._conversation = conversation
504504

505505
# Register state change callback to automatically publish updates

openhands-agent-server/openhands/agent_server/models.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,14 @@
77
from pydantic import BaseModel, Discriminator, Field, Tag, field_validator
88

99
from openhands.agent_server.utils import OpenHandsUUID, utc_now
10-
from openhands.sdk import LLM, Agent, ImageContent, Message, TextContent
10+
from openhands.sdk import (
11+
LLM,
12+
Agent,
13+
ConversationSettings,
14+
ImageContent,
15+
Message,
16+
TextContent,
17+
)
1118
from openhands.sdk.agent.acp_agent import ACPAgent
1219
from openhands.sdk.conversation.conversation_stats import ConversationStats
1320
from openhands.sdk.conversation.secret_registry import SecretRegistry
@@ -105,6 +112,10 @@ class _StartConversationRequestBase(BaseModel):
105112
description="Controls when the conversation will prompt the user before "
106113
"continuing. Defaults to never.",
107114
)
115+
security_analyzer: SecurityAnalyzerBase | None = Field(
116+
default=None,
117+
description="Optional security analyzer to evaluate action risks.",
118+
)
108119
initial_message: SendMessageRequest | None = Field(
109120
default=None, description="Initial message to pass to the LLM"
110121
)
@@ -181,12 +192,42 @@ class StartConversationRequest(_StartConversationRequestBase):
181192

182193
agent: Agent
183194

195+
@classmethod
196+
def from_settings(
197+
cls,
198+
*,
199+
agent: Agent,
200+
workspace: LocalWorkspace,
201+
conversation_settings: ConversationSettings | None = None,
202+
**kwargs: Any,
203+
) -> "StartConversationRequest":
204+
payload = dict(kwargs)
205+
if conversation_settings is not None:
206+
for key, value in conversation_settings.to_start_request_kwargs().items():
207+
payload.setdefault(key, value)
208+
return cls(agent=agent, workspace=workspace, **payload)
209+
184210

185211
class StartACPConversationRequest(_StartConversationRequestBase):
186212
"""Payload to create a conversation with ACP-capable agent support."""
187213

188214
agent: ACPEnabledAgent
189215

216+
@classmethod
217+
def from_settings(
218+
cls,
219+
*,
220+
agent: ACPEnabledAgent,
221+
workspace: LocalWorkspace,
222+
conversation_settings: ConversationSettings | None = None,
223+
**kwargs: Any,
224+
) -> "StartACPConversationRequest":
225+
payload = dict(kwargs)
226+
if conversation_settings is not None:
227+
for key, value in conversation_settings.to_start_request_kwargs().items():
228+
payload.setdefault(key, value)
229+
return cls(agent=agent, workspace=workspace, **payload)
230+
190231

191232
class StoredConversation(StartACPConversationRequest):
192233
"""Stored details about a conversation.

openhands-agent-server/openhands/agent_server/settings_router.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from fastapi import APIRouter
44

5-
from openhands.sdk.settings import AgentSettings, SettingsSchema
5+
from openhands.sdk.settings import AgentSettings, ConversationSettings, SettingsSchema
66

77

88
settings_router = APIRouter(prefix="/settings", tags=["Settings"])
@@ -13,7 +13,18 @@ def _get_agent_settings_schema() -> SettingsSchema:
1313
return AgentSettings.export_schema()
1414

1515

16+
@lru_cache(maxsize=1)
17+
def _get_conversation_settings_schema() -> SettingsSchema:
18+
return ConversationSettings.export_schema()
19+
20+
1621
@settings_router.get("/schema", response_model=SettingsSchema)
1722
async def get_agent_settings_schema() -> SettingsSchema:
1823
"""Return the schema used to render AgentSettings-based settings forms."""
1924
return _get_agent_settings_schema()
25+
26+
27+
@settings_router.get("/conversation-schema", response_model=SettingsSchema)
28+
async def get_conversation_settings_schema() -> SettingsSchema:
29+
"""Return the schema used to render ConversationSettings-based forms."""
30+
return _get_conversation_settings_schema()

openhands-sdk/openhands/sdk/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@
5252
from openhands.sdk.settings import (
5353
AgentSettings,
5454
CondenserSettings,
55+
ConversationSettings,
56+
ConversationVerificationSettings,
5557
SettingsChoice,
5658
SettingsFieldSchema,
5759
SettingsSchema,
@@ -137,6 +139,8 @@
137139
"AgentContext",
138140
"LLMSummarizingCondenser",
139141
"CondenserSettings",
142+
"ConversationSettings",
143+
"ConversationVerificationSettings",
140144
"VerificationSettings",
141145
"AgentSettings",
142146
"SettingsChoice",

openhands-sdk/openhands/sdk/settings/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
from .model import (
1717
AgentSettings,
1818
CondenserSettings,
19+
ConversationSettings,
20+
ConversationVerificationSettings,
1921
SettingsChoice,
2022
SettingsFieldSchema,
2123
SettingsSchema,
@@ -27,6 +29,8 @@
2729
_MODEL_EXPORTS = {
2830
"AgentSettings",
2931
"CondenserSettings",
32+
"ConversationSettings",
33+
"ConversationVerificationSettings",
3034
"SettingsChoice",
3135
"SettingsFieldSchema",
3236
"SettingsSchema",
@@ -38,6 +42,8 @@
3842
__all__ = [
3943
"AgentSettings",
4044
"CondenserSettings",
45+
"ConversationSettings",
46+
"ConversationVerificationSettings",
4147
"SETTINGS_METADATA_KEY",
4248
"SETTINGS_SECTION_METADATA_KEY",
4349
"SettingProminence",

0 commit comments

Comments
 (0)