openhands-agent-server 1.8.1__py3-none-any.whl → 1.9.0__py3-none-any.whl

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.
@@ -0,0 +1,181 @@
1
+ """Skills router for OpenHands Agent Server.
2
+
3
+ This module defines the HTTP API endpoints for skill operations.
4
+ Business logic is delegated to skills_service.py.
5
+ """
6
+
7
+ from typing import Literal
8
+
9
+ from fastapi import APIRouter
10
+ from pydantic import BaseModel, Field
11
+
12
+ from openhands.agent_server.skills_service import (
13
+ ExposedUrlData,
14
+ load_all_skills,
15
+ sync_public_skills,
16
+ )
17
+
18
+
19
+ skills_router = APIRouter(prefix="/skills", tags=["Skills"])
20
+
21
+
22
+ class ExposedUrl(BaseModel):
23
+ """Represents an exposed URL from the sandbox."""
24
+
25
+ name: str
26
+ url: str
27
+ port: int
28
+
29
+
30
+ class OrgConfig(BaseModel):
31
+ """Configuration for loading organization-level skills."""
32
+
33
+ repository: str = Field(description="Selected repository (e.g., 'owner/repo')")
34
+ provider: str = Field(
35
+ description="Git provider type: github, gitlab, azure, bitbucket"
36
+ )
37
+ org_repo_url: str = Field(
38
+ description="Pre-authenticated Git URL for the organization repository. "
39
+ "Contains sensitive credentials - handle with care and avoid logging."
40
+ )
41
+ org_name: str = Field(description="Organization name")
42
+
43
+
44
+ class SandboxConfig(BaseModel):
45
+ """Configuration for loading sandbox-specific skills."""
46
+
47
+ exposed_urls: list[ExposedUrl] = Field(
48
+ default_factory=list,
49
+ description="List of exposed URLs from the sandbox",
50
+ )
51
+
52
+
53
+ class SkillsRequest(BaseModel):
54
+ """Request body for loading skills."""
55
+
56
+ load_public: bool = Field(
57
+ default=True, description="Load public skills from OpenHands/skills repo"
58
+ )
59
+ load_user: bool = Field(
60
+ default=True, description="Load user skills from ~/.openhands/skills/"
61
+ )
62
+ load_project: bool = Field(
63
+ default=True, description="Load project skills from workspace"
64
+ )
65
+ load_org: bool = Field(default=True, description="Load organization-level skills")
66
+ project_dir: str | None = Field(
67
+ default=None, description="Workspace directory path for project skills"
68
+ )
69
+ org_config: OrgConfig | None = Field(
70
+ default=None, description="Organization skills configuration"
71
+ )
72
+ sandbox_config: SandboxConfig | None = Field(
73
+ default=None, description="Sandbox skills configuration"
74
+ )
75
+
76
+
77
+ class SkillInfo(BaseModel):
78
+ """Skill information returned by the API."""
79
+
80
+ name: str
81
+ type: Literal["repo", "knowledge", "agentskills"]
82
+ content: str
83
+ triggers: list[str] = Field(default_factory=list)
84
+ source: str | None = None
85
+ description: str | None = None
86
+ is_agentskills_format: bool = False
87
+
88
+
89
+ class SkillsResponse(BaseModel):
90
+ """Response containing all available skills."""
91
+
92
+ skills: list[SkillInfo]
93
+ sources: dict[str, int] = Field(
94
+ default_factory=dict,
95
+ description="Count of skills loaded from each source",
96
+ )
97
+
98
+
99
+ class SyncResponse(BaseModel):
100
+ """Response from skill sync operation."""
101
+
102
+ status: Literal["success", "error"]
103
+ message: str
104
+
105
+
106
+ @skills_router.post("", response_model=SkillsResponse)
107
+ def get_skills(request: SkillsRequest) -> SkillsResponse:
108
+ """Load and merge skills from all configured sources.
109
+
110
+ Skills are loaded from multiple sources and merged with the following
111
+ precedence (later overrides earlier for duplicate names):
112
+ 1. Sandbox skills (lowest) - Exposed URLs from sandbox
113
+ 2. Public skills - From GitHub OpenHands/skills repository
114
+ 3. User skills - From ~/.openhands/skills/
115
+ 4. Organization skills - From {org}/.openhands or equivalent
116
+ 5. Project skills (highest) - From {workspace}/.openhands/skills/
117
+
118
+ Args:
119
+ request: SkillsRequest containing configuration for which sources to load.
120
+
121
+ Returns:
122
+ SkillsResponse containing merged skills and source counts.
123
+ """
124
+ # Convert Pydantic models to service data types
125
+ sandbox_urls = None
126
+ if request.sandbox_config and request.sandbox_config.exposed_urls:
127
+ sandbox_urls = [
128
+ ExposedUrlData(name=url.name, url=url.url, port=url.port)
129
+ for url in request.sandbox_config.exposed_urls
130
+ ]
131
+
132
+ org_repo_url = None
133
+ org_name = None
134
+ if request.org_config:
135
+ org_repo_url = request.org_config.org_repo_url
136
+ org_name = request.org_config.org_name
137
+
138
+ # Call the service
139
+ result = load_all_skills(
140
+ load_public=request.load_public,
141
+ load_user=request.load_user,
142
+ load_project=request.load_project,
143
+ load_org=request.load_org,
144
+ project_dir=request.project_dir,
145
+ org_repo_url=org_repo_url,
146
+ org_name=org_name,
147
+ sandbox_exposed_urls=sandbox_urls,
148
+ )
149
+
150
+ # Convert Skill objects to SkillInfo for response
151
+ skills_info = [
152
+ SkillInfo(
153
+ name=info.name,
154
+ type=info.type,
155
+ content=info.content,
156
+ triggers=info.triggers,
157
+ source=info.source,
158
+ description=info.description,
159
+ is_agentskills_format=info.is_agentskills_format,
160
+ )
161
+ for info in (skill.to_skill_info() for skill in result.skills)
162
+ ]
163
+
164
+ return SkillsResponse(skills=skills_info, sources=result.sources)
165
+
166
+
167
+ @skills_router.post("/sync", response_model=SyncResponse)
168
+ def sync_skills() -> SyncResponse:
169
+ """Force refresh of public skills from GitHub repository.
170
+
171
+ This triggers a git pull on the cached skills repository to get
172
+ the latest skills from the OpenHands/skills repository.
173
+
174
+ Returns:
175
+ SyncResponse indicating success or failure.
176
+ """
177
+ success, message = sync_public_skills()
178
+ return SyncResponse(
179
+ status="success" if success else "error",
180
+ message=message,
181
+ )
@@ -0,0 +1,401 @@
1
+ """Skills service for OpenHands Agent Server.
2
+
3
+ This module contains the business logic for skill loading and management,
4
+ keeping the router clean and focused on HTTP concerns.
5
+
6
+ Skill Sources:
7
+ - Public skills: GitHub OpenHands/skills repository
8
+ - User skills: ~/.openhands/skills/ and ~/.openhands/microagents/
9
+ - Project skills: {workspace}/.openhands/skills/, .cursorrules, agents.md
10
+ - Organization skills: {org}/.openhands or {org}/openhands-config
11
+ - Sandbox skills: Exposed URLs from sandbox environment
12
+
13
+ Precedence (later overrides earlier):
14
+ sandbox < public < user < org < project
15
+ """
16
+
17
+ import os
18
+ import shutil
19
+ import subprocess
20
+ import tempfile
21
+ from dataclasses import dataclass
22
+ from pathlib import Path
23
+
24
+ from openhands.sdk.context.skills import (
25
+ Skill,
26
+ load_project_skills,
27
+ load_public_skills,
28
+ load_user_skills,
29
+ )
30
+ from openhands.sdk.context.skills.skill import (
31
+ PUBLIC_SKILLS_BRANCH,
32
+ PUBLIC_SKILLS_REPO,
33
+ load_skills_from_dir,
34
+ )
35
+ from openhands.sdk.context.skills.utils import (
36
+ get_skills_cache_dir,
37
+ update_skills_repository,
38
+ )
39
+ from openhands.sdk.logger import get_logger
40
+
41
+
42
+ logger = get_logger(__name__)
43
+
44
+
45
+ # Content template for sandbox work hosts skill
46
+ WORK_HOSTS_SKILL_CONTENT = (
47
+ "The user has access to the following hosts for accessing "
48
+ "a web application, each of which has a corresponding port:\n{hosts}"
49
+ )
50
+
51
+ # Prefix for sandbox URLs that should be exposed as work_hosts skill.
52
+ # URLs with names starting with this prefix represent web applications
53
+ # or services running in the sandbox that the agent should be aware of.
54
+ SANDBOX_WORKER_URL_PREFIX = "WORKER_"
55
+
56
+
57
+ @dataclass
58
+ class ExposedUrlData:
59
+ """Internal representation of an exposed URL from the sandbox."""
60
+
61
+ name: str
62
+ url: str
63
+ port: int
64
+
65
+
66
+ @dataclass
67
+ class SkillLoadResult:
68
+ """Result of loading skills from all sources."""
69
+
70
+ skills: list[Skill]
71
+ sources: dict[str, int]
72
+
73
+
74
+ def load_org_skills_from_url(
75
+ org_repo_url: str,
76
+ org_name: str,
77
+ working_dir: str | Path | None = None,
78
+ ) -> list[Skill]:
79
+ """Load skills from an organization repository.
80
+
81
+ This function clones an organization-level skills repository to a temporary
82
+ directory, loads skills from the skills/ and microagents/ directories, and
83
+ then cleans up the temporary directory.
84
+
85
+ The org_repo_url should be a pre-authenticated Git URL (e.g., containing
86
+ credentials or tokens) as provided by the app-server.
87
+
88
+ Note:
89
+ This is a blocking I/O operation that may take up to 120 seconds due to
90
+ the git clone timeout. When called from FastAPI endpoints defined with
91
+ `def` (not `async def`), FastAPI automatically runs this in a thread
92
+ pool to avoid blocking the event loop. Do not call this function
93
+ directly from async code without wrapping it in asyncio.to_thread().
94
+
95
+ Args:
96
+ org_repo_url: Pre-authenticated Git URL for the organization repository.
97
+ This should be a full Git URL that includes authentication.
98
+ org_name: Name of the organization (used for temp directory naming).
99
+ working_dir: Optional working directory for git operations. If None,
100
+ uses a subdirectory of the system temp directory.
101
+
102
+ Returns:
103
+ List of Skill objects loaded from the organization repository.
104
+ Returns empty list if the repository doesn't exist or loading fails.
105
+ """
106
+ all_skills: list[Skill] = []
107
+
108
+ # Determine the temporary directory for cloning
109
+ if working_dir:
110
+ base_dir = Path(working_dir) if isinstance(working_dir, str) else working_dir
111
+ temp_dir = base_dir / f"_org_skills_{org_name}"
112
+ else:
113
+ temp_dir = Path(tempfile.gettempdir()) / f"openhands_org_skills_{org_name}"
114
+
115
+ try:
116
+ # Clean up any existing temp directory
117
+ if temp_dir.exists():
118
+ shutil.rmtree(temp_dir)
119
+
120
+ # Clone the organization repository (shallow clone for efficiency)
121
+ logger.info(f"Cloning organization skills repository for {org_name}")
122
+ try:
123
+ subprocess.run(
124
+ [
125
+ "git",
126
+ "clone",
127
+ "--depth",
128
+ "1",
129
+ org_repo_url,
130
+ str(temp_dir),
131
+ ],
132
+ check=True,
133
+ capture_output=True,
134
+ timeout=120,
135
+ env={**os.environ, "GIT_TERMINAL_PROMPT": "0"},
136
+ )
137
+ except subprocess.CalledProcessError:
138
+ # Repository doesn't exist or access denied - this is expected.
139
+ # Note: We intentionally don't log stderr as it may contain credentials.
140
+ logger.debug(
141
+ f"Organization repository not found or access denied for {org_name}"
142
+ )
143
+ return all_skills
144
+ except subprocess.TimeoutExpired:
145
+ logger.warning(
146
+ f"Git clone timed out for organization repository {org_name}"
147
+ )
148
+ return all_skills
149
+
150
+ logger.debug(f"Successfully cloned org repository to {temp_dir}")
151
+
152
+ # Load skills from skills/ directory (preferred)
153
+ skills_dir = temp_dir / "skills"
154
+ if skills_dir.exists():
155
+ try:
156
+ repo_skills, knowledge_skills, agent_skills = load_skills_from_dir(
157
+ skills_dir
158
+ )
159
+ for skills_dict in [repo_skills, knowledge_skills, agent_skills]:
160
+ all_skills.extend(skills_dict.values())
161
+ logger.debug(
162
+ f"Loaded {len(all_skills)} skills from org skills/ directory"
163
+ )
164
+ except Exception as e:
165
+ logger.warning(f"Failed to load skills from {skills_dir}: {e}")
166
+
167
+ # Load skills from microagents/ directory (legacy support)
168
+ microagents_dir = temp_dir / "microagents"
169
+ if microagents_dir.exists():
170
+ seen_names = {s.name for s in all_skills}
171
+ try:
172
+ repo_skills, knowledge_skills, agent_skills = load_skills_from_dir(
173
+ microagents_dir
174
+ )
175
+ for skills_dict in [repo_skills, knowledge_skills, agent_skills]:
176
+ for name, skill in skills_dict.items():
177
+ if name not in seen_names:
178
+ all_skills.append(skill)
179
+ seen_names.add(name)
180
+ else:
181
+ logger.debug(
182
+ f"Skipping duplicate org skill '{name}' "
183
+ "from microagents/"
184
+ )
185
+ except Exception as e:
186
+ logger.warning(f"Failed to load skills from {microagents_dir}: {e}")
187
+
188
+ logger.info(
189
+ f"Loaded {len(all_skills)} organization skills for {org_name}: "
190
+ f"{[s.name for s in all_skills]}"
191
+ )
192
+
193
+ except Exception as e:
194
+ logger.warning(f"Failed to load organization skills for {org_name}: {e}")
195
+
196
+ finally:
197
+ # Clean up the temporary directory
198
+ if temp_dir.exists():
199
+ try:
200
+ shutil.rmtree(temp_dir)
201
+ logger.debug(f"Cleaned up temp directory {temp_dir}")
202
+ except Exception as e:
203
+ logger.warning(f"Failed to clean up temp directory {temp_dir}: {e}")
204
+
205
+ return all_skills
206
+
207
+
208
+ def create_sandbox_skill(
209
+ exposed_urls: list[ExposedUrlData],
210
+ ) -> Skill | None:
211
+ """Create a skill from sandbox exposed URLs.
212
+
213
+ This function creates a skill that informs the agent about web applications
214
+ and services available in the sandbox environment via exposed ports/URLs.
215
+
216
+ Only URLs with names starting with SANDBOX_WORKER_URL_PREFIX are included,
217
+ as these represent web applications the agent should be aware of.
218
+
219
+ Args:
220
+ exposed_urls: List of ExposedUrlData objects containing name, url, and port.
221
+
222
+ Returns:
223
+ A Skill object with work_hosts content if there are matching URLs,
224
+ or None if no relevant URLs are provided.
225
+ """
226
+ if not exposed_urls:
227
+ return None
228
+
229
+ # Filter for URLs with the worker prefix
230
+ worker_urls = [
231
+ url for url in exposed_urls if url.name.startswith(SANDBOX_WORKER_URL_PREFIX)
232
+ ]
233
+
234
+ if not worker_urls:
235
+ return None
236
+
237
+ # Build the hosts content
238
+ hosts_lines = []
239
+ for url_info in worker_urls:
240
+ hosts_lines.append(f"* {url_info.url} (port {url_info.port})")
241
+
242
+ hosts_content = "\n".join(hosts_lines)
243
+ content = WORK_HOSTS_SKILL_CONTENT.format(hosts=hosts_content)
244
+
245
+ return Skill(
246
+ name="work_hosts",
247
+ content=content,
248
+ trigger=None, # Always active
249
+ source=None, # Programmatically generated
250
+ )
251
+
252
+
253
+ def merge_skills(skill_lists: list[list[Skill]]) -> list[Skill]:
254
+ """Merge multiple skill lists with precedence.
255
+
256
+ Later lists override earlier lists for duplicate names.
257
+
258
+ Args:
259
+ skill_lists: List of skill lists to merge in order of precedence.
260
+
261
+ Returns:
262
+ Merged list of skills with duplicates resolved.
263
+ """
264
+ skills_by_name: dict[str, Skill] = {}
265
+
266
+ for skill_list in skill_lists:
267
+ for skill in skill_list:
268
+ if skill.name in skills_by_name:
269
+ logger.info(
270
+ f"Overriding skill '{skill.name}' from earlier source "
271
+ "with later source"
272
+ )
273
+ skills_by_name[skill.name] = skill
274
+
275
+ return list(skills_by_name.values())
276
+
277
+
278
+ def load_all_skills(
279
+ load_public: bool = True,
280
+ load_user: bool = True,
281
+ load_project: bool = True,
282
+ load_org: bool = True,
283
+ project_dir: str | None = None,
284
+ org_repo_url: str | None = None,
285
+ org_name: str | None = None,
286
+ sandbox_exposed_urls: list[ExposedUrlData] | None = None,
287
+ ) -> SkillLoadResult:
288
+ """Load and merge skills from all configured sources.
289
+
290
+ Skills are loaded from multiple sources and merged with the following
291
+ precedence (later overrides earlier for duplicate names):
292
+ 1. Sandbox skills (lowest) - Exposed URLs from sandbox
293
+ 2. Public skills - From GitHub OpenHands/skills repository
294
+ 3. User skills - From ~/.openhands/skills/
295
+ 4. Organization skills - From {org}/.openhands or equivalent
296
+ 5. Project skills (highest) - From {workspace}/.openhands/skills/
297
+
298
+ Args:
299
+ load_public: Whether to load public skills from OpenHands/skills repo.
300
+ load_user: Whether to load user skills from ~/.openhands/skills/.
301
+ load_project: Whether to load project skills from workspace.
302
+ load_org: Whether to load organization-level skills.
303
+ project_dir: Workspace directory path for project skills.
304
+ org_repo_url: Pre-authenticated Git URL for org skills.
305
+ org_name: Organization name for org skills.
306
+ sandbox_exposed_urls: List of exposed URLs from sandbox.
307
+
308
+ Returns:
309
+ SkillLoadResult containing merged skills and source counts.
310
+ """
311
+ sources: dict[str, int] = {}
312
+ skill_lists: list[list[Skill]] = []
313
+
314
+ # 1. Load sandbox skills (lowest precedence)
315
+ sandbox_skills: list[Skill] = []
316
+ if sandbox_exposed_urls:
317
+ sandbox_skill = create_sandbox_skill(sandbox_exposed_urls)
318
+ if sandbox_skill:
319
+ sandbox_skills.append(sandbox_skill)
320
+ sources["sandbox"] = len(sandbox_skills)
321
+ skill_lists.append(sandbox_skills)
322
+
323
+ # 2. Load public skills
324
+ public_skills: list[Skill] = []
325
+ if load_public:
326
+ try:
327
+ public_skills = load_public_skills()
328
+ logger.info(f"Loaded {len(public_skills)} public skills")
329
+ except Exception as e:
330
+ logger.warning(f"Failed to load public skills: {e}")
331
+ sources["public"] = len(public_skills)
332
+ skill_lists.append(public_skills)
333
+
334
+ # 3. Load user skills
335
+ user_skills: list[Skill] = []
336
+ if load_user:
337
+ try:
338
+ user_skills = load_user_skills()
339
+ logger.info(f"Loaded {len(user_skills)} user skills")
340
+ except Exception as e:
341
+ logger.warning(f"Failed to load user skills: {e}")
342
+ sources["user"] = len(user_skills)
343
+ skill_lists.append(user_skills)
344
+
345
+ # 4. Load organization skills
346
+ org_skills: list[Skill] = []
347
+ if load_org and org_repo_url and org_name:
348
+ try:
349
+ org_skills = load_org_skills_from_url(
350
+ org_repo_url=org_repo_url,
351
+ org_name=org_name,
352
+ )
353
+ logger.info(f"Loaded {len(org_skills)} organization skills")
354
+ except Exception as e:
355
+ logger.warning(f"Failed to load organization skills: {e}")
356
+ sources["org"] = len(org_skills)
357
+ skill_lists.append(org_skills)
358
+
359
+ # 5. Load project skills (highest precedence)
360
+ project_skills: list[Skill] = []
361
+ if load_project and project_dir:
362
+ try:
363
+ project_skills = load_project_skills(project_dir)
364
+ logger.info(f"Loaded {len(project_skills)} project skills")
365
+ except Exception as e:
366
+ logger.warning(f"Failed to load project skills: {e}")
367
+ sources["project"] = len(project_skills)
368
+ skill_lists.append(project_skills)
369
+
370
+ # Merge all skills with precedence
371
+ all_skills = merge_skills(skill_lists)
372
+
373
+ logger.info(
374
+ f"Returning {len(all_skills)} total skills: {[s.name for s in all_skills]}"
375
+ )
376
+
377
+ return SkillLoadResult(skills=all_skills, sources=sources)
378
+
379
+
380
+ def sync_public_skills() -> tuple[bool, str]:
381
+ """Force refresh of public skills from GitHub repository.
382
+
383
+ This triggers a git pull on the cached skills repository to get
384
+ the latest skills from the OpenHands/skills repository.
385
+
386
+ Returns:
387
+ Tuple of (success: bool, message: str).
388
+ """
389
+ try:
390
+ cache_dir = get_skills_cache_dir()
391
+ result = update_skills_repository(
392
+ PUBLIC_SKILLS_REPO, PUBLIC_SKILLS_BRANCH, cache_dir
393
+ )
394
+
395
+ if result:
396
+ return (True, "Skills repository synced successfully")
397
+ else:
398
+ return (False, "Failed to sync skills repository")
399
+ except Exception as e:
400
+ logger.warning(f"Failed to sync skills repository: {e}")
401
+ return (False, f"Sync failed: {str(e)}")
@@ -142,7 +142,7 @@ async def _send_event(event: Event, websocket: WebSocket):
142
142
  dumped = event.model_dump(mode="json")
