openhands-sdk 1.3.0__py3-none-any.whl → 1.4.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 (38) hide show
  1. openhands/sdk/__init__.py +4 -0
  2. openhands/sdk/agent/agent.py +55 -22
  3. openhands/sdk/agent/base.py +8 -1
  4. openhands/sdk/agent/prompts/system_prompt.j2 +1 -11
  5. openhands/sdk/agent/utils.py +5 -0
  6. openhands/sdk/context/agent_context.py +30 -0
  7. openhands/sdk/context/skills/__init__.py +2 -0
  8. openhands/sdk/context/skills/skill.py +202 -1
  9. openhands/sdk/conversation/__init__.py +5 -1
  10. openhands/sdk/conversation/base.py +15 -6
  11. openhands/sdk/conversation/conversation.py +10 -1
  12. openhands/sdk/conversation/conversation_stats.py +38 -1
  13. openhands/sdk/conversation/fifo_lock.py +14 -8
  14. openhands/sdk/conversation/impl/local_conversation.py +21 -5
  15. openhands/sdk/conversation/secret_source.py +1 -1
  16. openhands/sdk/conversation/state.py +8 -0
  17. openhands/sdk/conversation/types.py +5 -0
  18. openhands/sdk/event/conversation_state.py +8 -0
  19. openhands/sdk/llm/__init__.py +3 -0
  20. openhands/sdk/llm/llm.py +82 -16
  21. openhands/sdk/llm/llm_registry.py +1 -1
  22. openhands/sdk/llm/options/chat_options.py +12 -24
  23. openhands/sdk/llm/options/responses_options.py +9 -1
  24. openhands/sdk/llm/router/base.py +3 -0
  25. openhands/sdk/llm/streaming.py +9 -0
  26. openhands/sdk/llm/utils/model_features.py +12 -0
  27. openhands/sdk/logger/logger.py +7 -0
  28. openhands/sdk/tool/tool.py +18 -1
  29. openhands/sdk/utils/models.py +90 -9
  30. openhands/sdk/utils/truncate.py +81 -8
  31. openhands/sdk/workspace/__init__.py +3 -1
  32. openhands/sdk/workspace/models.py +7 -1
  33. openhands/sdk/workspace/remote/async_remote_workspace.py +22 -1
  34. openhands/sdk/workspace/remote/base.py +13 -0
  35. {openhands_sdk-1.3.0.dist-info → openhands_sdk-1.4.1.dist-info}/METADATA +2 -2
  36. {openhands_sdk-1.3.0.dist-info → openhands_sdk-1.4.1.dist-info}/RECORD +38 -37
  37. {openhands_sdk-1.3.0.dist-info → openhands_sdk-1.4.1.dist-info}/WHEEL +0 -0
  38. {openhands_sdk-1.3.0.dist-info → openhands_sdk-1.4.1.dist-info}/top_level.txt +0 -0
@@ -24,6 +24,7 @@ class ModelFeatures:
24
24
  supports_responses_api: bool
25
25
  force_string_serializer: bool
26
26
  send_reasoning_content: bool
27
+ supports_prompt_cache_retention: bool
27
28
 
28
29
 
29
30
  # Pattern tables capturing current behavior. Keep patterns lowercase.
@@ -68,6 +69,14 @@ PROMPT_CACHE_PATTERNS: list[str] = [
68
69
  "claude-opus-4-5",
69
70
  ]
70
71
 
72
+ # Models that support a top-level prompt_cache_retention parameter
73
+ PROMPT_CACHE_RETENTION_PATTERNS: list[str] = [
74
+ # OpenAI GPT-5+ family
75
+ "gpt-5",
76
+ # GPT-4.1 too
77
+ "gpt-4.1",
78
+ ]
79
+
71
80
  SUPPORTS_STOP_WORDS_FALSE_PATTERNS: list[str] = [
72
81
  # o-series families don't support stop words
73
82
  "o1",
@@ -118,6 +127,9 @@ def get_features(model: str) -> ModelFeatures:
118
127
  supports_responses_api=model_matches(model, RESPONSES_API_PATTERNS),
119
128
  force_string_serializer=model_matches(model, FORCE_STRING_SERIALIZER_PATTERNS),
120
129
  send_reasoning_content=model_matches(model, SEND_REASONING_CONTENT_PATTERNS),
130
+ supports_prompt_cache_retention=model_matches(
131
+ model, PROMPT_CACHE_RETENTION_PATTERNS
132
+ ),
121
133
  )
122
134
 
123
135
 
