Skip to content

feat: ExtensionConfig — models, Conversation wiring, and server integration#2858

Draft
csmith49 wants to merge 4 commits intorefactor/mcp-merge-to-mcp-modulefrom
feat/extension-config-model
Draft

feat: ExtensionConfig — models, Conversation wiring, and server integration#2858
csmith49 wants to merge 4 commits intorefactor/mcp-merge-to-mcp-modulefrom
feat/extension-config-model

Conversation

@csmith49
Copy link
Copy Markdown
Collaborator

@csmith49 csmith49 commented Apr 16, 2026

Summary

Consolidated PR (previously #2858, #2859, #2860) introducing ExtensionConfig for centralized extension loading across the SDK and server.

1. ExtensionConfig and ResolvedExtensions models

ExtensionConfig holds the specification (skills, plugins, hooks, auto-loading flags); resolve() loads, merges, and returns a ResolvedExtensions with the materialized result.

2. Wire ExtensionConfig into Conversation

LocalConversation:

  • New _extension_config field replaces _plugin_specs/_pending_hook_config
  • _ensure_plugins_loaded() renamed to _ensure_extensions_loaded()
  • Delegates to ExtensionConfig.resolve(work_dir) then merges skills/mcp/hooks
  • Backward-compatible: builds ExtensionConfig from legacy params when not provided

Conversation factory & RemoteConversation:

  • All overloads accept extension_config kwarg
  • ExtensionConfig exported from openhands.sdk

3. Server-side integration and deprecations

Server request model:

  • Add extension_config field to _StartConversationRequestBase
  • Flows through StoredConversation automatically via inheritance

Event service:

  • Forward extension_config from StoredConversation to LocalConversation

AgentContext deprecation:

  • load_user_skills, load_public_skills, marketplace_path deprecated (1.18.0 → 1.23.0)

Examples:

  • 03_activate_skill: move load_public_skills to ExtensionConfig
  • 01_loading_agentskills: remove redundant load_public_skills=False

Changes

  • extensions/config.py: ExtensionConfig model with resolve(work_dir) method and ResolvedExtensions dataclass
  • Comprehensive tests in tests/sdk/extensions/test_config.py
  • Conversation wiring (local, remote, factory)
  • Server request model and event service integration
  • AgentContext field deprecations
  • Example updates

Stack

Part of a gh stack — review bottom-up:

  1. ⬇️ feat: Extension installation module #2811 feat/installed-extensionsmain
  2. ⬇️ refactor: Centralize MCP utils in openhands.sdk.mcp #2848 refactor/mcp-merge-to-mcp-modulefeat/installed-extensions
  3. → This PR (feat: ExtensionConfig — models, Conversation wiring, and server integration #2858) — consolidated from feat: ExtensionConfig — models, Conversation wiring, and server integration #2858, feat: wire ExtensionConfig into Conversation for centralized extension loading #2859, feat: server-side ExtensionConfig support, deprecate AgentContext loading fields #2860

This PR was updated by an AI assistant (OpenHands) on behalf of the user.

csmith49 and others added 4 commits April 16, 2026 09:24
Exploratory draft of the declarative extension config and resolution
workflow. ExtensionConfig holds the specification (skills, plugins,
hooks, auto-loading flags); resolve() loads, merges, and returns a
ResolvedExtensions with the materialized result.

Not wired into Conversation yet -- just the data models and merge logic.

Co-authored-by: openhands <openhands@all-hands.dev>
- Rename load_user_skills → load_user_extensions and
  load_public_skills → load_public_extensions on ExtensionConfig
  since the config handles plugins, hooks, and agents — not just skills.
- Replace deprecated plugin.add_mcp_config_to() with merge_mcp_configs()
  in ExtensionConfig.resolve().
- Fix test_config.py: add required 'content' field to Skill helper,
  use correct HookConfig/HookMatcher/HookDefinition API.

Co-authored-by: openhands <openhands@all-hands.dev>
…n loading

Conversation (local and remote) now accepts an ExtensionConfig parameter
that replaces the separate plugins, hook_config, and skills parameters.
ExtensionConfig.resolve() centralizes all extension loading logic.

LocalConversation changes:
- New _extension_config field replaces _plugin_specs/_pending_hook_config
- _ensure_plugins_loaded() renamed to _ensure_extensions_loaded()
- Delegates to ExtensionConfig.resolve(work_dir) then merges skills/mcp/hooks
- Backward-compatible: builds ExtensionConfig from legacy params when not provided

Conversation factory:
- All overloads accept extension_config kwarg and forward it

RemoteConversation:
- Accepts extension_config and serializes it into the server payload
- Falls back to legacy plugins/hook_config fields for server compatibility

Public API:
- ExtensionConfig exported from openhands.sdk

Closes #2767 (wiring phase)

Co-authored-by: openhands <openhands@all-hands.dev>
…ding fields

Server request model:
- Add extension_config field to _StartConversationRequestBase
- Flows through StoredConversation automatically via inheritance

Event service:
- Forward extension_config from StoredConversation to LocalConversation
- Legacy plugins/hook_config still passed for backward compat

AgentContext deprecation:
- load_user_skills, load_public_skills, marketplace_path now carry
  deprecated Field metadata (deprecated_in=1.18.0, removed_in=1.23.0)
- _load_auto_skills model_validator emits warn_deprecated() when
  these fields are set to non-default values
- Fields still function for backward compatibility during the
  deprecation window

Examples:
- 03_activate_skill: move load_public_skills to ExtensionConfig on
  Conversation
- 01_loading_agentskills: remove redundant load_public_skills=False

Tests:
- 3 new server tests: extension_config field exists, flows through
  StoredConversation, defaults to None

Co-authored-by: openhands <openhands@all-hands.dev>
@csmith49 csmith49 changed the title feat/extension config model feat: ExtensionConfig and ResolvedExtensions models Apr 16, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 16, 2026

Coverage

Coverage Report •
FileStmtsMissCoverMissing
openhands-agent-server/openhands/agent_server
   event_service.py3498974%56–57, 75–77, 86–90, 93–96, 116, 220, 237, 278, 288, 312–313, 317, 325, 328, 368–369, 385, 387, 391–393, 397, 406–407, 409, 413, 419, 421, 451–456, 600, 602–603, 607, 621–623, 625, 629–632, 636–639, 647–650, 670–671, 673–680, 682–683, 692–693, 695–696, 703–704, 706–707, 727, 733, 739, 748–749
openhands-sdk/openhands/sdk
   __init__.py28292%97–98
openhands-sdk/openhands/sdk/context
   agent_context.py133695%316–318, 347, 370, 376
openhands-sdk/openhands/sdk/conversation
   conversation.py35877%144, 157–158, 164–167, 171
   request.py45197%61
openhands-sdk/openhands/sdk/conversation/impl
   local_conversation.py3932493%298, 303, 333, 377, 443, 635–636, 639, 799, 807, 809, 813–814, 825, 827–829, 854, 926, 1052, 1056, 1126, 1133–1134
   remote_conversation.py62610483%79, 81, 148, 175, 188, 190–193, 203, 225–226, 231–234, 317, 327–329, 335, 376, 523–526, 528, 548–552, 557–560, 563, 579, 727–729, 765–766, 770–771, 785, 809–810, 829, 840–841, 861–864, 866–867, 891–893, 896–900, 902–903, 907, 909–917, 919, 956, 1087, 1159–1160, 1164, 1169–1173, 1179–1185, 1198–1199, 1234, 1290, 1297, 1303–1304, 1382–1383
openhands-sdk/openhands/sdk/extensions
   config.py64198%174
TOTAL23486598774% 

@csmith49 csmith49 changed the title feat: ExtensionConfig and ResolvedExtensions models feat: ExtensionConfig — models, Conversation wiring, and server integration Apr 16, 2026
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.

1 participant