143
143
  await websocket.send_json(dumped)
144
144
  except Exception:
145
- logger.exception("error_sending_event:{event}", stack_info=True)
145
+ logger.exception("error_sending_event: %r", event, stack_info=True)
146
146
 
147
147
 
148
148
  @dataclass
@@ -160,7 +160,7 @@ async def _send_bash_event(event: BashEventBase, websocket: WebSocket):
160
160
  dumped = event.model_dump(mode="json")
161
161
  await websocket.send_json(dumped)
162
162
  except Exception:
163
- logger.exception("error_sending_event:{event}", stack_info=True)
163
+ logger.exception("error_sending_bash_event: %r", event, stack_info=True)
164
164
 
165
165
 
166
166
  @dataclass
@@ -1,7 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openhands-agent-server
3
- Version: 1.8.1
3
+ Version: 1.9.0
4
4
  Summary: OpenHands Agent Server - REST/WebSocket interface for OpenHands AI Agent
5
+ Project-URL: Source, https://github.com/OpenHands/software-agent-sdk
6
+ Project-URL: Homepage, https://github.com/OpenHands/software-agent-sdk
7
+ Project-URL: Documentation, https://docs.openhands.dev/sdk
8
+ Project-URL: Bug Tracker, https://github.com/OpenHands/software-agent-sdk/issues
5
9
  Requires-Python: >=3.12
