openhands-agent-server 1.44.0__tar.gz → 1.44.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/PKG-INFO +1 -1
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/agent_profiles_router.py +7 -3
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/config.py +15 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/conversation_service.py +65 -5
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/docker/Dockerfile +35 -7
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/docker/build.py +36 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/pub_sub.py +7 -1
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/sockets.py +3 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands_agent_server.egg-info/PKG-INFO +1 -1
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/pyproject.toml +1 -1
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/__init__.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/__main__.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/_secret_redaction.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/_secrets_exposure.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/api.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/auth_router.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/bash_router.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/bash_service.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/canvas_extensions/__init__.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/canvas_extensions/installed.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/canvas_extensions/manifest.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/canvas_extensions_router.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/conversation_lease.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/conversation_router.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/credential_binding.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/dependencies.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/desktop_router.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/desktop_service.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/docker/wallpaper.svg +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/env_parser.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/event_router.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/event_service.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/file_router.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/git_router.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/hooks_router.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/hooks_service.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/init_router.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/llm_router.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/logging_config.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/mcp_oauth_store.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/mcp_router.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/middleware.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/models.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/openai/__init__.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/openai/models.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/openai/router.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/openai/service.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/openapi.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/persistence/__init__.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/persistence/models.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/persistence/store.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/plugins_router.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/plugins_service.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/profiles_router.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/provider_connections_router.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/py.typed +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/server_details_router.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/settings_router.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/skills_router.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/skills_service.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/sub_agents_router.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/telemetry/__init__.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/telemetry/factory.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/telemetry/http_exporter.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/telemetry/models.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/telemetry/policy.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/telemetry/posthog_exporter.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/telemetry/sanitizer.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/telemetry/service.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/telemetry/sink.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/telemetry/subscriber.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/telemetry_types.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/tool_preload_service.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/tool_router.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/utils.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/vscode_extensions/openhands-settings/extension.js +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/vscode_extensions/openhands-settings/package.json +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/vscode_router.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/vscode_service.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/workspace_router.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/workspaces_router.py +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands_agent_server.egg-info/SOURCES.txt +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands_agent_server.egg-info/dependency_links.txt +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands_agent_server.egg-info/entry_points.txt +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands_agent_server.egg-info/requires.txt +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands_agent_server.egg-info/top_level.txt +0 -0
- {openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openhands-agent-server
|
|
3
|
-
Version: 1.44.
|
|
3
|
+
Version: 1.44.1
|
|
4
4
|
Summary: OpenHands Agent Server - REST/WebSocket interface for OpenHands AI Agent
|
|
5
5
|
Project-URL: Source, https://github.com/OpenHands/software-agent-sdk
|
|
6
6
|
Project-URL: Homepage, https://github.com/OpenHands/software-agent-sdk
|
|
@@ -514,13 +514,17 @@ async def materialize_agent_profile(
|
|
|
514
514
|
mcp_config = settings.agent_settings.mcp_config
|
|
515
515
|
|
|
516
516
|
# Discover skills off the event loop so the dry-run can report which skills
|
|
517
|
-
# (catalog minus ``disabled_skills``) resolve.
|
|
518
|
-
#
|
|
517
|
+
# (catalog minus ``disabled_skills``) resolve. Mirrors the launch rule in
|
|
518
|
+
# ``conversation_service._resolve_agent_from_profile`` so the preview matches
|
|
519
|
+
# a real launch: an ACP profile is only given a catalog where the CLI cannot
|
|
520
|
+
# read the user's own configuration (#4019). A discovery failure must not 500
|
|
519
521
|
# the preview: pass ``available_skills=None`` and surface the failure as its
|
|
520
522
|
# own diagnostic below.
|
|
521
523
|
discovery_error: str | None = None
|
|
522
524
|
available_skills = None
|
|
523
|
-
if profile.agent_kind == "openhands"
|
|
525
|
+
if profile.agent_kind == "openhands" or (
|
|
526
|
+
config.acp_skill_sourcing == "openhands_managed"
|
|
527
|
+
):
|
|
524
528
|
try:
|
|
525
529
|
available_skills = await asyncio.to_thread(discover_profile_skills)
|
|
526
530
|
except Exception as exc:
|
{openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/config.py
RENAMED
|
@@ -27,6 +27,7 @@ CONFIG_PATH_ENV = "OPENHANDS_AGENT_SERVER_CONFIG_PATH"
|
|
|
27
27
|
DEFAULT_CONFIG_PATH = Path("workspace/openhands_agent_server_config.json")
|
|
28
28
|
# 20 minutes, matching the idle timeout used by OpenHands Cloud.
|
|
29
29
|
DEFAULT_CONVERSATION_IDLE_TTL_SECONDS: Final[float] = 20 * 60.0
|
|
30
|
+
ACPSkillSourcing = Literal["native", "openhands_managed"]
|
|
30
31
|
_logger = logging.getLogger(__name__)
|
|
31
32
|
|
|
32
33
|
|
|
@@ -349,6 +350,20 @@ class Config(BaseModel):
|
|
|
349
350
|
"The URL where this agent server instance is available externally"
|
|
350
351
|
),
|
|
351
352
|
)
|
|
353
|
+
acp_skill_sourcing: ACPSkillSourcing = Field(
|
|
354
|
+
default="native",
|
|
355
|
+
description=(
|
|
356
|
+
"Who supplies an ACP agent's skills. 'native' (the default, for a "
|
|
357
|
+
"host-local agent-server): nobody but the ACP CLI — it reads the "
|
|
358
|
+
"user's own home configuration and the repository, so OpenHands "
|
|
359
|
+
"injects none of its managed skills. 'openhands_managed' (for "
|
|
360
|
+
"container runtimes, where that host configuration is absent): also "
|
|
361
|
+
"inject the user/org/public/marketplace skills the server "
|
|
362
|
+
"discovers. Project/repository skills are never injected either way "
|
|
363
|
+
"— the CLI reads AGENTS.md itself (#4019). Set explicitly per "
|
|
364
|
+
"deployment; the agent-server image sets 'openhands_managed'."
|
|
365
|
+
),
|
|
366
|
+
)
|
|
352
367
|
registered_marketplaces: list[MarketplaceRegistration] = Field(
|
|
353
368
|
default_factory=list,
|
|
354
369
|
description=(
|
|
@@ -15,7 +15,7 @@ from weakref import WeakValueDictionary
|
|
|
15
15
|
import httpx
|
|
16
16
|
from pydantic import BaseModel
|
|
17
17
|
|
|
18
|
-
from openhands.agent_server.config import Config, WebhookSpec
|
|
18
|
+
from openhands.agent_server.config import ACPSkillSourcing, Config, WebhookSpec
|
|
19
19
|
from openhands.agent_server.conversation_lease import (
|
|
20
20
|
DEFAULT_LEASE_TTL_SECONDS,
|
|
21
21
|
ConversationLeaseHeldError,
|
|
@@ -290,11 +290,53 @@ def _same_workspace(a: LocalWorkspace, b: LocalWorkspace) -> bool:
|
|
|
290
290
|
return Path(a.working_dir).resolve() == Path(b.working_dir).resolve()
|
|
291
291
|
|
|
292
292
|
|
|
293
|
+
def _apply_acp_skill_sourcing(
|
|
294
|
+
agent: "AgentBase", sourcing: ACPSkillSourcing
|
|
295
|
+
) -> "AgentBase":
|
|
296
|
+
"""Strip OpenHands-managed skills from an ACP agent under ``native`` sourcing.
|
|
297
|
+
|
|
298
|
+
A host-local ACP CLI reads the user's own skills from its home directory, so
|
|
299
|
+
a second, OpenHands-managed set injected into its prompt is at best noise —
|
|
300
|
+
and the catalog listing tells it to call ``invoke_skill``, a tool no ACP
|
|
301
|
+
agent has. Container runtimes set ``openhands_managed`` because that home
|
|
302
|
+
configuration is absent there. Project skills are excluded either way, by
|
|
303
|
+
``ACPAgent`` itself (#4019).
|
|
304
|
+
|
|
305
|
+
A caller that sends ``agent`` / ``agent_settings`` puts its own skills on the
|
|
306
|
+
context, so the strip happens here rather than at profile resolution.
|
|
307
|
+
"""
|
|
308
|
+
if sourcing != "native" or not isinstance(agent, ACPAgent):
|
|
309
|
+
return agent
|
|
310
|
+
context = agent.agent_context
|
|
311
|
+
if context is None:
|
|
312
|
+
return agent
|
|
313
|
+
if not (
|
|
314
|
+
context.skills
|
|
315
|
+
or context.load_user_skills
|
|
316
|
+
or context.load_public_skills
|
|
317
|
+
or context.registered_marketplaces
|
|
318
|
+
):
|
|
319
|
+
return agent
|
|
320
|
+
return agent.model_copy(
|
|
321
|
+
update={
|
|
322
|
+
"agent_context": context.model_copy(
|
|
323
|
+
update={
|
|
324
|
+
"skills": [],
|
|
325
|
+
"load_user_skills": False,
|
|
326
|
+
"load_public_skills": False,
|
|
327
|
+
"registered_marketplaces": [],
|
|
328
|
+
}
|
|
329
|
+
)
|
|
330
|
+
}
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
|
|
293
334
|
def _resolve_agent_from_profile(
|
|
294
335
|
profile_id: "UUID",
|
|
295
336
|
cipher: "Cipher | None",
|
|
296
337
|
mcp_config: "dict[str, MCPServer]",
|
|
297
338
|
load_memory: bool = False,
|
|
339
|
+
acp_skill_sourcing: ACPSkillSourcing = "native",
|
|
298
340
|
) -> "tuple[AgentBase, LaunchedAgentProfile]":
|
|
299
341
|
"""Load and resolve an agent profile by id, returning the built agent + provenance.
|
|
300
342
|
|
|
@@ -310,6 +352,9 @@ def _resolve_agent_from_profile(
|
|
|
310
352
|
has no ``agent_context`` field, so the preference cannot ride the
|
|
311
353
|
profile — it is stamped onto the resolved agent below, else a
|
|
312
354
|
profile-launched conversation would silently ignore the setting.
|
|
355
|
+
acp_skill_sourcing: This deployment's ACP skill policy
|
|
356
|
+
(``Config.acp_skill_sourcing``). Decides whether an ACP profile is
|
|
357
|
+
resolved with the server's managed skill catalog or with none.
|
|
313
358
|
|
|
314
359
|
Raises:
|
|
315
360
|
ProfileNotFound: No stored profile has ``profile_id``.
|
|
@@ -340,11 +385,15 @@ def _resolve_agent_from_profile(
|
|
|
340
385
|
) from exc
|
|
341
386
|
|
|
342
387
|
# OpenHands profiles get the discovered catalog minus their ``disabled_skills``
|
|
343
|
-
# deny-list
|
|
344
|
-
#
|
|
345
|
-
#
|
|
388
|
+
# deny-list. An ACP profile gets it only where the CLI cannot reach the user's
|
|
389
|
+
# own configuration (``openhands_managed``); under ``native`` it sources its
|
|
390
|
+
# own skills and OpenHands injects none (#4019). A genuine discovery failure
|
|
391
|
+
# fails the launch loudly rather than silently producing a zero-skill agent.
|
|
346
392
|
available_skills = None
|
|
347
|
-
|
|
393
|
+
wants_skills = profile.agent_kind == "openhands" or (
|
|
394
|
+
acp_skill_sourcing == "openhands_managed"
|
|
395
|
+
)
|
|
396
|
+
if wants_skills:
|
|
348
397
|
try:
|
|
349
398
|
available_skills = discover_profile_skills()
|
|
350
399
|
except Exception as exc:
|
|
@@ -641,6 +690,7 @@ class ConversationService:
|
|
|
641
690
|
conversation_worktree_root: Path = field(
|
|
642
691
|
default=Path("/tmp/conversation-worktrees")
|
|
643
692
|
)
|
|
693
|
+
acp_skill_sourcing: ACPSkillSourcing = "native"
|
|
644
694
|
_event_services: dict[UUID, EventService] | None = field(default=None, init=False)
|
|
645
695
|
_conversation_records: dict[UUID, _ConversationRecord] = field(
|
|
646
696
|
default_factory=dict, init=False
|
|
@@ -1533,9 +1583,18 @@ class ConversationService:
|
|
|
1533
1583
|
self.cipher,
|
|
1534
1584
|
mcp_config,
|
|
1535
1585
|
load_memory=bool(stored_context and stored_context.load_memory),
|
|
1586
|
+
acp_skill_sourcing=self.acp_skill_sourcing,
|
|
1536
1587
|
)
|
|
1537
1588
|
request = request.model_copy(update={"agent": resolved_agent})
|
|
1538
1589
|
|
|
1590
|
+
request = request.model_copy(
|
|
1591
|
+
update={
|
|
1592
|
+
"agent": _apply_acp_skill_sourcing(
|
|
1593
|
+
request.agent, self.acp_skill_sourcing
|
|
1594
|
+
)
|
|
1595
|
+
}
|
|
1596
|
+
)
|
|
1597
|
+
|
|
1539
1598
|
additions = request.agent_launch_additions
|
|
1540
1599
|
suffix = (
|
|
1541
1600
|
additions.system_message_suffix_append.strip()
|
|
@@ -2238,6 +2297,7 @@ class ConversationService:
|
|
|
2238
2297
|
lease_ttl_seconds=config.lease_ttl_seconds,
|
|
2239
2298
|
conversation_idle_ttl_seconds=config.conversation_idle_ttl_seconds,
|
|
2240
2299
|
conversation_worktree_root=config.conversation_worktree_root,
|
|
2300
|
+
acp_skill_sourcing=config.acp_skill_sourcing,
|
|
2241
2301
|
)
|
|
2242
2302
|
|
|
2243
2303
|
async def _start_event_service(
|
|
@@ -13,6 +13,12 @@ ARG PORT=8000
|
|
|
13
13
|
# to bundle google-cloud-aiplatform so the resulting binary supports
|
|
14
14
|
# `vertex_ai/*` partner models (MiniMax, Qwen, Kimi MaaS endpoints).
|
|
15
15
|
ARG ENABLE_VERTEX=0
|
|
16
|
+
# Comma-separated ACP provider keys (see ACP_PROVIDERS in
|
|
17
|
+
# openhands-sdk/openhands/sdk/settings/acp_providers.py) to bake into the
|
|
18
|
+
# acp-providers stage below. The default reproduces the full provider set
|
|
19
|
+
# shipped today; an unknown key fails the build rather than silently
|
|
20
|
+
# installing nothing.
|
|
21
|
+
ARG INSTALL_ACP_PROVIDERS=claude-code,codex,gemini-cli
|
|
16
22
|
|
|
17
23
|
####################################################################################
|
|
18
24
|
# Builder (source mode)
|
|
@@ -92,6 +98,7 @@ RUN test -x /agent-server/dist/openhands-agent-server
|
|
|
92
98
|
# the per-target-image fallback).
|
|
93
99
|
####################################################################################
|
|
94
100
|
FROM python:3.13-bookworm AS acp-providers
|
|
101
|
+
ARG INSTALL_ACP_PROVIDERS
|
|
95
102
|
# A fresh top-level path, not nested under a directory (e.g. /opt) that base
|
|
96
103
|
# images commonly already populate (SWE-bench images ship /opt/miniconda3):
|
|
97
104
|
# COPY --from=<stage> only produces a byte-identical layer across different
|
|
@@ -100,12 +107,30 @@ FROM python:3.13-bookworm AS acp-providers
|
|
|
100
107
|
# stage above.
|
|
101
108
|
ENV ACP_NODE_DIR=/acp-node
|
|
102
109
|
RUN set -eux; \
|
|
110
|
+
ACP_WRAPPER_DIR=/opt/acp-wrappers; \
|
|
111
|
+
mkdir -p "$ACP_NODE_DIR" "$ACP_WRAPPER_DIR"; \
|
|
112
|
+
PACKAGES=""; \
|
|
113
|
+
WRAPPER_BINS=""; \
|
|
114
|
+
for provider in $(echo "$INSTALL_ACP_PROVIDERS" | tr ',' ' '); do \
|
|
115
|
+
case "$provider" in \
|
|
116
|
+
claude-code) PACKAGES="$PACKAGES @agentclientprotocol/claude-agent-acp@0.63.0"; \
|
|
117
|
+
WRAPPER_BINS="$WRAPPER_BINS claude-agent-acp";; \
|
|
118
|
+
codex) PACKAGES="$PACKAGES @agentclientprotocol/codex-acp@1.1.7"; \
|
|
119
|
+
WRAPPER_BINS="$WRAPPER_BINS codex-acp";; \
|
|
120
|
+
gemini-cli) PACKAGES="$PACKAGES @google/gemini-cli@0.46.0"; \
|
|
121
|
+
WRAPPER_BINS="$WRAPPER_BINS gemini";; \
|
|
122
|
+
*) echo "Unknown ACP provider '$provider' in INSTALL_ACP_PROVIDERS" \
|
|
123
|
+
"(expected one of: claude-code, codex, gemini-cli)" >&2; exit 1;; \
|
|
124
|
+
esac; \
|
|
125
|
+
done; \
|
|
126
|
+
if [ -z "$PACKAGES" ]; then \
|
|
127
|
+
echo "INSTALL_ACP_PROVIDERS is empty; no ACP providers will be installed"; \
|
|
128
|
+
exit 0; \
|
|
129
|
+
fi; \
|
|
103
130
|
apt-get -o Acquire::Retries=5 update; \
|
|
104
131
|
apt-get -o Acquire::Retries=5 install -y --no-install-recommends \
|
|
105
132
|
ca-certificates curl xz-utils; \
|
|
106
133
|
rm -rf /var/lib/apt/lists/*; \
|
|
107
|
-
ACP_WRAPPER_DIR=/opt/acp-wrappers; \
|
|
108
|
-
mkdir -p "$ACP_NODE_DIR" "$ACP_WRAPPER_DIR"; \
|
|
109
134
|
ARCH=$(uname -m); \
|
|
110
135
|
case "$ARCH" in \
|
|
111
136
|
x86_64|amd64) NARCH=x64; NODE_SHA256=69b09dba5c8dcb05c4e4273a4340db1005abeafe3927efda2bc5b249e80437ec;; \
|
|
@@ -119,11 +144,8 @@ RUN set -eux; \
|
|
|
119
144
|
tar -xJ --strip-components=1 -C "$ACP_NODE_DIR" -f "$NODE_TARBALL"; \
|
|
120
145
|
rm -f "$NODE_TARBALL"; \
|
|
121
146
|
"$ACP_NODE_DIR/bin/node" --version; \
|
|
122
|
-
PATH="$ACP_NODE_DIR/bin:$PATH" "$ACP_NODE_DIR/bin/npm" install -g \
|
|
123
|
-
|
|
124
|
-
@agentclientprotocol/codex-acp@1.1.7 \
|
|
125
|
-
@google/gemini-cli@0.46.0; \
|
|
126
|
-
for bin in claude-agent-acp codex-acp gemini; do \
|
|
147
|
+
PATH="$ACP_NODE_DIR/bin:$PATH" "$ACP_NODE_DIR/bin/npm" install -g $PACKAGES; \
|
|
148
|
+
for bin in $WRAPPER_BINS; do \
|
|
127
149
|
printf '#!/bin/sh\nPATH="%s/bin:$PATH" exec "%s/bin/%s" "$@"\n' \
|
|
128
150
|
"$ACP_NODE_DIR" "$ACP_NODE_DIR" "$bin" \
|
|
129
151
|
> "$ACP_WRAPPER_DIR/$bin"; \
|
|
@@ -244,6 +266,9 @@ ENV LC_ALL=C.UTF-8
|
|
|
244
266
|
ENV LANG=C.UTF-8
|
|
245
267
|
ENV OH_ENABLE_VNC=false
|
|
246
268
|
ENV LOG_JSON=true
|
|
269
|
+
# The ACP CLIs run without the user's host home here, so OpenHands supplies the
|
|
270
|
+
# managed skill catalog they cannot read for themselves (#4019).
|
|
271
|
+
ENV OH_ACP_SKILL_SOURCING=openhands_managed
|
|
247
272
|
EXPOSE ${PORT}
|
|
248
273
|
|
|
249
274
|
####################################################################################
|
|
@@ -356,6 +381,9 @@ USER ${USERNAME}
|
|
|
356
381
|
WORKDIR /
|
|
357
382
|
ENV OH_ENABLE_VNC=false
|
|
358
383
|
ENV LOG_JSON=true
|
|
384
|
+
# The ACP CLIs run without the user's host home here, so OpenHands supplies the
|
|
385
|
+
# managed skill catalog they cannot read for themselves (#4019).
|
|
386
|
+
ENV OH_ACP_SKILL_SOURCING=openhands_managed
|
|
359
387
|
EXPOSE ${PORT} ${NOVNC_PORT}
|
|
360
388
|
|
|
361
389
|
|
|
@@ -31,6 +31,7 @@ from pathlib import Path
|
|
|
31
31
|
from pydantic import BaseModel, Field, field_validator
|
|
32
32
|
|
|
33
33
|
from openhands.sdk.logger import IN_CI, get_logger, rolling_log_view
|
|
34
|
+
from openhands.sdk.settings.acp_providers import ACP_PROVIDERS
|
|
34
35
|
from openhands.sdk.workspace import PlatformType, TargetType
|
|
35
36
|
|
|
36
37
|
|
|
@@ -433,6 +434,27 @@ class BuildOptions(BaseModel):
|
|
|
433
434
|
"(e.g., at each release)."
|
|
434
435
|
),
|
|
435
436
|
)
|
|
437
|
+
install_acp_providers: str = Field(
|
|
438
|
+
default="claude-code,codex,gemini-cli",
|
|
439
|
+
description=(
|
|
440
|
+
"Comma-separated ACP provider keys (see ACP_PROVIDERS in "
|
|
441
|
+
"openhands-sdk/openhands/sdk/settings/acp_providers.py) to bake "
|
|
442
|
+
"into the image. Empty string installs none."
|
|
443
|
+
),
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
@field_validator("install_acp_providers")
|
|
447
|
+
@classmethod
|
|
448
|
+
def _valid_install_acp_providers(cls, v: str) -> str:
|
|
449
|
+
unknown = [
|
|
450
|
+
p for p in (t.strip() for t in v.split(",")) if p and p not in ACP_PROVIDERS
|
|
451
|
+
]
|
|
452
|
+
if unknown:
|
|
453
|
+
raise ValueError(
|
|
454
|
+
f"Unknown ACP provider(s) in install_acp_providers: "
|
|
455
|
+
f"{', '.join(unknown)}. Valid keys: {', '.join(ACP_PROVIDERS)}"
|
|
456
|
+
)
|
|
457
|
+
return v
|
|
436
458
|
|
|
437
459
|
@property
|
|
438
460
|
def short_sha(self) -> str:
|
|
@@ -834,6 +856,8 @@ def build_with_telemetry(opts: BuildOptions) -> BuildResult:
|
|
|
834
856
|
f"OPENHANDS_BUILD_GIT_SHA={opts.git_sha}",
|
|
835
857
|
"--build-arg",
|
|
836
858
|
f"OPENHANDS_BUILD_GIT_REF={opts.git_ref}",
|
|
859
|
+
"--build-arg",
|
|
860
|
+
f"INSTALL_ACP_PROVIDERS={opts.install_acp_providers}",
|
|
837
861
|
]
|
|
838
862
|
if push:
|
|
839
863
|
args += ["--platform", ",".join(opts.platforms), "--push"]
|
|
@@ -1051,6 +1075,16 @@ def main(argv: list[str]) -> int:
|
|
|
1051
1075
|
"as v1 and v1.2) in output. Should only be used for release builds."
|
|
1052
1076
|
),
|
|
1053
1077
|
)
|
|
1078
|
+
parser.add_argument(
|
|
1079
|
+
"--install-acp-providers",
|
|
1080
|
+
# os.environ.get, not _env(): an explicit empty string here means
|
|
1081
|
+
# "install none" and must survive, but _env() treats blank as unset.
|
|
1082
|
+
default=os.environ.get("INSTALL_ACP_PROVIDERS", "claude-code,codex,gemini-cli"),
|
|
1083
|
+
help=(
|
|
1084
|
+
"Comma-separated ACP provider keys to bake into the image "
|
|
1085
|
+
"(default from $INSTALL_ACP_PROVIDERS; empty string installs none)."
|
|
1086
|
+
),
|
|
1087
|
+
)
|
|
1054
1088
|
|
|
1055
1089
|
args = parser.parse_args(argv)
|
|
1056
1090
|
|
|
@@ -1080,6 +1114,7 @@ def main(argv: list[str]) -> int:
|
|
|
1080
1114
|
prebuilt_sdist=args.prebuilt_sdist,
|
|
1081
1115
|
arch=args.arch or None,
|
|
1082
1116
|
include_versioned_tag=args.versioned_tag,
|
|
1117
|
+
install_acp_providers=args.install_acp_providers,
|
|
1083
1118
|
)
|
|
1084
1119
|
|
|
1085
1120
|
# If running in GitHub Actions, write outputs directly to GITHUB_OUTPUT
|
|
@@ -1128,6 +1163,7 @@ def main(argv: list[str]) -> int:
|
|
|
1128
1163
|
prebuilt_sdist=args.prebuilt_sdist,
|
|
1129
1164
|
arch=args.arch or None,
|
|
1130
1165
|
include_versioned_tag=args.versioned_tag,
|
|
1166
|
+
install_acp_providers=args.install_acp_providers,
|
|
1131
1167
|
)
|
|
1132
1168
|
tags = build(opts)
|
|
1133
1169
|
|
{openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/pub_sub.py
RENAMED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
from abc import ABC, abstractmethod
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
|
-
from typing import TypeVar
|
|
4
|
+
from typing import ClassVar, TypeVar
|
|
5
5
|
from uuid import UUID, uuid4
|
|
6
6
|
|
|
7
|
+
from openhands.sdk.event import StreamingDeltaEvent
|
|
7
8
|
from openhands.sdk.logger import get_logger
|
|
8
9
|
|
|
9
10
|
|
|
@@ -13,6 +14,9 @@ T = TypeVar("T")
|
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class Subscriber[T](ABC):
|
|
17
|
+
# Deltas arrive at token rate; consumers opt in rather than inherit them.
|
|
18
|
+
receives_streaming_deltas: ClassVar[bool] = False
|
|
19
|
+
|
|
16
20
|
@abstractmethod
|
|
17
21
|
async def __call__(self, event: T):
|
|
18
22
|
"""Invoke this subscriber"""
|
|
@@ -87,6 +91,8 @@ class PubSub[T]:
|
|
|
87
91
|
event: The event to pass to all callbacks
|
|
88
92
|
"""
|
|
89
93
|
subscribers = list(self._subscribers.items())
|
|
94
|
+
if isinstance(event, StreamingDeltaEvent):
|
|
95
|
+
subscribers = [s for s in subscribers if s[1].receives_streaming_deltas]
|
|
90
96
|
if not subscribers:
|
|
91
97
|
return
|
|
92
98
|
|
{openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/sockets.py
RENAMED
|
@@ -539,6 +539,9 @@ def _is_websocket_connected(websocket: WebSocket) -> bool:
|
|
|
539
539
|
class _WebSocketSubscriber(Subscriber):
|
|
540
540
|
"""WebSocket subscriber for conversation events."""
|
|
541
541
|
|
|
542
|
+
# The live socket is what token streaming is for.
|
|
543
|
+
receives_streaming_deltas = True
|
|
544
|
+
|
|
542
545
|
websocket: WebSocket
|
|
543
546
|
|
|
544
547
|
async def __call__(self, event: Event):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openhands-agent-server
|
|
3
|
-
Version: 1.44.
|
|
3
|
+
Version: 1.44.1
|
|
4
4
|
Summary: OpenHands Agent Server - REST/WebSocket interface for OpenHands AI Agent
|
|
5
5
|
Project-URL: Source, https://github.com/OpenHands/software-agent-sdk
|
|
6
6
|
Project-URL: Homepage, https://github.com/OpenHands/software-agent-sdk
|
{openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/__init__.py
RENAMED
|
File without changes
|
{openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/__main__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/api.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/env_parser.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/git_router.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/llm_router.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/mcp_router.py
RENAMED
|
File without changes
|
{openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/middleware.py
RENAMED
|
File without changes
|
{openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/models.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/openapi.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/py.typed
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openhands_agent_server-1.44.0 → openhands_agent_server-1.44.1}/openhands/agent_server/utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|