openhands-sdk 1.8.2__py3-none-any.whl → 1.9.1__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.
Files changed (32) hide show
  1. openhands/sdk/agent/agent.py +64 -0
  2. openhands/sdk/agent/base.py +22 -10
  3. openhands/sdk/context/skills/skill.py +59 -1
  4. openhands/sdk/context/skills/utils.py +6 -65
  5. openhands/sdk/conversation/base.py +5 -0
  6. openhands/sdk/conversation/impl/remote_conversation.py +16 -3
  7. openhands/sdk/conversation/visualizer/base.py +23 -0
  8. openhands/sdk/critic/__init__.py +4 -1
  9. openhands/sdk/critic/base.py +17 -20
  10. openhands/sdk/critic/impl/__init__.py +2 -0
  11. openhands/sdk/critic/impl/agent_finished.py +9 -5
  12. openhands/sdk/critic/impl/api/__init__.py +18 -0
  13. openhands/sdk/critic/impl/api/chat_template.py +232 -0
  14. openhands/sdk/critic/impl/api/client.py +313 -0
  15. openhands/sdk/critic/impl/api/critic.py +90 -0
  16. openhands/sdk/critic/impl/api/taxonomy.py +180 -0
  17. openhands/sdk/critic/result.py +148 -0
  18. openhands/sdk/event/llm_convertible/action.py +10 -0
  19. openhands/sdk/event/llm_convertible/message.py +10 -0
  20. openhands/sdk/git/cached_repo.py +459 -0
  21. openhands/sdk/git/utils.py +118 -3
  22. openhands/sdk/hooks/__init__.py +7 -1
  23. openhands/sdk/hooks/config.py +154 -45
  24. openhands/sdk/llm/utils/model_features.py +3 -0
  25. openhands/sdk/plugin/__init__.py +17 -0
  26. openhands/sdk/plugin/fetch.py +231 -0
  27. openhands/sdk/plugin/plugin.py +61 -4
  28. openhands/sdk/plugin/types.py +394 -1
  29. {openhands_sdk-1.8.2.dist-info → openhands_sdk-1.9.1.dist-info}/METADATA +5 -1
  30. {openhands_sdk-1.8.2.dist-info → openhands_sdk-1.9.1.dist-info}/RECORD +32 -24
  31. {openhands_sdk-1.8.2.dist-info → openhands_sdk-1.9.1.dist-info}/WHEEL +1 -1
  32. {openhands_sdk-1.8.2.dist-info → openhands_sdk-1.9.1.dist-info}/top_level.txt +0 -0
@@ -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.8.2
3
+ Version: 1.9.1
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=X00fM2FSjlZF36Y5E_-JkYS-n_x5RoEupTj2Rp6Gj_k,26361
5
- openhands/sdk/agent/base.py,sha256=GDT1sJsU3wIya0pYhfibuwQI3eejTNJ6DCFVUZnvg3k,19350
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=TVpfxbDYE0NSmD1lfaEh_hO0iMSvV42b5Sph30QSD5c,34603
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=Wp7oMEXczAmgNWc2KXTZuCLUtlxPbElqcoS3oETTKJg,13760
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=sVpfqtyTVi7_d8rzI_gemZGHusVrNy9S_yNJmTGMEq8,8964
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=G8Asgg99gBxmQa2wsp-S6u8nEqJOq9DYjpzVNAi6AEI,38011
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=77DdRdHAPSESxRCYyRRSOK7ROBlljscxogkFFr4YgM0,2323
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=gb6gtnUGMkWHkRnNRdV0lR6Mxa43nLfELY9nxso40Wo,291
65
- openhands/sdk/critic/base.py,sha256=9Rm5Sq3GWzFSi5KFBtmZgDlpCl997PUrpPTBlMQatjA,1094
66
- openhands/sdk/critic/impl/__init__.py,sha256=WcLchRYwxMvfQPVhnb9BCK_Ue1ZWh8WT0Pu13dkQuSE,324
67
- openhands/sdk/critic/impl/agent_finished.py,sha256=Je8ZLpWlohXA7JWrer7FBbbViiWqbeVDPIBvBQPAGHY,2935
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=QbYQYSIDStsFR8R8dfrY00aVpcHOKtqqawD40rijVl8,6316
81
- openhands/sdk/event/llm_convertible/message.py,sha256=yeEI2zy3IuOu2fAyCkr8fhdn6qwiLcgR4MYAot4CGpw,5048
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=5vSxC8p_6Yu-LEZ_qEJL5oSHHlAZtg0cax1Z7Z1vWLw,7262
89
- openhands/sdk/hooks/__init__.py,sha256=J2ZO0Ajnw6IDbrDuA1BtXlzoGkSIZ3VcF0Haihgd8pE,854
90
- openhands/sdk/hooks/config.py,sha256=QeUA2joRglX-fXazoSXpZLrKpQO9jwW7htNQ_D5WtFU,5964
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=vAsRbD0dTIVM7SZUjO3Vwyd2CfDYxcR8zy7KWzkt5e0,6215
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=NCzotHxOH7FfNVcJFy0ttNHOXf3xesHbDQ5sBa1twAI,474
141
- openhands/sdk/plugin/plugin.py,sha256=WiY_3n69kDYDPwY2srurYWbOLYO5ZiPtwJ0yOHc45jU,9970
142
- openhands/sdk/plugin/types.py,sha256=kYrlSKNnxZJiIB2XaJ2j4iKnG9jTXT8DIz54IzU6qyE,8122
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.8.2.dist-info/METADATA,sha256=FMs6gTS3Ot5Y62nDPD0H77XKgFgvvD0mj2b97cXrSQk,578
182
- openhands_sdk-1.8.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
183
- openhands_sdk-1.8.2.dist-info/top_level.txt,sha256=jHgVu9I0Blam8BXFgedoGKfglPF8XvW1TsJFIjcgP4E,10
184
- openhands_sdk-1.8.2.dist-info/RECORD,,
189
+ openhands_sdk-1.9.1.dist-info/METADATA,sha256=BYfyhzTTPM85dNhsi8PyiIhYJeuv_clMhhZ--ZfXy9A,858
190
+ openhands_sdk-1.9.1.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
191
+ openhands_sdk-1.9.1.dist-info/top_level.txt,sha256=jHgVu9I0Blam8BXFgedoGKfglPF8XvW1TsJFIjcgP4E,10
192
+ openhands_sdk-1.9.1.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