6
10
  Requires-Dist: aiosqlite>=0.19
7
11
  Requires-Dist: alembic>=1.13
@@ -1,39 +1,41 @@
1
1
  openhands/agent_server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- openhands/agent_server/__main__.py,sha256=TDeBZxdloHltvdRDiFcwDZ5SW0sk3NONTDoqGt2IhsE,1619
3
- openhands/agent_server/api.py,sha256=M9__D_T6m3_0dlkbZzahMM_SPF-j0uYAnoJoCvkZGQM,11901
4
- openhands/agent_server/bash_router.py,sha256=9aKNxzdKSlKoYJ3ngvdf4rjv1_iNNmpRoZkqhJ4qhxQ,3203
2
+ openhands/agent_server/__main__.py,sha256=QCdBRWVV9gNtPwRwYieQisvKsmJljjJ8f293RhtHl_w,3642
3
+ openhands/agent_server/api.py,sha256=LqU4BWXc4VQ29sIiABZykznSMJ_dUQEOHixxeLBTuGY,12009
4
+ openhands/agent_server/bash_router.py,sha256=9GRsLeVZgqR1XeayJvBBycVqfg5O-Fye2ek75kGCuno,3353
5
5
  openhands/agent_server/bash_service.py,sha256=QgTSyQoxlk52BnZuo35xlX7u7-3xs5BvvaKU3AEva_w,14083
