openhands-sdk 1.8.2__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.
- openhands/sdk/agent/agent.py +64 -0
- openhands/sdk/agent/base.py +22 -10
- openhands/sdk/context/skills/skill.py +59 -1
- openhands/sdk/context/skills/utils.py +6 -65
- openhands/sdk/conversation/base.py +5 -0
- openhands/sdk/conversation/impl/remote_conversation.py +16 -3
- openhands/sdk/conversation/visualizer/base.py +23 -0
- openhands/sdk/critic/__init__.py +4 -1
- openhands/sdk/critic/base.py +17 -20
- openhands/sdk/critic/impl/__init__.py +2 -0
- openhands/sdk/critic/impl/agent_finished.py +9 -5
- openhands/sdk/critic/impl/api/__init__.py +18 -0
- openhands/sdk/critic/impl/api/chat_template.py +232 -0
- openhands/sdk/critic/impl/api/client.py +313 -0
- openhands/sdk/critic/impl/api/critic.py +90 -0
- openhands/sdk/critic/impl/api/taxonomy.py +180 -0
- openhands/sdk/critic/result.py +148 -0
- openhands/sdk/event/llm_convertible/action.py +10 -0
- openhands/sdk/event/llm_convertible/message.py +10 -0
- openhands/sdk/git/cached_repo.py +459 -0
- openhands/sdk/git/utils.py +118 -3
- openhands/sdk/hooks/__init__.py +7 -1
- openhands/sdk/hooks/config.py +154 -45
- openhands/sdk/llm/utils/model_features.py +3 -0
- openhands/sdk/plugin/__init__.py +17 -0
- openhands/sdk/plugin/fetch.py +231 -0
- openhands/sdk/plugin/plugin.py +61 -4
- openhands/sdk/plugin/types.py +394 -1
- {openhands_sdk-1.8.2.dist-info → openhands_sdk-1.9.0.dist-info}/METADATA +5 -1
- {openhands_sdk-1.8.2.dist-info → openhands_sdk-1.9.0.dist-info}/RECORD +32 -24
- {openhands_sdk-1.8.2.dist-info → openhands_sdk-1.9.0.dist-info}/WHEEL +1 -1
- {openhands_sdk-1.8.2.dist-info → openhands_sdk-1.9.0.dist-info}/top_level.txt +0 -0
openhands/sdk/plugin/types.py
CHANGED
|
@@ -2,12 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import json
|
|
5
6
|
import re
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
from typing import Any
|
|
8
9
|
|
|
9
10
|
import frontmatter
|
|
10
|
-
from pydantic import BaseModel, Field
|
|
11
|
+
from pydantic import BaseModel, Field, field_validator, model_validator
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# Directories to check for marketplace manifest
|
|
15
|
+
MARKETPLACE_MANIFEST_DIRS = [".plugin", ".claude-plugin"]
|
|
16
|
+
MARKETPLACE_MANIFEST_FILE = "marketplace.json"
|
|
17
|
+
|
|
18
|
+
# Type aliases for marketplace plugin entry configurations
|
|
19
|
+
# These provide better documentation than dict[str, Any] while remaining flexible
|
|
20
|
+
|
|
21
|
+
#: MCP server configuration dict. Keys are server names, values are server configs.
|
|
22
|
+
#: Each config should have 'command' (str), optional 'args' (list[str]), 'env'.
|
|
23
|
+
#: See https://gofastmcp.com/clients/client#configuration-format
|
|
24
|
+
type McpServersDict = dict[str, dict[str, Any]]
|
|
25
|
+
|
|
26
|
+
#: LSP server configuration dict. Keys are server names, values are server configs.
|
|
27
|
+
#: Each server config should have 'command' (str) and optional 'args' (list[str]),
|
|
28
|
+
#: 'extensionToLanguage' (dict mapping file extensions to language IDs).
|
|
29
|
+
#: See https://github.com/OpenHands/software-agent-sdk/issues/1745 for LSP support.
|
|
30
|
+
type LspServersDict = dict[str, dict[str, Any]]
|
|
31
|
+
|
|
32
|
+
#: Hooks configuration dict matching HookConfig.to_dict() structure.
|
|
33
|
+
#: Should have 'hooks' key with event types mapping to list of matchers.
|
|
34
|
+
#: See openhands.sdk.hooks.HookConfig for the full structure.
|
|
35
|
+
type HooksConfigDict = dict[str, Any]
|
|
11
36
|
|
|
12
37
|
|
|
13
38
|
class PluginAuthor(BaseModel):
|
|
@@ -224,3 +249,371 @@ class CommandDefinition(BaseModel):
|
|
|
224
249
|
source=str(command_path),
|
|
225
250
|
metadata=metadata,
|
|
226
251
|
)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
class MarketplaceOwner(BaseModel):
|
|
255
|
+
"""Owner information for a marketplace.
|
|
256
|
+
|
|
257
|
+
The owner represents the maintainer or team responsible for the marketplace.
|
|
258
|
+
"""
|
|
259
|
+
|
|
260
|
+
name: str = Field(description="Name of the maintainer or team")
|
|
261
|
+
email: str | None = Field(
|
|
262
|
+
default=None, description="Contact email for the maintainer"
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
class MarketplacePluginSource(BaseModel):
|
|
267
|
+
"""Plugin source specification for non-local sources.
|
|
268
|
+
|
|
269
|
+
Supports GitHub repositories and generic git URLs.
|
|
270
|
+
"""
|
|
271
|
+
|
|
272
|
+
source: str = Field(description="Source type: 'github' or 'url'")
|
|
273
|
+
repo: str | None = Field(
|
|
274
|
+
default=None, description="GitHub repository in 'owner/repo' format"
|
|
275
|
+
)
|
|
276
|
+
url: str | None = Field(default=None, description="Git URL for 'url' source type")
|
|
277
|
+
ref: str | None = Field(
|
|
278
|
+
default=None, description="Branch, tag, or commit reference"
|
|
279
|
+
)
|
|
280
|
+
path: str | None = Field(
|
|
281
|
+
default=None, description="Subdirectory path within the repository"
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
model_config = {"extra": "allow"}
|
|
285
|
+
|
|
286
|
+
@model_validator(mode="after")
|
|
287
|
+
def validate_source_fields(self) -> MarketplacePluginSource:
|
|
288
|
+
"""Validate that required fields are present based on source type."""
|
|
289
|
+
if self.source == "github" and not self.repo:
|
|
290
|
+
raise ValueError("GitHub source requires 'repo' field")
|
|
291
|
+
if self.source == "url" and not self.url:
|
|
292
|
+
raise ValueError("URL source requires 'url' field")
|
|
293
|
+
return self
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
class MarketplacePluginEntry(BaseModel):
|
|
297
|
+
"""Plugin entry in a marketplace.
|
|
298
|
+
|
|
299
|
+
Represents a single plugin available in the marketplace with its
|
|
300
|
+
metadata and source location.
|
|
301
|
+
|
|
302
|
+
This schema extends the core PluginManifest fields (name, version,
|
|
303
|
+
description, author) with marketplace-specific fields (source, category,
|
|
304
|
+
tags) and optional inline component definitions.
|
|
305
|
+
|
|
306
|
+
The `license` and `keywords` fields align with the AgentSkills standard
|
|
307
|
+
(https://agentskills.io/specification) used by Skill definitions.
|
|
308
|
+
|
|
309
|
+
Related schemas:
|
|
310
|
+
- PluginManifest: Core plugin metadata (plugin.json)
|
|
311
|
+
- Skill: Individual skill definitions with license, description
|
|
312
|
+
- Plugin: Loaded plugin with skills, commands, agents, hooks
|
|
313
|
+
"""
|
|
314
|
+
|
|
315
|
+
# Core fields (shared with PluginManifest)
|
|
316
|
+
name: str = Field(
|
|
317
|
+
description="Plugin identifier (kebab-case, no spaces). "
|
|
318
|
+
"Users see this when installing: /plugin install <name>@marketplace"
|
|
319
|
+
)
|
|
320
|
+
version: str | None = Field(default=None, description="Plugin version")
|
|
321
|
+
description: str | None = Field(
|
|
322
|
+
default=None, description="Brief plugin description"
|
|
323
|
+
)
|
|
324
|
+
author: PluginAuthor | None = Field(
|
|
325
|
+
default=None, description="Plugin author information"
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
# Marketplace-specific: source location
|
|
329
|
+
source: str | MarketplacePluginSource = Field(
|
|
330
|
+
description="Where to fetch the plugin from. Can be a relative path string "
|
|
331
|
+
"(e.g., './plugins/my-plugin') or a source object for GitHub/git URLs"
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
# Discovery and categorization (aligns with Skill.license for compatibility)
|
|
335
|
+
license: str | None = Field(
|
|
336
|
+
default=None,
|
|
337
|
+
description="SPDX license identifier (e.g., MIT, Apache-2.0). "
|
|
338
|
+
"Aligns with AgentSkills standard used by Skill definitions.",
|
|
339
|
+
)
|
|
340
|
+
keywords: list[str] = Field(
|
|
341
|
+
default_factory=list,
|
|
342
|
+
description="Tags for plugin discovery and categorization. "
|
|
343
|
+
"Aligns with AgentSkills standard used by Skill definitions.",
|
|
344
|
+
)
|
|
345
|
+
category: str | None = Field(
|
|
346
|
+
default=None, description="Plugin category for organization"
|
|
347
|
+
)
|
|
348
|
+
tags: list[str] = Field(default_factory=list, description="Tags for searchability")
|
|
349
|
+
|
|
350
|
+
# Repository/project links
|
|
351
|
+
homepage: str | None = Field(
|
|
352
|
+
default=None, description="Plugin homepage or documentation URL"
|
|
353
|
+
)
|
|
354
|
+
repository: str | None = Field(
|
|
355
|
+
default=None, description="Source code repository URL"
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
# Marketplace behavior control
|
|
359
|
+
strict: bool = Field(
|
|
360
|
+
default=True,
|
|
361
|
+
description="If True, plugin source must contain plugin.json. "
|
|
362
|
+
"If False, marketplace entry defines everything about the plugin.",
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
# Inline plugin component definitions (when strict=False)
|
|
366
|
+
# These fields are part of the marketplace schema for future use.
|
|
367
|
+
# Currently, Plugin.load() reads these from the plugin directory itself.
|
|
368
|
+
# TODO: Support loading inline definitions from marketplace entries.
|
|
369
|
+
commands: str | list[str] | None = Field(
|
|
370
|
+
default=None,
|
|
371
|
+
description="Custom paths to command files or directories. "
|
|
372
|
+
"Loaded as CommandDefinition objects by Plugin.",
|
|
373
|
+
)
|
|
374
|
+
agents: str | list[str] | None = Field(
|
|
375
|
+
default=None,
|
|
376
|
+
description="Custom paths to agent files. "
|
|
377
|
+
"Loaded as AgentDefinition objects by Plugin.",
|
|
378
|
+
)
|
|
379
|
+
hooks: str | HooksConfigDict | None = Field(
|
|
380
|
+
default=None,
|
|
381
|
+
description="Hooks configuration - either a path to hooks.json file (str) "
|
|
382
|
+
"or inline configuration dict. Loaded as HookConfig by Plugin. "
|
|
383
|
+
"See openhands.sdk.hooks.HookConfig for the expected structure.",
|
|
384
|
+
)
|
|
385
|
+
mcp_servers: McpServersDict | None = Field(
|
|
386
|
+
default=None,
|
|
387
|
+
alias="mcpServers",
|
|
388
|
+
description="MCP server configurations keyed by server name. "
|
|
389
|
+
"Each server config should have 'command' and optional 'args', 'env'. "
|
|
390
|
+
"Corresponds to Plugin.mcp_config loaded from .mcp.json. "
|
|
391
|
+
"See https://gofastmcp.com/clients/client#configuration-format",
|
|
392
|
+
)
|
|
393
|
+
lsp_servers: LspServersDict | None = Field(
|
|
394
|
+
default=None,
|
|
395
|
+
alias="lspServers",
|
|
396
|
+
description="LSP server configurations keyed by server name. "
|
|
397
|
+
"Each server config should have 'command' and optional 'args'.",
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
model_config = {"extra": "allow", "populate_by_name": True}
|
|
401
|
+
|
|
402
|
+
@field_validator("author", mode="before")
|
|
403
|
+
@classmethod
|
|
404
|
+
def _parse_author(cls, v: Any) -> Any:
|
|
405
|
+
"""Parse author from string format 'Name <email>' if needed."""
|
|
406
|
+
if isinstance(v, str):
|
|
407
|
+
return PluginAuthor.from_string(v)
|
|
408
|
+
return v
|
|
409
|
+
|
|
410
|
+
@field_validator("source", mode="before")
|
|
411
|
+
@classmethod
|
|
412
|
+
def _parse_source(cls, v: Any) -> Any:
|
|
413
|
+
"""Parse source dict to MarketplacePluginSource if needed."""
|
|
414
|
+
if isinstance(v, dict):
|
|
415
|
+
return MarketplacePluginSource.model_validate(v)
|
|
416
|
+
return v
|
|
417
|
+
|
|
418
|
+
def to_plugin_manifest(self) -> PluginManifest:
|
|
419
|
+
"""Convert marketplace entry to a PluginManifest.
|
|
420
|
+
|
|
421
|
+
Useful when strict=False and the marketplace entry defines the
|
|
422
|
+
plugin metadata directly without a separate plugin.json file.
|
|
423
|
+
|
|
424
|
+
Returns:
|
|
425
|
+
PluginManifest with the core fields from this entry.
|
|
426
|
+
"""
|
|
427
|
+
return PluginManifest(
|
|
428
|
+
name=self.name,
|
|
429
|
+
version=self.version or "1.0.0",
|
|
430
|
+
description=self.description or "",
|
|
431
|
+
author=self.author,
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
class MarketplaceMetadata(BaseModel):
|
|
436
|
+
"""Optional metadata for a marketplace."""
|
|
437
|
+
|
|
438
|
+
description: str | None = Field(
|
|
439
|
+
default=None, description="Brief marketplace description"
|
|
440
|
+
)
|
|
441
|
+
version: str | None = Field(default=None, description="Marketplace version")
|
|
442
|
+
plugin_root: str | None = Field(
|
|
443
|
+
default=None,
|
|
444
|
+
alias="pluginRoot",
|
|
445
|
+
description="Base directory prepended to relative plugin source paths. "
|
|
446
|
+
"E.g., './plugins' allows writing 'source: formatter' "
|
|
447
|
+
"instead of 'source: ./plugins/formatter'",
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
model_config = {"extra": "allow", "populate_by_name": True}
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
class Marketplace(BaseModel):
|
|
454
|
+
"""A plugin marketplace that lists available plugins.
|
|
455
|
+
|
|
456
|
+
Marketplaces follow the Claude Code marketplace structure for compatibility.
|
|
457
|
+
The marketplace.json file is located in `.plugin/` or `.claude-plugin/`
|
|
458
|
+
directory at the root of the marketplace repository.
|
|
459
|
+
|
|
460
|
+
Example marketplace.json:
|
|
461
|
+
```json
|
|
462
|
+
{
|
|
463
|
+
"name": "company-tools",
|
|
464
|
+
"owner": {
|
|
465
|
+
"name": "DevTools Team",
|
|
466
|
+
"email": "devtools@example.com"
|
|
467
|
+
},
|
|
468
|
+
"description": "Internal development tools",
|
|
469
|
+
"metadata": {
|
|
470
|
+
"version": "1.0.0",
|
|
471
|
+
"pluginRoot": "./plugins"
|
|
472
|
+
},
|
|
473
|
+
"plugins": [
|
|
474
|
+
{
|
|
475
|
+
"name": "code-formatter",
|
|
476
|
+
"source": "./plugins/formatter",
|
|
477
|
+
"description": "Automatic code formatting"
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
"name": "deployment-tools",
|
|
481
|
+
"source": {
|
|
482
|
+
"source": "github",
|
|
483
|
+
"repo": "company/deploy-plugin"
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
]
|
|
487
|
+
}
|
|
488
|
+
```
|
|
489
|
+
"""
|
|
490
|
+
|
|
491
|
+
name: str = Field(
|
|
492
|
+
description="Marketplace identifier (kebab-case, no spaces). "
|
|
493
|
+
"Users see this when installing plugins: /plugin install tool@<marketplace>"
|
|
494
|
+
)
|
|
495
|
+
owner: MarketplaceOwner = Field(description="Marketplace maintainer information")
|
|
496
|
+
description: str | None = Field(
|
|
497
|
+
default=None,
|
|
498
|
+
description="Brief marketplace description. Can also be in metadata.",
|
|
499
|
+
)
|
|
500
|
+
plugins: list[MarketplacePluginEntry] = Field(
|
|
501
|
+
default_factory=list, description="List of available plugins"
|
|
502
|
+
)
|
|
503
|
+
metadata: MarketplaceMetadata | None = Field(
|
|
504
|
+
default=None, description="Optional marketplace metadata"
|
|
505
|
+
)
|
|
506
|
+
path: str | None = Field(
|
|
507
|
+
default=None,
|
|
508
|
+
description="Path to the marketplace directory (set after loading)",
|
|
509
|
+
)
|
|
510
|
+
|
|
511
|
+
model_config = {"extra": "allow"}
|
|
512
|
+
|
|
513
|
+
@classmethod
|
|
514
|
+
def load(cls, marketplace_path: str | Path) -> Marketplace:
|
|
515
|
+
"""Load a marketplace from a directory.
|
|
516
|
+
|
|
517
|
+
Looks for marketplace.json in .plugin/ or .claude-plugin/ directories.
|
|
518
|
+
|
|
519
|
+
Args:
|
|
520
|
+
marketplace_path: Path to the marketplace directory.
|
|
521
|
+
|
|
522
|
+
Returns:
|
|
523
|
+
Loaded Marketplace instance.
|
|
524
|
+
|
|
525
|
+
Raises:
|
|
526
|
+
FileNotFoundError: If the marketplace directory or manifest doesn't exist.
|
|
527
|
+
ValueError: If the marketplace manifest is invalid.
|
|
528
|
+
"""
|
|
529
|
+
marketplace_dir = Path(marketplace_path).resolve()
|
|
530
|
+
if not marketplace_dir.is_dir():
|
|
531
|
+
raise FileNotFoundError(
|
|
532
|
+
f"Marketplace directory not found: {marketplace_dir}"
|
|
533
|
+
)
|
|
534
|
+
|
|
535
|
+
# Find manifest file
|
|
536
|
+
manifest_path = None
|
|
537
|
+
for manifest_dir in MARKETPLACE_MANIFEST_DIRS:
|
|
538
|
+
candidate = marketplace_dir / manifest_dir / MARKETPLACE_MANIFEST_FILE
|
|
539
|
+
if candidate.exists():
|
|
540
|
+
manifest_path = candidate
|
|
541
|
+
break
|
|
542
|
+
|
|
543
|
+
if manifest_path is None:
|
|
544
|
+
dirs = " or ".join(MARKETPLACE_MANIFEST_DIRS)
|
|
545
|
+
raise FileNotFoundError(
|
|
546
|
+
f"Marketplace manifest not found. "
|
|
547
|
+
f"Expected {MARKETPLACE_MANIFEST_FILE} in {dirs} "
|
|
548
|
+
f"directory under {marketplace_dir}"
|
|
549
|
+
)
|
|
550
|
+
|
|
551
|
+
try:
|
|
552
|
+
with open(manifest_path) as f:
|
|
553
|
+
data = json.load(f)
|
|
554
|
+
except json.JSONDecodeError as e:
|
|
555
|
+
raise ValueError(f"Invalid JSON in {manifest_path}: {e}") from e
|
|
556
|
+
|
|
557
|
+
return cls.model_validate({**data, "path": str(marketplace_dir)})
|
|
558
|
+
|
|
559
|
+
def get_plugin(self, name: str) -> MarketplacePluginEntry | None:
|
|
560
|
+
"""Get a plugin entry by name.
|
|
561
|
+
|
|
562
|
+
Args:
|
|
563
|
+
name: Plugin name to look up.
|
|
564
|
+
|
|
565
|
+
Returns:
|
|
566
|
+
MarketplacePluginEntry if found, None otherwise.
|
|
567
|
+
"""
|
|
568
|
+
for plugin in self.plugins:
|
|
569
|
+
if plugin.name == name:
|
|
570
|
+
return plugin
|
|
571
|
+
return None
|
|
572
|
+
|
|
573
|
+
def resolve_plugin_source(
|
|
574
|
+
self, plugin: MarketplacePluginEntry
|
|
575
|
+
) -> tuple[str, str | None, str | None]:
|
|
576
|
+
"""Resolve a plugin's source to a full path or URL.
|
|
577
|
+
|
|
578
|
+
Handles relative paths and plugin_root from metadata.
|
|
579
|
+
|
|
580
|
+
Args:
|
|
581
|
+
plugin: Plugin entry to resolve source for.
|
|
582
|
+
|
|
583
|
+
Returns:
|
|
584
|
+
Tuple of (source, ref, subpath) where:
|
|
585
|
+
- source: Resolved source string (path or URL)
|
|
586
|
+
- ref: Branch, tag, or commit reference (None for local paths)
|
|
587
|
+
- subpath: Subdirectory path within the repo (None if not specified)
|
|
588
|
+
|
|
589
|
+
Raises:
|
|
590
|
+
ValueError: If source object is invalid.
|
|
591
|
+
"""
|
|
592
|
+
source = plugin.source
|
|
593
|
+
|
|
594
|
+
# Handle complex source objects (GitHub, git URLs)
|
|
595
|
+
if isinstance(source, MarketplacePluginSource):
|
|
596
|
+
if source.source == "github" and source.repo:
|
|
597
|
+
return (f"github:{source.repo}", source.ref, source.path)
|
|
598
|
+
if source.source == "url" and source.url:
|
|
599
|
+
return (source.url, source.ref, source.path)
|
|
600
|
+
raise ValueError(
|
|
601
|
+
f"Invalid plugin source for '{plugin.name}': "
|
|
602
|
+
f"source type '{source.source}' is missing required field. "
|
|
603
|
+
f"'github' sources require 'repo', 'url' sources require 'url'"
|
|
604
|
+
)
|
|
605
|
+
|
|
606
|
+
# Source is a string path - check if it's absolute or a URL
|
|
607
|
+
if source.startswith(("/", "~")) or "://" in source:
|
|
608
|
+
return (source, None, None)
|
|
609
|
+
|
|
610
|
+
# Relative path: apply plugin_root if configured
|
|
611
|
+
if self.metadata and self.metadata.plugin_root:
|
|
612
|
+
plugin_root = self.metadata.plugin_root.rstrip("/")
|
|
613
|
+
source = f"{plugin_root}/{source.lstrip('./')}"
|
|
614
|
+
|
|
615
|
+
# Resolve relative paths to absolute if we know the marketplace path
|
|
616
|
+
if self.path and not source.startswith(("/", "~")):
|
|
617
|
+
source = str(Path(self.path) / source.lstrip("./"))
|
|
618
|
+
|
|
619
|
+
return (source, None, None)
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openhands-sdk
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.9.0
|
|
4
4
|
Summary: OpenHands SDK - Core functionality for building AI agents
|
|
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: deprecation>=2.1.0
|
|
7
11
|
Requires-Dist: fastmcp>=2.11.3
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
openhands/sdk/__init__.py,sha256=FNRcPyCbvwYGrbSCxox_UWIHK5JuyCLHCyPLPZRd5sA,2589
|
|
2
2
|
openhands/sdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
3
|
openhands/sdk/agent/__init__.py,sha256=yOn1ZCgTTq2VJlTzKDSzmWVPli1siBzqV89vlEHCwOg,137
|
|
4
|
-
openhands/sdk/agent/agent.py,sha256=
|
|
5
|
-
openhands/sdk/agent/base.py,sha256=
|
|
4
|
+
openhands/sdk/agent/agent.py,sha256=Uyvd49V7u5cMOxb_MonLLvWoW7KSHOhFjmWpnRQwtRQ,28960
|
|
5
|
+
openhands/sdk/agent/base.py,sha256=AwnHm7gscv8gXX3WhFcVVzc6jRrHcYKFO0J1vSPjnF0,19798
|
|
6
6
|
openhands/sdk/agent/utils.py,sha256=AY_7VBFaJ2iorfh0MiS-mXaK6f8ONDejNcbkde_xTGg,8274
|
|
7
7
|
openhands/sdk/agent/prompts/in_context_learning_example.j2,sha256=MGB0dPUlh6pwLoR_dBK-M3e5dtETX6C6WNjPcPixZmU,5512
|
|
8
8
|
openhands/sdk/agent/prompts/in_context_learning_example_suffix.j2,sha256=k3Zwnd7Iq7kL4lo307RDuu1mxWXn6pSLsEdvKEXN3BU,164
|
|
@@ -35,12 +35,12 @@ openhands/sdk/context/prompts/templates/skill_knowledge_info.j2,sha256=boqEsAIsz
|
|
|
35
35
|
openhands/sdk/context/prompts/templates/system_message_suffix.j2,sha256=4OWDu6YAXLQwmFLiIRJ7gEaR8z_RXYlvasmn1zeFP2g,2608
|
|
36
36
|
openhands/sdk/context/skills/__init__.py,sha256=dS6XJYLHlm_wboD-C42bRw4HCFu-o9dzCiFRpoyR1h0,938
|
|
37
37
|
openhands/sdk/context/skills/exceptions.py,sha256=tVBSbXTXG32nb1TebVAuzbNNHvn8GScpSM1bHCnQzvY,305
|
|
38
|
-
openhands/sdk/context/skills/skill.py,sha256=
|
|
38
|
+
openhands/sdk/context/skills/skill.py,sha256=s_krfMmqELS1YD24EZyU_vqUs-pmNUO1jZn8txeR8pY,36473
|
|
39
39
|
openhands/sdk/context/skills/trigger.py,sha256=ZGaDmMpJghnAEuTTYX6UepsA5nX1CSz83zK1Ox46vMk,756
|
|
40
40
|
openhands/sdk/context/skills/types.py,sha256=LvyCveHBSt2-g9Lbpr_eQMvOd4eEBjJb3irAWL-OzE0,1813
|
|
41
|
-
openhands/sdk/context/skills/utils.py,sha256=
|
|
41
|
+
openhands/sdk/context/skills/utils.py,sha256=kpJjVz_BQGjFrgO8QpBwTAqHbAU8spD6crOLI_ollac,11870
|
|
42
42
|
openhands/sdk/conversation/__init__.py,sha256=1-xh49S2KJhtAjkHDaDHU28UWhfQLl3CeFlo179gr00,1402
|
|
43
|
-
openhands/sdk/conversation/base.py,sha256=
|
|
43
|
+
openhands/sdk/conversation/base.py,sha256=btRjqHk4FkAJjIAi34fgkNUml9R-0qfsDsJgomlN-kI,9143
|
|
44
44
|
openhands/sdk/conversation/conversation.py,sha256=KN4mqYqrzc5LWksliDmRV-wNg1FdF0IVyaopOKeajas,6079
|
|
45
45
|
openhands/sdk/conversation/conversation_stats.py,sha256=ZlQ99kgG5YVCrZ4rqJlq63JaiInxX8jqv-q5lS7RN68,3038
|
|
46
46
|
openhands/sdk/conversation/event_store.py,sha256=JZF6AibFezcIzEw4IQqKHG8T8s53SfD_LkZycsOH6xY,7992
|
|
@@ -57,16 +57,22 @@ openhands/sdk/conversation/title_utils.py,sha256=j40-dP-Oes-mhU2xUC7fCC8cB0wkMdb
|
|
|
57
57
|
openhands/sdk/conversation/types.py,sha256=q_yc3VNc8r3cdmvPXzpj7HvdLeDqv-37hCgOWMU65a4,1507
|
|
58
58
|
openhands/sdk/conversation/impl/__init__.py,sha256=DmDFyNR4RU8eiMocKf2j9eBQomipP-rrJgU1LoVWTDA,220
|
|
59
59
|
openhands/sdk/conversation/impl/local_conversation.py,sha256=Egdr7YLdZNcjmKlijracQigaoSBoiR2roinIWV2Ubnc,29559
|
|
60
|
-
openhands/sdk/conversation/impl/remote_conversation.py,sha256=
|
|
60
|
+
openhands/sdk/conversation/impl/remote_conversation.py,sha256=f06zRwZWC_vgqQaxTP-wTd7_5whGTsT-smu5_uamMek,38605
|
|
61
61
|
openhands/sdk/conversation/visualizer/__init__.py,sha256=0LXpKlt2eJcrqP1z6jQP_nLx23V8ErnQkKYSxvUp0_A,275
|
|
62
|
-
openhands/sdk/conversation/visualizer/base.py,sha256=
|
|
62
|
+
openhands/sdk/conversation/visualizer/base.py,sha256=oMg-JvQc34ebYdC3J9itHraoB2u3MdQ6E77AKiTmu30,3198
|
|
63
63
|
openhands/sdk/conversation/visualizer/default.py,sha256=k-3-l1j8H_EOEn_pfzsUczcc4JDhQni7AUQZgZ2TpB4,11885
|
|
64
|
-
openhands/sdk/critic/__init__.py,sha256=
|
|
65
|
-
openhands/sdk/critic/base.py,sha256=
|
|
66
|
-
openhands/sdk/critic/
|
|
67
|
-
openhands/sdk/critic/impl/
|
|
64
|
+
openhands/sdk/critic/__init__.py,sha256=uWVXxar10vSzEMOAtTCOlJiA0TPwPIKuQkVmabA5A2s,372
|
|
65
|
+
openhands/sdk/critic/base.py,sha256=LvYhDpJgN_xLzyvQgTjKEUN9wrB8i54DKjjQyfaIu04,1158
|
|
66
|
+
openhands/sdk/critic/result.py,sha256=ptx1aRDp3kvhrssTS9a8QSLQRtq6SYYDb4EoVHIytng,5129
|
|
67
|
+
openhands/sdk/critic/impl/__init__.py,sha256=if0S347bcciabr288SuwgDqeRJlzoBBmnp6OiSwrVYM,403
|
|
68
|
+
openhands/sdk/critic/impl/agent_finished.py,sha256=qnNFnmVg71rFkcYWcCt-IbN_1ZepWrGGgmRFrDJoFNI,2938
|
|
68
69
|
openhands/sdk/critic/impl/empty_patch.py,sha256=_fvVq77DFye9hS5vAodUa14LMCzG8VrhNqo22jLJ77Y,1566
|
|
69
70
|
openhands/sdk/critic/impl/pass_critic.py,sha256=kikTVZmve0DCTkQnhRutdUSff0OLbqH0WqRxGXP103Y,1239
|
|
71
|
+
openhands/sdk/critic/impl/api/__init__.py,sha256=cgpLoVholRbE6YyCu4KUs0InG6LHcdBVAM1eq1ja8xQ,375
|
|
72
|
+
openhands/sdk/critic/impl/api/chat_template.py,sha256=qu5tJMi1Nuynp4FsV_MG_sAz4V6hpHactbxoGZGkAJE,7564
|
|
73
|
+
openhands/sdk/critic/impl/api/client.py,sha256=M43mM76EQY66ANfX2tIUeE_wvV6M5MdMYlbHDIqxQ34,10980
|
|
74
|
+
openhands/sdk/critic/impl/api/critic.py,sha256=tmi2e_YJF8Kw1WHmWfaR95QjC2uiFALUmGW7gV225D4,3448
|
|
75
|
+
openhands/sdk/critic/impl/api/taxonomy.py,sha256=dVM0t4x4H4a_vCkc4jl40agLR1PgQlW-V9NO6_wkhGo,6440
|
|
70
76
|
openhands/sdk/event/__init__.py,sha256=ir-jRVA0QjEbFuDzJOYRq2kXTgUHs8eJ7_skoCRNzr8,1141
|
|
71
77
|
openhands/sdk/event/base.py,sha256=QpQtYfYiZqaKWA-2xa4GEmk2qkUHKD6x-OIAk7SMnDs,5568
|
|
72
78
|
openhands/sdk/event/condenser.py,sha256=sne9CxhhNq9UvJMKuKcw8tXuMgutuGh85TbbZHwYhPQ,2481
|
|
@@ -77,17 +83,18 @@ openhands/sdk/event/token.py,sha256=QlEbBrfZaHe9tp8-Ot4vTd0T-_Vj7lpUqKfVJcVHOeI,
|
|
|
77
83
|
openhands/sdk/event/types.py,sha256=3hyFDBNtYTGEwLV6muodJO7iDMoZTC4D0Dd4vYeF2dE,271
|
|
78
84
|
openhands/sdk/event/user_action.py,sha256=JnU5LDYY1aMgSSEPkSz5wrQ6XScHKcdsFi_tnjcWO9U,661
|
|
79
85
|
openhands/sdk/event/llm_convertible/__init__.py,sha256=04kTm2F3M8oGpkBbePb35R1jpd5ZpA-XYWHMYyJ46sQ,553
|
|
80
|
-
openhands/sdk/event/llm_convertible/action.py,sha256=
|
|
81
|
-
openhands/sdk/event/llm_convertible/message.py,sha256=
|
|
86
|
+
openhands/sdk/event/llm_convertible/action.py,sha256=WbLNQMS0DJFMRrDLOtqhBTjQ2T1U7brIZ_b7LzMt52w,6680
|
|
87
|
+
openhands/sdk/event/llm_convertible/message.py,sha256=I_chCsUuqBhc5INnNZUDsGa464TJYJBb7lLbOdHRFeM,5413
|
|
82
88
|
openhands/sdk/event/llm_convertible/observation.py,sha256=LF09On5eI9Cadb_yK4bhh_-pvnf5onsr-MzayL0r0h0,5045
|
|
83
89
|
openhands/sdk/event/llm_convertible/system.py,sha256=iGyizThTd8lxekgHK7E-4Z5xTF_Zb7_y7J9zAitL7lI,2332
|
|
90
|
+
openhands/sdk/git/cached_repo.py,sha256=Xe3fmum5By-8vaYFg-WzBoLhhL0ClKEfazVfkvhlHYs,15806
|
|
84
91
|
openhands/sdk/git/exceptions.py,sha256=T3_jdOzilK39y1_ilMQ3Z3RSkxHJZafUoZTzOiuGji0,991
|
|
85
92
|
openhands/sdk/git/git_changes.py,sha256=RQwva8j5mq09WoTrmNcZMBHrGUzn0CPYilW6k1kJCD0,8339
|
|
86
93
|
openhands/sdk/git/git_diff.py,sha256=osaSys05wp9i-_KqfcYEHhNFzzU0B2qCn9vChfRPL0g,3820
|
|
87
94
|
openhands/sdk/git/models.py,sha256=himUK-g49iQHMBfJlVr4tTUOXQhudJUR-vcntTeFkPM,349
|
|
88
|
-
openhands/sdk/git/utils.py,sha256=
|
|
89
|
-
openhands/sdk/hooks/__init__.py,sha256=
|
|
90
|
-
openhands/sdk/hooks/config.py,sha256=
|
|
95
|
+
openhands/sdk/git/utils.py,sha256=8wXm9UhqjuJEcjRUSkVK7V7XgxMnIMLgwh2ShbrpZfk,10691
|
|
96
|
+
openhands/sdk/hooks/__init__.py,sha256=BC3ANExTcfec_UrD_yrYZbgMh3WVY86gI5OLs5YNvB0,901
|
|
97
|
+
openhands/sdk/hooks/config.py,sha256=1aOXzx23qOKH15sDOsg4dXXe-9v0QokGCygZd8gdLEg,9462
|
|
91
98
|
openhands/sdk/hooks/conversation_hooks.py,sha256=I62p4wrS2Jz3GatlNghUYY-57CnRrcxlvQawWFXU1-0,10629
|
|
92
99
|
openhands/sdk/hooks/executor.py,sha256=K-nE92r_6dsNV_ISEhoqIyxGvYzD9wgwSyHFM456NX4,5016
|
|
93
100
|
openhands/sdk/hooks/manager.py,sha256=woce5tBGaygQvKL_p3K77Xq9K8QMwsbZYjEmDCGHoIY,5864
|
|
@@ -118,7 +125,7 @@ openhands/sdk/llm/router/base.py,sha256=izGHV17UctI70t2OjhuLgVAV7gGZ51vbZ4WpWJxb
|
|
|
118
125
|
openhands/sdk/llm/router/impl/multimodal.py,sha256=uKFVm7b3Jr0xCC1lvP-cVn-fIkTv24-pK3xKlOJahz4,3034
|
|
119
126
|
openhands/sdk/llm/router/impl/random.py,sha256=oBHoFTBMa9OeDyg-rV4siLCkN6rKYL0uDlZMEseB3ro,656
|
|
120
127
|
openhands/sdk/llm/utils/metrics.py,sha256=4zD0Hkc9Oc4qcDcVZUX13RyggyObshUbz4Ik9W1uIw4,11592
|
|
121
|
-
openhands/sdk/llm/utils/model_features.py,sha256=
|
|
128
|
+
openhands/sdk/llm/utils/model_features.py,sha256=y-l501P5tbIvY-Vxi5TeE4QyobW9-xRMW0AzCKTidMw,6295
|
|
122
129
|
openhands/sdk/llm/utils/model_info.py,sha256=1mFYA7OcEyUB6k1doao8_w1XT7UMM_DAm57HcTpKkLw,2628
|
|
123
130
|
openhands/sdk/llm/utils/model_prompt_spec.py,sha256=onw9-y7x0aJS8IOjNzeqhdvcFNwK1l_s0XgurWlnj5o,2587
|
|
124
131
|
openhands/sdk/llm/utils/retry_mixin.py,sha256=M-hXp8EwP1FjNN6tgHiv133BtUQgRr9Kz_ZWxeAJLGA,4765
|
|
@@ -137,9 +144,10 @@ openhands/sdk/mcp/utils.py,sha256=AazuWwhEwHBs1JKXfNVJDArkgQfmuj4MrPQwxDo52ds,29
|
|
|
137
144
|
openhands/sdk/observability/__init__.py,sha256=JKHDWoj01igaCUChdXgKmstESiMmbAK9CN5XzT2H7fo,122
|
|
138
145
|
openhands/sdk/observability/laminar.py,sha256=U2AbSWpPcUYdrzx__-BEg4LPW13f1l-Hk-V4qeBmE3k,5053
|
|
139
146
|
openhands/sdk/observability/utils.py,sha256=N0p8ACs2WKS1PyFPjiU-PPng6WsAe15ZpFViFAw1gO0,576
|
|
140
|
-
openhands/sdk/plugin/__init__.py,sha256=
|
|
141
|
-
openhands/sdk/plugin/
|
|
142
|
-
openhands/sdk/plugin/
|
|
147
|
+
openhands/sdk/plugin/__init__.py,sha256=Sf9dM3EPmKrsMDXpQyNaROPWW6bPiUUHTtaMUwfd-ww,987
|
|
148
|
+
openhands/sdk/plugin/fetch.py,sha256=CBQfQrJWyjl3LaaM2LkBVHwY44nixZrRO4sePKZwtFk,7599
|
|
149
|
+
openhands/sdk/plugin/plugin.py,sha256=mdsGZucgamJVxgb-ZyHRbmNl9spn2F63O8iZjE8O-sI,12372
|
|
150
|
+
openhands/sdk/plugin/types.py,sha256=vDTfarbHD88B8VjJtSvd0gTSBy-MbGBh8Zg6RuhBoBk,22920
|
|
143
151
|
openhands/sdk/secret/__init__.py,sha256=Y-M2WPfYLfVYZSFZGTf6MDOnjsL5SayETtA4t9X0gjw,348
|
|
144
152
|
openhands/sdk/secret/secrets.py,sha256=RFhyTBxwGgeYoojD56DtI_4CL43R8emo8WzvUH-8Z4w,3281
|
|
145
153
|
openhands/sdk/security/__init__.py,sha256=x2-a0fsImxSM2msHl7gV0fENWyZbpY7-HW_CBwNrPZY,89
|
|
@@ -178,7 +186,7 @@ openhands/sdk/workspace/remote/__init__.py,sha256=eKkj6NOESMUBGDVC6_L2Wfuc4K6G-m
|
|
|
178
186
|
openhands/sdk/workspace/remote/async_remote_workspace.py,sha256=MfnYoXvx_tZ7MKDGJCofnkYAJxfBKqNtM2Qprx3QQRk,5608
|
|
179
187
|
openhands/sdk/workspace/remote/base.py,sha256=t-qbouLxkAsPKvVblXWWHKixHsLFz4bw3l9LW_n9_6o,6152
|
|
180
188
|
openhands/sdk/workspace/remote/remote_workspace_mixin.py,sha256=CzHfnLUIra5sgPkP9kcggb1vHGOPpYQzLsHvGO2rRt0,10963
|
|
181
|
-
openhands_sdk-1.
|
|
182
|
-
openhands_sdk-1.
|
|
183
|
-
openhands_sdk-1.
|
|
184
|
-
openhands_sdk-1.
|
|
189
|
+
openhands_sdk-1.9.0.dist-info/METADATA,sha256=eFfiNSYi5c9iW-3H_zvNdAt0MxQbour-o2VlOvUDsp0,858
|
|
190
|
+
openhands_sdk-1.9.0.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
|
|
191
|
+
openhands_sdk-1.9.0.dist-info/top_level.txt,sha256=jHgVu9I0Blam8BXFgedoGKfglPF8XvW1TsJFIjcgP4E,10
|
|
192
|
+
openhands_sdk-1.9.0.dist-info/RECORD,,
|
|
File without changes
|