@@ -105,7 +105,14 @@ def setup_logging(
105
105
  keep = ENV_BACKUP_COUNT if backup_count is None else backup_count
106
106
 
107
107
  root = logging.getLogger()
108
+ old_level = root.level
108
109
  root.setLevel(lvl)
110
+
111
+ # Set the level for any existing logger with the same intial level
112
+ for logger in logging.root.manager.loggerDict.values():
113
+ if isinstance(logger, logging.Logger) and logger.level == old_level:
114
+ logger.setLevel(lvl)
115
+
109
116
  # Do NOT clear existing handlers; Uvicorn installs these before importing the app.
110
117
  # Only add ours if there isn't already a comparable stream handler.
111
118
  has_stream = any(isinstance(h, logging.StreamHandler) for h in root.handlers)
@@ -440,7 +440,24 @@ class ToolDefinition[ActionT, ObservationT](DiscriminatedUnionMixin, ABC):
440
440
  for subclass in get_known_concrete_subclasses(cls):
441
441
  if subclass.__name__ == kind:
442
442
  return subclass
443
- raise ValueError(f"Unknown kind '{kind}' for {cls}")
443
+
444
+ # Get all possible kinds for the error message
445
+ possible_kinds = [
446
+ subclass.__name__ for subclass in get_known_concrete_subclasses(cls)
447
+ ]
448
+ possible_kinds_str = (
449
+ ", ".join(sorted(possible_kinds)) if possible_kinds else "none"
450
+ )
451
+
452
+ error_msg = (
453
+ f"Unexpected kind '{kind}' for {cls.__name__}. "
454
+ f"Expected one of: {possible_kinds_str}. "
455
+ f"If you receive this error when trying to wrap a DiscriminatedUnion "
456
+ f"instance inside another pydantic model, you may need to use "
457
+ f"OpenHandsModel instead of BaseModel to make sure that an invalid "
458
+ f"schema has not been cached."
459
+ )
460
+ raise ValueError(error_msg)
444
461
 
445
462
 
446
463
  def _create_action_type_with_risk(action_type: type[Schema]) -> type[Schema]:
@@ -3,7 +3,7 @@ import json
3
3
  import logging
4
4
  import os
5
5
  from abc import ABC
6
- from typing import Annotated, Any, ClassVar, Literal, Self, Union
6
+ from typing import Annotated, Any, ClassVar, Literal, NoReturn, Self, Union
7
7
 
8
8
  from pydantic import (
9
9
  BaseModel,
@@ -11,7 +11,9 @@ from pydantic import (
11
11
  Field,
12
12
  Tag,
13
13
  TypeAdapter,
14
+ ValidationError,
14
15
  )
16
+ from pydantic_core import ErrorDetails
15
17
 
16
18
 
17
19
  logger = logging.getLogger(__name__)
@@ -56,6 +58,48 @@ def kind_of(obj) -> str:
56
58
  return obj.__name__
57
59
 
58
60
 
61
+ def _create_enhanced_discriminated_union_error_message(
62
+ invalid_kind: str, cls_name: str, valid_kinds: list[str]
63
+ ) -> str:
64
+ """Create an enhanced error message for discriminated union validation failures."""
65
+ possible_kinds_str = ", ".join(sorted(valid_kinds)) if valid_kinds else "none"
66
+ return (
67
+ f"Unexpected kind '{invalid_kind}' for {cls_name}. "
68
+ f"Expected one of: {possible_kinds_str}. "
69
+ f"If you receive this error when trying to wrap a "
70
+ f"DiscriminatedUnion instance inside another pydantic model, "
71
+ f"you may need to use OpenHandsModel instead of BaseModel "
72
+ f"to make sure that an invalid schema has not been cached."
73
+ )
74
+
75
+
76
+ def _extract_invalid_kind_from_validation_error(error: ErrorDetails) -> str:
77
+ """Extract the invalid kind from a Pydantic validation error."""
78
+ input_value = error.get("input")
79
+ if input_value is not None and hasattr(input_value, "kind"):
80
+ return input_value.kind
81
+ elif isinstance(input_value, dict) and "kind" in input_value:
82
+ return input_value["kind"]
83
+ else:
84
+ return kind_of(input_value)
85
+
86
+
87
+ def _handle_discriminated_union_validation_error(
88
+ validation_error: ValidationError, cls_name: str, valid_kinds: list[str]
89
+ ) -> NoReturn:
90
+ """Handle discriminated union validation errors with enhanced messages."""
91
+ for error in validation_error.errors():
92
+ if error.get("type") == "union_tag_invalid":
93
+ invalid_kind = _extract_invalid_kind_from_validation_error(error)
94
+ error_msg = _create_enhanced_discriminated_union_error_message(
95
+ invalid_kind, cls_name, valid_kinds
96
+ )
97
+ raise ValueError(error_msg) from validation_error
98
+
99
+ # If it's not a discriminated union error, re-raise the original error
100
+ raise validation_error
101
+
102
+
59
103
  def get_known_concrete_subclasses(cls) -> list[type]:
60
104
  """Recursively returns all concrete subclasses in a stable order,
61
105
  without deduping classes that share the same (module, name)."""
@@ -137,7 +181,15 @@ class DiscriminatedUnionMixin(OpenHandsModel, ABC):
137
181
  for subclass in get_known_concrete_subclasses(cls):
138
182
  if subclass.__name__ == kind:
139
183
  return subclass
140
- raise ValueError(f"Unknown kind '{kind}' for {cls}")
184
+
185
+ # Generate enhanced error message for unknown kind
186
+ valid_kinds = [
187
+ subclass.__name__ for subclass in get_known_concrete_subclasses(cls)
188
+ ]
189
+ error_msg = _create_enhanced_discriminated_union_error_message(
190
+ kind, cls.__name__, valid_kinds
191
+ )
192
+ raise ValueError(error_msg)
141
193
 
142
194
  @classmethod
143
195
  def __get_pydantic_core_schema__(cls, source_type, handler):
@@ -150,7 +202,30 @@ class DiscriminatedUnionMixin(OpenHandsModel, ABC):
150
202
  serializable_type = source_type.get_serializable_type()
151
203
  # If there are subclasses, generate schema for the discriminated union
152
204
  if serializable_type is not source_type:
153
- return handler.generate_schema(serializable_type)
205
+ from pydantic_core import core_schema
206
+
207
+ # Generate the base schema
208
+ base_schema = handler.generate_schema(serializable_type)
209
+
210
+ # Wrap it with a custom validation function that provides
211
+ # enhanced error messages
212
+ def validate_with_enhanced_error(value, handler_func, info): # noqa: ARG001
213
+ try:
214
+ return handler_func(value)
215
+ except ValidationError as e:
216
+ valid_kinds = [
217
+ subclass.__name__
218
+ for subclass in get_known_concrete_subclasses(source_type)
219
+ ]
220
+ _handle_discriminated_union_validation_error(
221
+ e, source_type.__name__, valid_kinds
222
+ )
223
+
224
+ # Create a with_info_wrap_validator_function schema
225
+ return core_schema.with_info_wrap_validator_function(
226
+ validate_with_enhanced_error,
227
+ base_schema,
228
+ )
154
229
 
155
230
  return handler(source_type)
156
231
 
@@ -241,12 +316,18 @@ class DiscriminatedUnionMixin(OpenHandsModel, ABC):
241
316
 
242
317
  @classmethod
243
318
  def model_validate(cls, obj: Any, **kwargs) -> Self:
244
- if _is_abstract(cls):
245
- resolved = cls.resolve_kind(kind_of(obj))
246
- else:
247
- resolved = super()
248
- result = resolved.model_validate(obj, **kwargs)
249
- return result # type: ignore
319
+ try:
320
+ if _is_abstract(cls):
321
+ resolved = cls.resolve_kind(kind_of(obj))
322
+ else:
323
+ resolved = super()
324
+ result = resolved.model_validate(obj, **kwargs)
325
+ return result # type: ignore
326
+ except ValidationError as e:
327
+ valid_kinds = [
328
+ subclass.__name__ for subclass in get_known_concrete_subclasses(cls)
329
+ ]
330
+ _handle_discriminated_union_validation_error(e, cls.__name__, valid_kinds)
250
331
 
251
332
  @classmethod
252
333
  def model_validate_json(
@@ -1,5 +1,13 @@
1
1
  """Utility functions for truncating text content."""
2
2
 
3
+ import hashlib
4
+ from pathlib import Path
5
+
6
+ from openhands.sdk.logger import get_logger
7
+
8
+
9
+ logger = get_logger(__name__)
10
+
3
11
  # Default truncation limits
4
12
  DEFAULT_TEXT_CONTENT_LIMIT = 50_000
5
13
 
@@ -7,38 +15,103 @@ DEFAULT_TEXT_CONTENT_LIMIT = 50_000
7
15
  DEFAULT_TRUNCATE_NOTICE = (
8
16
  "<response clipped><NOTE>Due to the max output limit, only part of the full "
9
17
  "response has been shown to you.</NOTE>"
18
+ ) # 113 chars
19
+
20
+ DEFAULT_TRUNCATE_NOTICE_WITH_PERSIST = (
21
+ "<response clipped><NOTE>Due to the max output limit, only part of the full "
22
+ "response has been shown to you. The complete output has been saved to "
23
+ "{file_path} - you can use other tools to view the full content (truncated "
24
+ "part starts around line {line_num}).</NOTE>"
10
25
  )
11
26
 
12
27
 
28
+ def _save_full_content(content: str, save_dir: str, tool_prefix: str) -> str | None:
29
+ """Save full content to the specified directory and return the file path."""
30
+
31
+ save_dir_path = Path(save_dir)
32
+ save_dir_path.mkdir(exist_ok=True)
33
+
34
+ # Generate hash-based filename for deduplication
35
+ content_hash = hashlib.sha256(content.encode("utf-8")).hexdigest()[:8]
36
+ filename = f"{tool_prefix}_output_{content_hash}.txt"
37
+ file_path = save_dir_path / filename
38
+
39
+ # Only write if file doesn't exist (deduplication)
40
+ if not file_path.exists():
41
+ try:
42
+ file_path.write_text(content, encoding="utf-8")
43
+ except Exception as e:
44
+ logger.debug(f"Failed to save full content to {file_path}: {e}")
45
+ return None
46
+
47
+ return str(file_path)
48
+
49
+
13
50
  def maybe_truncate(
14
51
  content: str,
15
52
  truncate_after: int | None = None,
16
53
  truncate_notice: str = DEFAULT_TRUNCATE_NOTICE,
54
+ save_dir: str | None = None,
55
+ tool_prefix: str = "output",
17
56
  ) -> str:
18
57
  """
19
58
  Truncate the middle of content if it exceeds the specified length.
20
59
 
21
60
  Keeps the head and tail of the content to preserve context at both ends.
61
+ Optionally saves the full content to a file for later investigation.
22
62
 
23
63
  Args:
24
64
  content: The text content to potentially truncate
25
65
  truncate_after: Maximum length before truncation. If None, no truncation occurs
26
66
  truncate_notice: Notice to insert in the middle when content is truncated
67
+ save_dir: Working directory to save full content file in
68
+ tool_prefix: Prefix for the saved file (e.g., "bash", "browser", "editor")
27
69
 
28
70
  Returns:
29
71
  Original content if under limit, or truncated content with head and tail
30
- preserved
72
+ preserved and reference to saved file if applicable
31
73
  """
74
+ # 1) Early exits: no truncation requested, or content already within limit
32
75
  if not truncate_after or len(content) <= truncate_after or truncate_after < 0:
33
76
  return content
34
77
 
35
- # Calculate how much space we have for actual content
78
+ # 2) If even the base notice doesn't fit, return a slice of it
79
+ if len(truncate_notice) >= truncate_after:
80
+ return truncate_notice[:truncate_after]
81
+
82
+ # 3) Calculate proposed head size based on base notice
83
+ # (for consistent line number calc)
36
84
  available_chars = truncate_after - len(truncate_notice)
37
- half = available_chars // 2
85
+ # Prefer giving the "extra" char to head (ceil split)
86
+ proposed_head = available_chars // 2 + (available_chars % 2)
87
+
88
+ # 4) Optionally save full content, then construct the final notice
89
+ final_notice = truncate_notice
90
+ if save_dir:
91
+ saved_file_path = _save_full_content(content, save_dir, tool_prefix)
92
+ if saved_file_path:
93
+ # Calculate line number where truncation happens (using head_chars)
94
+ head_content_lines = len(content[:proposed_head].splitlines())
95
+
96
+ final_notice = DEFAULT_TRUNCATE_NOTICE_WITH_PERSIST.format(
97
+ file_path=saved_file_path,
98
+ line_num=head_content_lines + 1, # +1 to indicate next line
99
+ )
100
+
101
+ # 5) If the final notice (with persist info) alone fills the
102
+ # budget, return a slice of it
103
+ if len(final_notice) >= truncate_after:
104
+ return final_notice[:truncate_after]
38
105
 
39
- # Give extra character to head if odd number
40
- head_chars = half + (available_chars % 2)
41
- tail_chars = half
106
+ # 6) Allocate remaining budget to head/tail
107
+ remaining = truncate_after - len(final_notice)
108
+ head_chars = min(
109
+ proposed_head, remaining
110
+ ) # Ensure head_chars doesn't exceed remaining
111
+ tail_chars = remaining - head_chars # non-negative due to previous checks
42
112
 
43
- # Keep head and tail, insert notice in the middle
44
- return content[:head_chars] + truncate_notice + content[-tail_chars:]
113
+ return (
114
+ content[:head_chars]
115
+ + final_notice
116
+ + (content[-tail_chars:] if tail_chars > 0 else "")
117
+ )
@@ -1,6 +1,6 @@
1
1
  from .base import BaseWorkspace
2
2
  from .local import LocalWorkspace
3
- from .models import CommandResult, FileOperationResult
3
+ from .models import CommandResult, FileOperationResult, PlatformType, TargetType
4
4
  from .remote import RemoteWorkspace
5
5
  from .workspace import Workspace
6
6
 
@@ -10,6 +10,8 @@ __all__ = [
10
10
  "CommandResult",
11
11
  "FileOperationResult",
12
12
  "LocalWorkspace",
13
+ "PlatformType",
13
14
  "RemoteWorkspace",
15
+ "TargetType",
14
16
  "Workspace",
15
17
  ]
@@ -1,8 +1,14 @@
1
- """Pydantic models for workspace operation results."""
1
+ """Pydantic models for workspace operation results and build types."""
2
+
3
+ from typing import Literal
2
4
 
3
5
  from pydantic import BaseModel, Field
4
6
 
5
7
 
8
+ TargetType = Literal["binary", "binary-minimal", "source", "source-minimal"]
9
+ PlatformType = Literal["linux/amd64", "linux/arm64"]
10
+
11
+
6
12
  class CommandResult(BaseModel):
7
13
  """Result of executing a command in the workspace."""
8
14
 
@@ -15,11 +15,32 @@ class AsyncRemoteWorkspace(RemoteWorkspaceMixin):
15
15
 
16
16
  _client: httpx.AsyncClient | None = PrivateAttr(default=None)
17
17
 
18
+ async def reset_client(self) -> None:
19
+ """Reset the HTTP client to force re-initialization.
20
+
21
+ This is useful when connection parameters (host, api_key) have changed
22
+ and the client needs to be recreated with new values.
23
+ """
24
+ if self._client is not None:
25
+ try:
26
+ await self._client.aclose()
27
+ except Exception:
28
+ pass
29
+ self._client = None
30
+
18
31
  @property
19
32
  def client(self) -> httpx.AsyncClient:
20
33
  client = self._client
21
34
  if client is None:
22
- client = httpx.AsyncClient(base_url=self.host)
35
+ # Configure reasonable timeouts for HTTP requests
36
+ # - connect: 10 seconds to establish connection
37
+ # - read: 60 seconds to read response (for LLM operations)
38
+ # - write: 10 seconds to send request
39
+ # - pool: 10 seconds to get connection from pool
40
+ timeout = httpx.Timeout(connect=10.0, read=60.0, write=10.0, pool=10.0)
41
+ client = httpx.AsyncClient(
42
+ base_url=self.host, timeout=timeout, headers=self._headers
43
+ )
23
44
  self._client = client
24
45
  return client
25
46
 
@@ -30,6 +30,19 @@ class RemoteWorkspace(RemoteWorkspaceMixin, BaseWorkspace):
30
30
 
31
31
  _client: httpx.Client | None = PrivateAttr(default=None)
32
32
 
33
+ def reset_client(self) -> None:
34
+ """Reset the HTTP client to force re-initialization.
35
+
36
+ This is useful when connection parameters (host, api_key) have changed
37
+ and the client needs to be recreated with new values.
38
+ """
39
+ if self._client is not None:
40
+ try:
41
+ self._client.close()
42
+ except Exception:
43
+ pass
44
+ self._client = None
45
+
33
46
  @property
34
47
  def client(self) -> httpx.Client:
35
48
  client = self._client
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openhands-sdk
3
- Version: 1.3.0
3
+ Version: 1.4.1
4
4
  Summary: OpenHands SDK - Core functionality for building AI agents
5
5
  Requires-Python: >=3.12
6
6
  Requires-Dist: deprecation>=2.1.0
7
7
  Requires-Dist: fastmcp>=2.11.3
8
8
  Requires-Dist: httpx>=0.27.0
9
- Requires-Dist: litellm>=1.77.7.dev9
9
+ Requires-Dist: litellm>=1.80.7
10
10
  Requires-Dist: pydantic>=2.11.7
11
11
  Requires-Dist: python-frontmatter>=1.1.0
12
12
  Requires-Dist: python-json-logger>=3.3.0
@@ -1,20 +1,20 @@
1
- openhands/sdk/__init__.py,sha256=fs0eN6b9WXrjyP0g1N4Ms_ThlYnYrS4i861lZhmTvU8,2284
1
+ openhands/sdk/__init__.py,sha256=SsB5acHhWvF6e3FlbR72PzZHH9ByJiXc7vnwxBPqmhw,2374
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=UX1FK5DhL0vwPYKqWKAN4Noz59cX_MTC3EtFpNbJTkU,18217
5
- openhands/sdk/agent/base.py,sha256=dG0dzZKioQIVVw0g6ARwZzP76eI4MW0tgP4bHw_-bPI,15517
6
- openhands/sdk/agent/utils.py,sha256=U3C1GnX3xEuCT3hp1zbm25vFas3QhQ6zhb1HbPjEIS8,7158
4
+ openhands/sdk/agent/agent.py,sha256=y8jfExyzgP898_Vr_Fz75Ovg2XTnkNQTgekdOi2S58I,19516
5
+ openhands/sdk/agent/base.py,sha256=HmZ_wNXdbsStqxjgkylGFm9B0pkp-0MCjmFTyh_XenM,15780
6
+ openhands/sdk/agent/utils.py,sha256=alYsAQ611XQ_94ogJacYD22BLbgSJjzd3Xex_b9KuC4,7418
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
9
9
  openhands/sdk/agent/prompts/security_policy.j2,sha256=K56d2aaZ88DI-y2DsMSDaiZRTTnkkzuBLjbzXfKHGA8,993
10
10
  openhands/sdk/agent/prompts/security_risk_assessment.j2,sha256=7o1tk6MIQpVD7sAES-sBhw4ckYLGQydzYnjjNitP5iY,1196
11
- openhands/sdk/agent/prompts/system_prompt.j2,sha256=JTFd07yBCZa0dIE3iQ5ZIgWNNySBoiTaNqk5fukHivo,8546
11
+ openhands/sdk/agent/prompts/system_prompt.j2,sha256=Cdi9eDy5el1mp5IXlJqr3aqsgRYt3p6yC0Wgw1E8NFQ,7979
12
12
  openhands/sdk/agent/prompts/system_prompt_interactive.j2,sha256=AW3rGuqu82BqbS1XMXVO4Fp-Apa8DPYZV3_nQYkzVtM,1388
13
13
  openhands/sdk/agent/prompts/system_prompt_long_horizon.j2,sha256=_oOHRIer_FSuRrBOSOPpe5Ueo9KgSTba5SPoHHpghCI,2995
14
14
  openhands/sdk/agent/prompts/system_prompt_planning.j2,sha256=wh01KX7yzeUSn5PtG3Ij0keRM6vmQuZZM6I0Fod4DK4,2934
15
15
  openhands/sdk/agent/prompts/system_prompt_tech_philosophy.j2,sha256=Yq9H7hHen2-tNsfBq9RlAWpyWsVRpjHhzmziZv8JHs8,5005
16
16
  openhands/sdk/context/__init__.py,sha256=dsOiCbO-eizN7HiGn_ZSSgH-EE841ygwalMzWz9GbVY,557
17
- openhands/sdk/context/agent_context.py,sha256=JSVrWCX7ETugHgj8W77Qkw7df94TvCGMyzl6WstpulY,7112
17
+ openhands/sdk/context/agent_context.py,sha256=lEJySpZz2aq9RonsCwsf2eF4bivrCikOqpmnogsiNhk,8370
18
18
  openhands/sdk/context/view.py,sha256=j8R4r1PSKFUJR_G4qDyI8ToqyHbWtU0YRKDBm8LJm4I,9260
19
19
  openhands/sdk/context/condenser/__init__.py,sha256=o3O5wEOUak7mzJ3EzY4s6pjUwA_Q5EzLvgNSFM46d50,490
20
20
  openhands/sdk/context/condenser/base.py,sha256=fp1WxZGslE1luwmzFmablZ9Q3h_F7ErBJfCAh6HKpRI,3659
@@ -27,30 +27,30 @@ openhands/sdk/context/prompts/prompt.py,sha256=a2eCR4QSYzgdDxcGkGVktKerAOk-SCb8o
27
27
  openhands/sdk/context/prompts/templates/ask_agent_template.j2,sha256=VRKWdF2VTJ_Tyway_Wexp8_KlNgAkME94eZelbbsEZI,212
28
28
  openhands/sdk/context/prompts/templates/skill_knowledge_info.j2,sha256=3yNxEEbScAU1w60ix7Ph91wr6p8mjwT-3EGkkZkWT9Q,261
29
29
  openhands/sdk/context/prompts/templates/system_message_suffix.j2,sha256=JTNvdtH_qbFBTzZVV8NzHtn48BJXNY2dLFgMWj_iwM8,401
30
- openhands/sdk/context/skills/__init__.py,sha256=cQuHXin2tGg1qH9Y_nV6Oud-cWztrLaUyPCTYRpCWM4,543
30
+ openhands/sdk/context/skills/__init__.py,sha256=5YmyufIWoLvoUYtcZE_xI4sOtcCs70-CHrblnzavWWY,593
31
31
  openhands/sdk/context/skills/exceptions.py,sha256=tVBSbXTXG32nb1TebVAuzbNNHvn8GScpSM1bHCnQzvY,305
32
- openhands/sdk/context/skills/skill.py,sha256=Qz5bazmLaR2cvRLvJpJUUoGy6sl_XACT1eJ-WhlReFs,13237
32
+ openhands/sdk/context/skills/skill.py,sha256=iJpJWiXLHhxd7tmdPo1HcIDqa4emAYkQHgN7KvkQcl0,20407
33
33
  openhands/sdk/context/skills/trigger.py,sha256=ZGaDmMpJghnAEuTTYX6UepsA5nX1CSz83zK1Ox46vMk,756
34
34
  openhands/sdk/context/skills/types.py,sha256=qdakWgfs_88I_cnmVhO5bl3C3s11GvCydq7Q-V53kCI,1662
35
- openhands/sdk/conversation/__init__.py,sha256=_8jMjId0iyn-TLH59EjV3JiWOPUVbCU9b4ayXsuf6c4,1321
36
- openhands/sdk/conversation/base.py,sha256=SVlFQPwJt8hOgWAovt5ZHaMxtCmwoo3KTv4j2FqtRW8,8151
37
- openhands/sdk/conversation/conversation.py,sha256=5H-hV7sgXQRwncd9-lyf6TytmUyiEzJOZLGHd4cKj-Q,4905
38
- openhands/sdk/conversation/conversation_stats.py,sha256=_-iKBOnT5PYhbDkm7kB3Jsw1RrOhViA0HdnfmCDwRn0,1643
35
+ openhands/sdk/conversation/__init__.py,sha256=1-xh49S2KJhtAjkHDaDHU28UWhfQLl3CeFlo179gr00,1402
36
+ openhands/sdk/conversation/base.py,sha256=3qUc4_PjrT1IztBX7EJKuypvRVWRIbMmIQ3Fc2lvspI,8308
37
+ openhands/sdk/conversation/conversation.py,sha256=GsPK9MWTAWG47J6jQ1vcSD9ER31wbFkRF9zdFmCqz9I,5275
38
+ openhands/sdk/conversation/conversation_stats.py,sha256=ZlQ99kgG5YVCrZ4rqJlq63JaiInxX8jqv-q5lS7RN68,3038
39
39
  openhands/sdk/conversation/event_store.py,sha256=he-bwP823s5zAIdua_0ZgkkHQCJoAqbtV2SN0hibX30,5207
40
40
  openhands/sdk/conversation/events_list_base.py,sha256=n_YvgbhBPOPDbw4Kp68J0EKFM39vg95ng09GMfTz29s,505
41
41
  openhands/sdk/conversation/exceptions.py,sha256=C3pN3MJJIYdhcMHMqtOmVkR1BhVe3pfxBhxzQ6FVBjc,804
42
- openhands/sdk/conversation/fifo_lock.py,sha256=HG10uaTI9NMJ9P4njf6ne_EC6tMpBxYwfexafpt6kt8,4012
42
+ openhands/sdk/conversation/fifo_lock.py,sha256=nY5RsobNvVXBbAzwjqIxyQwPUh0AzffbTZw4PewhTTI,4240
43
43
  openhands/sdk/conversation/persistence_const.py,sha256=om3pOQa5sGK8t_NUYb3Tz-7sKeu531gaS1e7iCaqWmo,218
44
44
  openhands/sdk/conversation/response_utils.py,sha256=rPlC3cDSmoQte6NZ0kK6h6-9ho5cbF8jEw-DiyEhgIM,1548
45
45
  openhands/sdk/conversation/secret_registry.py,sha256=37kk-aaeQ_XKZDXHpNJ2oTOWsElL4Q9jI_6qd3A774I,4500
46
- openhands/sdk/conversation/secret_source.py,sha256=aKs3hFhbdwZVBEYHZf76jPfqWaNd1XPJBfY2Gg94364,2554
46
+ openhands/sdk/conversation/secret_source.py,sha256=1itHqTgwu1yZKGZ46HR0X08MLU2Y_rWa2ahzbqxH4k0,2568
47
47
  openhands/sdk/conversation/serialization_diff.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
48
- openhands/sdk/conversation/state.py,sha256=RzCS-uMkklaTPBRkLxlrutc1qY24pmkwTya_Dx4EP9Y,12746
48
+ openhands/sdk/conversation/state.py,sha256=EpP_0MAWv14gEIBvIlpdIxtxLCzN1n2mgzSzQNRpCY8,13046
49
49
  openhands/sdk/conversation/stuck_detector.py,sha256=PZF0HWC6G0SUud_U3hiv5r4AqfJJMw5-ookEWVOY5sY,10866
50
50
  openhands/sdk/conversation/title_utils.py,sha256=j40-dP-Oes-mhU2xUC7fCC8cB0wkMdbbDJU7WLHiVIo,7063
51
- openhands/sdk/conversation/types.py,sha256=O1_7_BrH-abvvTx6C1cXaPNHC3k2A1ZfsN6Q5w0YEi4,213
51
+ openhands/sdk/conversation/types.py,sha256=CMCCJz6fSfWgaAgWXeorEDC8VSXyyplyiMlpcueszT8,423
52
52
  openhands/sdk/conversation/impl/__init__.py,sha256=DmDFyNR4RU8eiMocKf2j9eBQomipP-rrJgU1LoVWTDA,220
53
- openhands/sdk/conversation/impl/local_conversation.py,sha256=lTLXSJGGeAMBQluVe-fKOCXw9Vz1AAZgqnpB6n3jUQg,22094
53
+ openhands/sdk/conversation/impl/local_conversation.py,sha256=R0iTc1oCaOBiBUH3Ei7RNO1JMS_7nEuBlCr3ILUosU0,22594
54
54
  openhands/sdk/conversation/impl/remote_conversation.py,sha256=9_yCDHO5KKODYHVzUt0mJnsSVcDE6Tv3TJvGULilcyE,28536
55
55
  openhands/sdk/conversation/visualizer/__init__.py,sha256=0LXpKlt2eJcrqP1z6jQP_nLx23V8ErnQkKYSxvUp0_A,275
56
56
  openhands/sdk/conversation/visualizer/base.py,sha256=77DdRdHAPSESxRCYyRRSOK7ROBlljscxogkFFr4YgM0,2323
@@ -65,7 +65,7 @@ openhands/sdk/event/__init__.py,sha256=ir-jRVA0QjEbFuDzJOYRq2kXTgUHs8eJ7_skoCRNz
65
65
  openhands/sdk/event/base.py,sha256=QpQtYfYiZqaKWA-2xa4GEmk2qkUHKD6x-OIAk7SMnDs,5568
66
66
  openhands/sdk/event/condenser.py,sha256=sne9CxhhNq9UvJMKuKcw8tXuMgutuGh85TbbZHwYhPQ,2481
67
67
  openhands/sdk/event/conversation_error.py,sha256=gZMyliJx1xbyoJFYH0-AEHqznyiA8LZ7olPSidaNfuc,957
68
- openhands/sdk/event/conversation_state.py,sha256=nARmfgjj8PJ6luBjKlBjNIgdf1U0YxWrkIVAHCvbguY,3284
68
+ openhands/sdk/event/conversation_state.py,sha256=V-ti5SLL5SL330sEOQgpy-U8tErVwZYG0iC3AqjTUwo,3650
69
69
  openhands/sdk/event/llm_completion_log.py,sha256=VCxJiZBsn1F6TRV6fwvsPs6W9DpjghfIFmJJlGKztXg,1232
70
70
  openhands/sdk/event/token.py,sha256=QlEbBrfZaHe9tp8-Ot4vTd0T-_Vj7lpUqKfVJcVHOeI,497
71
71
  openhands/sdk/event/types.py,sha256=3hyFDBNtYTGEwLV6muodJO7iDMoZTC4D0Dd4vYeF2dE,271
@@ -84,11 +84,12 @@ openhands/sdk/io/__init__.py,sha256=6pXTWP03Wn5S7b6fOT0g3PYn-qSoEGGdrrwBqALYGA4,
84
84
  openhands/sdk/io/base.py,sha256=kAcX0chfCswakiieJlKiHWoJgL3zOtaQauRqMPNYfW8,1355
85
85
  openhands/sdk/io/local.py,sha256=H1wjnBS0EkBJxEatWqNpteL1bPBHoetcnhrIeou4uEY,2991
86
86
  openhands/sdk/io/memory.py,sha256=XIsdXsSyF-PzoYVmvJuO7Vtz-k3D5jMOFoZ5gHw8tbA,1712
87
- openhands/sdk/llm/__init__.py,sha256=o-wWK3HPXUZIc-rXxHxRV8FdkGP8zy6iVXXt2LaQvnU,1085
88
- openhands/sdk/llm/llm.py,sha256=ZNkXDkDCcFsrxbrffYnHbkw0PwNkprR9Yhbm9TLyU0g,41476
89
- openhands/sdk/llm/llm_registry.py,sha256=LJMK7yyoFbHyCmlui5zmwwqpTO4zWtXsvXYjqjoiBQw,3554
87
+ openhands/sdk/llm/__init__.py,sha256=k8UneyfoDUMe0lSP4GSlYzrL2Fe3MkDUKpSg2OIDi_I,1206
88
+ openhands/sdk/llm/llm.py,sha256=7cpdaXvsgafim2ZXI5umjN3P5ND9xk1GjktKRbrWGqo,44308
89
+ openhands/sdk/llm/llm_registry.py,sha256=DL9yqSbAM7OBkzdIChLuxG2qk_oElW2tC2xem6mq0F8,3530
90
90
  openhands/sdk/llm/llm_response.py,sha256=DaBVBkij4Sz-RsYhRb3UUcvJCTzCBcOYQ9IhFwN4ukI,1988
91
91
  openhands/sdk/llm/message.py,sha256=zVcmHL4z99Bm0_qHz0hhtDh8a9r8Y1GtcfCgL5yBsME,25429
92
+ openhands/sdk/llm/streaming.py,sha256=tFJ7B0AjJ-e8Xv13DTtc2FdrsLRUCG8wxQex8fDlOp4,214
92
93
  openhands/sdk/llm/exceptions/__init__.py,sha256=6iMJah2nS6BboU06HqgAM2JT6aykCWY8muoUwaaJpR8,1144
93
94
  openhands/sdk/llm/exceptions/classifier.py,sha256=pu5fVNubUrB3eXV1i5W7m4-D4Ik2Z-fGe1ba2t0SSc4,1456
94
95
  openhands/sdk/llm/exceptions/mapping.py,sha256=D68duh85HPONw5P0Mv4e8Ji-ubi6qWEb2d9FkOKf8cA,1656
@@ -96,22 +97,22 @@ openhands/sdk/llm/exceptions/types.py,sha256=IfyRtfH5eJrsLOlp6Fz76CiCKjx1ukmXciI
96
97
  openhands/sdk/llm/mixins/fn_call_converter.py,sha256=G5u0cNGLBscog7Vt0c7Xjo5uLsvyleBlk12Mq0wTQTM,46310
97
98
  openhands/sdk/llm/mixins/non_native_fc.py,sha256=ymPELReJtFsBJCweEpfx2njyd6NCG9mj9AoDPPLFAqc,3250
98
99
  openhands/sdk/llm/options/__init__.py,sha256=EntvOWC5kwDoTMXXMkYuoWMQ13hD8YtC9CEMCtnKj7o,54
99
- openhands/sdk/llm/options/chat_options.py,sha256=x21uNuZcInnpDGajgTsJl4FSBu-2Uf-w9Pr9mHPQPTw,4126
100
+ openhands/sdk/llm/options/chat_options.py,sha256=YrQrMKUj9Sb0ZdzJMoB11PwD7hzdLBDGrOMWfgZQ10s,3452
100
101
  openhands/sdk/llm/options/common.py,sha256=qFcPuZF_c4rmH1bgGG8Qp6TJ4YWpv9IFzfLZRRiik9M,580
101
- openhands/sdk/llm/options/responses_options.py,sha256=vXLOSMebL8nK2ZHQ7_GUOf2dMZgXqgu3DdRbDCbmXXM,1876
102
+ openhands/sdk/llm/options/responses_options.py,sha256=xrBEc62PFjLeM9bGyXtXrQ6GgYDzhJfGha8Nhi5Oci8,2166
102
103
  openhands/sdk/llm/router/__init__.py,sha256=N8qldpGdLLCWZzn5Rz2y8AheSoCTQLGkLOBDCFNMJRA,261
103
- openhands/sdk/llm/router/base.py,sha256=5znrRHgDK6OHRY8UuXDOb_sVf3YfqKvopGD768FkAG8,3999
104
+ openhands/sdk/llm/router/base.py,sha256=9mUUpv-zgT6HVKmJ87Q5e5VncoKi8hHd9aP8j9cJI_g,4139
104
105
  openhands/sdk/llm/router/impl/multimodal.py,sha256=uKFVm7b3Jr0xCC1lvP-cVn-fIkTv24-pK3xKlOJahz4,3034
105
106
  openhands/sdk/llm/router/impl/random.py,sha256=oBHoFTBMa9OeDyg-rV4siLCkN6rKYL0uDlZMEseB3ro,656
106
107
  openhands/sdk/llm/utils/metrics.py,sha256=4zD0Hkc9Oc4qcDcVZUX13RyggyObshUbz4Ik9W1uIw4,11592
107
- openhands/sdk/llm/utils/model_features.py,sha256=JY5xqwkkKa7uF77Gp6jkBcfJVIeHBrpQnSDyBdqOeLo,4258
108
+ openhands/sdk/llm/utils/model_features.py,sha256=JMGShnYBplGL6O6f19YC2EqI3dIviNN_58TFbflBnlA,4607
108
109
  openhands/sdk/llm/utils/model_info.py,sha256=1mFYA7OcEyUB6k1doao8_w1XT7UMM_DAm57HcTpKkLw,2628
109
110
  openhands/sdk/llm/utils/retry_mixin.py,sha256=yIkUxgtym4ouOci0o1u1BJnThUt05njblhC0b2Tvmb8,4559
110
111
  openhands/sdk/llm/utils/telemetry.py,sha256=BGzikbw1DAj96Bo60D7XIGh034zKIbO1zRx597_3TR4,13966
111
112
  openhands/sdk/llm/utils/unverified_models.py,sha256=SmYrX_WxXOJBanTviztqy1xPjOcLY4i3qvwNBEga_Dk,4797
112
113
  openhands/sdk/llm/utils/verified_models.py,sha256=add09fF6MHOCBS4Wp2BxLYYfijMtzkq1IFc3GNZgMWU,1357
113
114
  openhands/sdk/logger/__init__.py,sha256=vZvFDYfW01Y8Act3tveMs3XxTysJlt4HeT-n6X_ujYk,330
114
- openhands/sdk/logger/logger.py,sha256=Dqhl8EFVIprQmSQNKVnyIpybF6twZKzTJ21pHK7aW9k,6243
115
+ openhands/sdk/logger/logger.py,sha256=kSpeol92dKesm24ZxVyYbtAxeYccmuFZJw3qNO9X-t4,6513
115
116
  openhands/sdk/logger/rolling.py,sha256=E6oy0asgmOhZHoWlSCw0QK1PKnS6kvtxjoWLAsqlGvs,3440
116
117
  openhands/sdk/mcp/__init__.py,sha256=-wQbZ405PjVRCBtSfirp4jsiRohd7IJAyAdicZ-M8Ok,588
117
118
  openhands/sdk/mcp/client.py,sha256=CLkFImydorlT_RBTE5UxvRNGMlc-uO3wvupoDzB5E20,2420
@@ -131,7 +132,7 @@ openhands/sdk/tool/__init__.py,sha256=Fpcy_I5CYC8lDf2KNLj7LJg9YpoDObbacMFOvM9fI5
131
132
  openhands/sdk/tool/registry.py,sha256=x5j8DwOjH3YJijhXe_3nRPsSr1A2sDPpNhxCuvFrHK0,5456
132
133
  openhands/sdk/tool/schema.py,sha256=PdQbkzPER-Z9B-eKPMr16M8ap27gghszq1xSHH5DGZQ,9114
133
134
  openhands/sdk/tool/spec.py,sha256=EbtWasVhwjLKNJywHubbwfYqfgXnZbU4QE6XUOLhsdk,1221
134
- openhands/sdk/tool/tool.py,sha256=pJLeqjGQMANPKNBNk16vO8I7trRNZs_fvJWRLDF9bsA,17100
135
+ openhands/sdk/tool/tool.py,sha256=tcAno2EEXF-Ta8icgSbX_0mQDuuBDmQ5CugnEINHqwk,17798
135
136
  openhands/sdk/tool/builtins/__init__.py,sha256=30MfENomH9JBPwKs03Xwgny09dDUOt3FSIQ3egRJKhM,729
136
137
  openhands/sdk/tool/builtins/finish.py,sha256=pPv_bKDOQE4sUK9lNh1H1lDaxavHozjOwLCIuYrb424,3136
137
138
  openhands/sdk/tool/builtins/think.py,sha256=Jp8CBHJZwrtNuVCLrxKlVwb9HeQ1lZB56PCYMxW3wWk,4050
@@ -143,21 +144,21 @@ openhands/sdk/utils/command.py,sha256=iqTdFL9tPQ0gyq5w_HOMXSSHg1nowtrWwJBeDL87lZ
143
144
  openhands/sdk/utils/deprecation.py,sha256=7XwepMKTwBLaqS-4rdJupKxNZkepV0Mrru3uTQukS0k,5114
144
145
  openhands/sdk/utils/github.py,sha256=l-_LQKw820dA_U8NmDdmaReAPhgMAWqfH1oRMi1q5OA,1638
145
146
  openhands/sdk/utils/json.py,sha256=hHAA7i7NCJrQhb5WWSsoT0nvmJUi0koyBdbviFrfdcM,1384
146
- openhands/sdk/utils/models.py,sha256=viYKdoCXd-ln6mDY0mx2Oqh-HWHwbVM4127bp71cFJU,17170
147
+ openhands/sdk/utils/models.py,sha256=tOTGa4O1vHZgHvOp2mNv5V4hM3fBSM6Sswv-0oYVyJQ,20676
147
148
  openhands/sdk/utils/pydantic_diff.py,sha256=vOy4M1XKKDkCzp7RXBvnibvPYWL8fNEDtWNUn_-N1yg,2732
148
149
  openhands/sdk/utils/pydantic_secrets.py,sha256=B9njRdijnqO4g-GDCWRsWd-TZc5GTjMncDEPMFP_aUE,2186
149
- openhands/sdk/utils/truncate.py,sha256=Owl2fYsRAuQCIwJ6ma_xOwOYQ0b7EPF1ektzPHn8RQc,1466
150
+ openhands/sdk/utils/truncate.py,sha256=wrPdD1557teSR0A66z9roYAINp_cWYYwS6Ku29Z7b8M,4376
150
151
  openhands/sdk/utils/visualize.py,sha256=ZRZTA0epdiOA7vdk-EdGV6Yb5t8rz6ZzrargH7MUTEQ,845
151
- openhands/sdk/workspace/__init__.py,sha256=gUAmB0CuzwzYfDRO9e1zaX6SyoCvJ6zXOpmVXxkS3D0,337
152
+ openhands/sdk/workspace/__init__.py,sha256=09huA83IZJZU8XZSVzSgZwfUv-slzYwofnUjLgqt7B8,401
152
153
  openhands/sdk/workspace/base.py,sha256=4_oBXbjspa2Oh2wpgbcQMyrshi7prhmOL1LY97NowgQ,4590
153
154
  openhands/sdk/workspace/local.py,sha256=N_cW_E5BfctIpgwU-OcRQIqKLn4ibxNwjL4c8iYjU6E,6253
154
- openhands/sdk/workspace/models.py,sha256=s-2-AtOspEgZqpptGS7a34n8x8-JyoiZm5stPGPABr4,1128
155
+ openhands/sdk/workspace/models.py,sha256=ORFn70kXvD0urjeq59d_XqXBYSHbDYkBMg9KYQKRa5I,1304
155
156
  openhands/sdk/workspace/workspace.py,sha256=Of1r2z3W_pXSgV8u10sUQ87ChtaA5khD6HJ_PtJz6aY,1296
156
157
  openhands/sdk/workspace/remote/__init__.py,sha256=eKkj6NOESMUBGDVC6_L2Wfuc4K6G-mpnJDNHKBkSeUI,114
157
- openhands/sdk/workspace/remote/async_remote_workspace.py,sha256=SrenHPqt432aEhAVivnA7IYxbkH_yWGqoaNjpxifHww,4168
158
- openhands/sdk/workspace/remote/base.py,sha256=petJPmrloN9tHrORagDn6kRAfdtk16On0A8Ado5pWA0,5183
158
+ openhands/sdk/workspace/remote/async_remote_workspace.py,sha256=ftv1Vdx4mmM3AjygJpemMJGvhaQel7ORxdQVk12z4ZE,5061
159
+ openhands/sdk/workspace/remote/base.py,sha256=72C9MZV7ch5n6oHNvFMo6irW7b6Le8n4gk3yFuc0798,5605
159
160
  openhands/sdk/workspace/remote/remote_workspace_mixin.py,sha256=CzHfnLUIra5sgPkP9kcggb1vHGOPpYQzLsHvGO2rRt0,10963
160
- openhands_sdk-1.3.0.dist-info/METADATA,sha256=QF-QF_V8FlMlZRxvgJasvyj8xpAdyjL4Dg-GLBIertI,550
161
- openhands_sdk-1.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
162
- openhands_sdk-1.3.0.dist-info/top_level.txt,sha256=jHgVu9I0Blam8BXFgedoGKfglPF8XvW1TsJFIjcgP4E,10
163
- openhands_sdk-1.3.0.dist-info/RECORD,,
161
+ openhands_sdk-1.4.1.dist-info/METADATA,sha256=9K8ieOX7Xf0snpoeuCY9KmXkd5GTHkg_3EpgTT6vGLo,545
162
+ openhands_sdk-1.4.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
163
+ openhands_sdk-1.4.1.dist-info/top_level.txt,sha256=jHgVu9I0Blam8BXFgedoGKfglPF8XvW1TsJFIjcgP4E,10
164
+ openhands_sdk-1.4.1.dist-info/RECORD,,