6
- openhands/agent_server/config.py,sha256=EQdoN39cOeI63zEpAQ7WyhMpo-9tHNTsLMalU0d4kHw,5479
6
+ openhands/agent_server/config.py,sha256=EKxVV0QyD3KLLtFxNuUextxJplLW_zZ32nPFhli6ozA,6403
7
7
  openhands/agent_server/conversation_router.py,sha256=lz-dnfPXrVBiGZ9GhYqfteCWJ4pq7AcnWqxsKWwCfc0,11178
8
8
  openhands/agent_server/conversation_service.py,sha256=Oj8IBSY9ZYbduOOBatd3I0H_kSyH7xi22Y_MyhfL5Tk,27058
9
9
  openhands/agent_server/dependencies.py,sha256=H3zyOc8uthpXseB3E7rWNccKIj7PlyfcgCYwFvmFq4c,2629
10
10
  openhands/agent_server/desktop_router.py,sha256=OaCmevO33eUo3jTwiXBmQ3uT3ONu4-tqgBfYpZWrHSA,1349
11
11
  openhands/agent_server/desktop_service.py,sha256=iCwQJXK4DvGuBXKOQ1oko60wXkf_pYHCubOzBsd2k60,7415
12
- openhands/agent_server/env_parser.py,sha256=NBa5p_JGYJVMMdCJJ_jicW1iAGS6h8AG_HVlcQ2dxaY,14929
12
+ openhands/agent_server/env_parser.py,sha256=GDTLy9-k-GBOvpVI8VlYuqN-kLRZw6ieR2TgWFsOIms,16400
13
13
  openhands/agent_server/event_router.py,sha256=XM46zcqPOXStISfihzsPXPfsW_23E50brmBHk04ncVI,6156
14
- openhands/agent_server/event_service.py,sha256=N-RKeXox4dMsZv-8brwYRtt5p2axfznPfd6C-8TKXk4,25189
15
- openhands/agent_server/file_router.py,sha256=ysDjIKzDNbi7m95JrH6kP6BYcBhTJhTpJVMkNtYVYhs,4011
16
- openhands/agent_server/git_router.py,sha256=g0cVeyJ_mUAuieZ3bbTl5Xvm94FnNwRt07iNBASiXgU,844
17
- openhands/agent_server/logging_config.py,sha256=DPPxIl1tUVGiui-JVSKgs73KXPFfDNX2ivGuZSkbLe4,1832
18
- openhands/agent_server/middleware.py,sha256=9wuAurN8TCXTCRl6ANIMoFSvHeZsYDLFXIrBH15yhQU,1117
14
+ openhands/agent_server/event_service.py,sha256=EFjIkWbTrROAv2zFzsIOhcRDTZIhf0uIGQStNDg8nFY,26838
15
+ openhands/agent_server/file_router.py,sha256=MqFmTcDFE42EEPwRncBtT-Vu8_U78OZfC6pm0tnlBZk,4161
16
+ openhands/agent_server/git_router.py,sha256=z-jbkY4me1HuLOWTzJu_osI19ZGGri5ffYujVAWe31s,974
17
+ openhands/agent_server/logging_config.py,sha256=b3N5LVGuwDd0bBsMxrVdCHa8N1Nsreawgi23hTkDrro,3985
18
+ openhands/agent_server/middleware.py,sha256=WQN5T14E58cvG2ZG6qfaJL4Dk9DqUGqD5tyAva1Un_4,1407
19
19
  openhands/agent_server/models.py,sha256=R3WSDIQN1burIyV4Dz05YITR9WWcC_pXgZDk-7WG6hw,9723
20
20
  openhands/agent_server/openapi.py,sha256=RJWaOnM9NjzrH-fJi3PoIBv5d0sH5z8zZTdvYzSqoFU,482
21
21
  openhands/agent_server/pub_sub.py,sha256=yPB84Wub7A2uwUWsW_grbxomWmKaWyGuo6dVNUzg1FU,2755
22
22
  openhands/agent_server/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
23
  openhands/agent_server/server_details_router.py,sha256=LCa0NQ3SOw-IgJh3rDJL5GolFOpZcX_-XEbDLHF8dN4,950
24
- openhands/agent_server/sockets.py,sha256=pWjuRpWeqPw-T5svqb5mO7JCAVcCptQyHxc6lDYHhvY,6606
24
+ openhands/agent_server/skills_router.py,sha256=mYiIAOGJxnpMz_OsbtP2cBjHNvo3qYUXqGPHKQs_pEw,5601
25
+ openhands/agent_server/skills_service.py,sha256=hmyo1FtUEwfPWk1NnMyGGhvIqPwO28Mjk3chUw_98XM,14125
26
+ openhands/agent_server/sockets.py,sha256=UhZr_QoHsIX7x_tzvk9_AJa2S8KJmRr4syhIU4He8Rs,6617
25
27
  openhands/agent_server/tool_preload_service.py,sha256=2QuFyn7jC4Ifq2aUhs9j8876wSBdY0eeoLo4qEVi-yA,2341
26
28
  openhands/agent_server/tool_router.py,sha256=vM_9UKUzfChLK9B9Z3DL4VtKNdDw4w635knu9N36y0c,676
27
29
  openhands/agent_server/utils.py,sha256=ajivE_kGCJ9qUhF9H3Qu7DUKg7uDvDQk16JcO3XntEs,1926
28
30
  openhands/agent_server/vscode_router.py,sha256=tPmXzN6teuqMa1jvKS4Q3aWpk9p9wsp4LleKoDvkYGs,2133
29
31
  openhands/agent_server/vscode_service.py,sha256=xS_vwIU5W5KwXTbOTzHPkPRIB4xbjmYlAygmRfBOAF8,7606
30
- openhands/agent_server/docker/Dockerfile,sha256=NuGBEpRbcFA2e8xfOhsG2iVIz5J047xJqWEAvw5X4sw,10711
31
- openhands/agent_server/docker/build.py,sha256=Egsge_yIVCeiJ_gvva6VyPeQtOp0WjZFsWvybFV-OfQ,27555
32
+ openhands/agent_server/docker/Dockerfile,sha256=rdFlMdI_uITipzR7-pPEGFx2Ld-jYhOBfGKONRk4dbU,10724
33
+ openhands/agent_server/docker/build.py,sha256=UgHoLkgHZtgWqloG2MQ2RCzc6zTIyttOFL_vUzmw_c0,28189
32
34
  openhands/agent_server/docker/wallpaper.svg,sha256=FR2g_b5mzz0x5EvRTKO93ASnWPagAyeS9RI3vRQBAsw,11532
33
35
  openhands/agent_server/vscode_extensions/openhands-settings/extension.js,sha256=xoCKZ6YXlzlTWnTC52HuzX0sn9s77Vma-47WgEibO88,858
34
36
  openhands/agent_server/vscode_extensions/openhands-settings/package.json,sha256=eCkuBBYEVArEjpp7c_m0H207OCLEygZhBLUEkeFNWOg,289
35
- openhands_agent_server-1.8.1.dist-info/METADATA,sha256=3uVueui8cp5vLxXTC7da-_bLIcDl9Lsd_wizydGpfbM,468
36
- openhands_agent_server-1.8.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
37
- openhands_agent_server-1.8.1.dist-info/entry_points.txt,sha256=uLQzPhqDqe85Dy9DvPiBE2CeqkwCryggr1Ty_mq65NA,70
38
- openhands_agent_server-1.8.1.dist-info/top_level.txt,sha256=jHgVu9I0Blam8BXFgedoGKfglPF8XvW1TsJFIjcgP4E,10
39
- openhands_agent_server-1.8.1.dist-info/RECORD,,
37
+ openhands_agent_server-1.9.0.dist-info/METADATA,sha256=xU9j2XKQ9PD1vPh5e7APdXkY4JzfFRlWv-BBe9ZeIEE,748
38
+ openhands_agent_server-1.9.0.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
39
+ openhands_agent_server-1.9.0.dist-info/entry_points.txt,sha256=uLQzPhqDqe85Dy9DvPiBE2CeqkwCryggr1Ty_mq65NA,70
40
+ openhands_agent_server-1.9.0.dist-info/top_level.txt,sha256=jHgVu9I0Blam8BXFgedoGKfglPF8XvW1TsJFIjcgP4E,10
41
+ openhands_agent_server-1.9.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5