openhands-tools 1.10.0__tar.gz → 1.11.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/PKG-INFO +1 -1
  2. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/planning_file_editor/definition.py +41 -3
  3. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/preset/planning.py +11 -2
  4. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/terminal/terminal/terminal_session.py +15 -0
  5. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/terminal/terminal/tmux_terminal.py +30 -7
  6. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/terminal/utils/command.py +1 -1
  7. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands_tools.egg-info/PKG-INFO +1 -1
  8. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/pyproject.toml +1 -1
  9. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/__init__.py +0 -0
  10. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/apply_patch/__init__.py +0 -0
  11. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/apply_patch/core.py +0 -0
  12. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/apply_patch/definition.py +0 -0
  13. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/browser_use/__init__.py +0 -0
  14. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/browser_use/definition.py +0 -0
  15. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/browser_use/impl.py +0 -0
  16. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/browser_use/impl_windows.py +0 -0
  17. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/browser_use/logging_fix.py +0 -0
  18. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/browser_use/server.py +0 -0
  19. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/delegate/__init__.py +0 -0
  20. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/delegate/definition.py +0 -0
  21. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/delegate/impl.py +0 -0
  22. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/delegate/registration.py +0 -0
  23. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/delegate/templates/delegate_tool_description.j2 +0 -0
  24. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/delegate/visualizer.py +0 -0
  25. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/file_editor/__init__.py +0 -0
  26. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/file_editor/definition.py +0 -0
  27. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/file_editor/editor.py +0 -0
  28. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/file_editor/exceptions.py +0 -0
  29. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/file_editor/impl.py +0 -0
  30. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/file_editor/utils/__init__.py +0 -0
  31. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/file_editor/utils/config.py +0 -0
  32. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/file_editor/utils/constants.py +0 -0
  33. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/file_editor/utils/diff.py +0 -0
  34. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/file_editor/utils/encoding.py +0 -0
  35. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/file_editor/utils/file_cache.py +0 -0
  36. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/file_editor/utils/history.py +0 -0
  37. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/file_editor/utils/shell.py +0 -0
  38. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/gemini/__init__.py +0 -0
  39. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/gemini/edit/__init__.py +0 -0
  40. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/gemini/edit/definition.py +0 -0
  41. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/gemini/edit/impl.py +0 -0
  42. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/gemini/list_directory/__init__.py +0 -0
  43. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/gemini/list_directory/definition.py +0 -0
  44. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/gemini/list_directory/impl.py +0 -0
  45. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/gemini/read_file/__init__.py +0 -0
  46. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/gemini/read_file/definition.py +0 -0
  47. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/gemini/read_file/impl.py +0 -0
  48. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/gemini/write_file/__init__.py +0 -0
  49. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/gemini/write_file/definition.py +0 -0
  50. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/gemini/write_file/impl.py +0 -0
  51. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/glob/__init__.py +0 -0
  52. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/glob/definition.py +0 -0
  53. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/glob/impl.py +0 -0
  54. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/grep/__init__.py +0 -0
  55. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/grep/definition.py +0 -0
  56. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/grep/impl.py +0 -0
  57. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/planning_file_editor/__init__.py +0 -0
  58. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/planning_file_editor/impl.py +0 -0
  59. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/preset/__init__.py +0 -0
  60. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/preset/default.py +0 -0
  61. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/preset/gemini.py +0 -0
  62. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/preset/gpt5.py +0 -0
  63. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/py.typed +0 -0
  64. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/task_tracker/__init__.py +0 -0
  65. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/task_tracker/definition.py +0 -0
  66. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/terminal/__init__.py +0 -0
  67. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/terminal/constants.py +0 -0
  68. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/terminal/definition.py +0 -0
  69. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/terminal/impl.py +0 -0
  70. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/terminal/metadata.py +0 -0
  71. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/terminal/terminal/__init__.py +0 -0
  72. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/terminal/terminal/factory.py +0 -0
  73. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/terminal/terminal/interface.py +0 -0
  74. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/terminal/terminal/subprocess_terminal.py +0 -0
  75. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/tom_consult/__init__.py +0 -0
  76. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/tom_consult/definition.py +0 -0
  77. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/tom_consult/executor.py +0 -0
  78. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/utils/__init__.py +0 -0
  79. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands/tools/utils/timeout.py +0 -0
  80. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands_tools.egg-info/SOURCES.txt +0 -0
  81. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands_tools.egg-info/dependency_links.txt +0 -0
  82. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands_tools.egg-info/requires.txt +0 -0
  83. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/openhands_tools.egg-info/top_level.txt +0 -0
  84. {openhands_tools-1.10.0 → openhands_tools-1.11.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openhands-tools
3
- Version: 1.10.0
3
+ Version: 1.11.0
4
4
  Summary: OpenHands Tools - Runtime tools for AI agents
5
5
  Project-URL: Source, https://github.com/OpenHands/software-agent-sdk
6
6
  Project-URL: Homepage, https://github.com/OpenHands/software-agent-sdk
@@ -8,6 +8,7 @@ from typing import TYPE_CHECKING
8
8
  if TYPE_CHECKING:
9
9
  from openhands.sdk.conversation.state import ConversationState
10
10
 
11
+ from openhands.sdk.logger import get_logger
11
12
  from openhands.sdk.tool import (
12
13
  ToolAnnotations,
13
14
  ToolDefinition,
@@ -20,7 +21,12 @@ from openhands.tools.file_editor.definition import (
20
21
  )
21
22
 
22
23
 
23
- # Hardcoded plan filename
24
+ logger = get_logger(__name__)
25
+
26
+ # Default config directory and plan filename
27
+ # PLAN.md is now stored in .agents_tmp/ to keep workspace root clean
28
+ # and separate agent temporary files from user content
29
+ DEFAULT_CONFIG_DIR = ".agents_tmp"
24
30
  PLAN_FILENAME = "PLAN.md"
25
31
 
26
32
 
@@ -62,11 +68,17 @@ class PlanningFileEditorTool(
62
68
  def create(
63
69
  cls,
64
70
  conv_state: "ConversationState",
71
+ plan_path: str | None = None,
65
72
  ) -> Sequence["PlanningFileEditorTool"]:
66
73
  """Initialize PlanningFileEditorTool.
67
74
 
68
75
  Args:
69
76
  conv_state: Conversation state to get working directory from.
77
+ plan_path: Optional absolute path to PLAN.md file. If not provided,
78
+ defaults to {working_dir}/.agents_tmp/PLAN.md.
79
+
80
+ Raises:
81
+ ValueError: If plan_path is provided but is not an absolute path.
70
82
  """
71
83
  # Import here to avoid circular imports
72
84
  from openhands.tools.planning_file_editor.impl import (
@@ -74,8 +86,31 @@ class PlanningFileEditorTool(
74
86
  )
75
87
 
76
88
  working_dir = conv_state.workspace.working_dir
77
- workspace_root = Path(working_dir).resolve()
78
- plan_path = str(workspace_root / PLAN_FILENAME)
89
+
90
+ # Validate plan_path is absolute if provided
91
+ if plan_path is not None and not Path(plan_path).is_absolute():
92
+ raise ValueError(f"plan_path must be an absolute path, got: {plan_path}")
93
+
94
+ # Use provided plan_path or fall back to .agents_tmp/PLAN.md at workspace root
95
+ if plan_path is None:
96
+ workspace_root = Path(working_dir).resolve()
97
+
98
+ # Check for legacy PLAN.md at workspace root
99
+ legacy_plan_path = workspace_root / PLAN_FILENAME
100
+ if legacy_plan_path.exists():
101
+ # Use legacy location for backward compatibility
102
+ new_recommended_path = (
103
+ workspace_root / DEFAULT_CONFIG_DIR / PLAN_FILENAME
104
+ )
105
+ logger.warning(
106
+ f"Found PLAN.md at legacy location {legacy_plan_path}. "
107
+ f"Consider moving it to {new_recommended_path} "
108
+ f"for consistency with OpenHands conventions."
109
+ )
110
+ plan_path = str(legacy_plan_path)
111
+ else:
112
+ # Use new default location
113
+ plan_path = str(workspace_root / DEFAULT_CONFIG_DIR / PLAN_FILENAME)
79
114
 
80
115
  # Initialize PLAN.md with headers if it doesn't exist
81
116
  plan_file = Path(plan_path)
@@ -83,7 +118,10 @@ class PlanningFileEditorTool(
83
118
  # Import here to avoid circular imports
84
119
  from openhands.tools.preset.planning import get_plan_headers
85
120
 
121
+ # Ensure parent directory exists
122
+ plan_file.parent.mkdir(parents=True, exist_ok=True)
86
123
  plan_file.write_text(get_plan_headers())
124
+ logger.info(f"Created new PLAN.md at {plan_path}")
87
125
 
88
126
  # Create executor with restricted edit access to PLAN.md only
89
127
  executor = PlanningFileEditorExecutor(
@@ -102,9 +102,13 @@ def register_planning_tools() -> None:
102
102
  logger.debug("Tool: PlanningFileEditorTool registered.")
103
103
 
104
104
 
105
- def get_planning_tools() -> list[Tool]:
105
+ def get_planning_tools(plan_path: str | None = None) -> list[Tool]:
106
106
  """Get the planning agent tool specifications.
107
107
 
108
+ Args:
109
+ plan_path: Optional absolute path to PLAN.md file. If provided, will be
110
+ passed to PlanningFileEditorTool via params.
111
+
108
112
  Returns:
109
113
  List of tools optimized for planning and analysis tasks, including
110
114
  file viewing and PLAN.md editing capabilities for advanced
@@ -117,10 +121,15 @@ def get_planning_tools() -> list[Tool]:
117
121
  from openhands.tools.grep import GrepTool
118
122
  from openhands.tools.planning_file_editor import PlanningFileEditorTool
119
123
 
124
+ # Build params for PlanningFileEditorTool if plan_path is provided
125
+ planning_tool_params = {}
126
+ if plan_path:
127
+ planning_tool_params["plan_path"] = plan_path
128
+
120
129
  return [
121
130
  Tool(name=GlobTool.name),
122
131
  Tool(name=GrepTool.name),
123
- Tool(name=PlanningFileEditorTool.name),
132
+ Tool(name=PlanningFileEditorTool.name, params=planning_tool_params),
124
133
  ]
125
134
 
126
135
 
@@ -5,8 +5,10 @@ import time
5
5
  from enum import Enum
6
6
 
7
7
  from openhands.sdk.logger import get_logger
8
+ from openhands.sdk.utils import maybe_truncate
8
9
  from openhands.tools.terminal.constants import (
9
10
  CMD_OUTPUT_PS1_END,
11
+ MAX_CMD_OUTPUT_SIZE,
10
12
  NO_CHANGE_TIMEOUT_SECONDS,
11
13
  POLL_INTERVAL,
12
14
  TIMEOUT_MESSAGE_TEMPLATE,
@@ -183,6 +185,10 @@ class TerminalSession(TerminalSessionBase):
183
185
  raw_command_output,
184
186
  metadata,
185
187
  )
188
+ command_output = maybe_truncate(
189
+ command_output, truncate_after=MAX_CMD_OUTPUT_SIZE
190
+ )
191
+
186
192
  self.prev_status = TerminalCommandStatus.COMPLETED
187
193
  self.prev_output = "" # Reset previous command output
188
194
  self._ready_for_next_command()
@@ -221,6 +227,9 @@ class TerminalSession(TerminalSessionBase):
221
227
  metadata,
222
228
  continue_prefix="[Below is the output of the previous command.]\n",
223
229
  )
230
+ command_output = maybe_truncate(
231
+ command_output, truncate_after=MAX_CMD_OUTPUT_SIZE
232
+ )
224
233
  return TerminalObservation.from_text(
225
234
  command=command,
226
235
  text=command_output,
@@ -257,6 +266,9 @@ class TerminalSession(TerminalSessionBase):
257
266
  metadata,
258
267
  continue_prefix="[Below is the output of the previous command.]\n",
259
268
  )
269
+ command_output = maybe_truncate(
270
+ command_output, truncate_after=MAX_CMD_OUTPUT_SIZE
271
+ )
260
272
  return TerminalObservation.from_text(
261
273
  command=command,
262
274
  exit_code=metadata.exit_code,
@@ -391,6 +403,9 @@ class TerminalSession(TerminalSessionBase):
391
403
  metadata,
392
404
  continue_prefix="[Below is the output of the previous command.]\n",
393
405
  )
406
+ command_output = maybe_truncate(
407
+ command_output, truncate_after=MAX_CMD_OUTPUT_SIZE
408
+ )
394
409
  obs = TerminalObservation.from_text(
395
410
  command=command,
396
411
  text=command_output,
@@ -4,6 +4,7 @@ import time
4
4
  import uuid
5
5
 
6
6
  import libtmux
7
+ from libtmux.exc import TmuxObjectDoesNotExist
7
8
 
8
9
  from openhands.sdk.logger import get_logger
9
10
  from openhands.sdk.utils import sanitized_env
@@ -52,13 +53,35 @@ class TmuxTerminal(TerminalInterface):
52
53
 
53
54
  logger.debug(f"Initializing tmux terminal with command: {window_command}")
54
55
  session_name = f"openhands-{self.username}-{uuid.uuid4()}"
55
- self.session = self.server.new_session(
56
- session_name=session_name,
57
- start_directory=self.work_dir,
58
- kill_session=True,
59
- x=1000,
60
- y=1000,
61
- )
56
+
57
+ # Retry session creation to handle race conditions in libtmux
58
+ # where the session is created but can't be found immediately
59
+ max_retries = 3
60
+ retry_delay = 0.5
61
+ last_error = None
62
+ for attempt in range(max_retries):
63
+ try:
64
+ self.session = self.server.new_session(
65
+ session_name=session_name,
66
+ start_directory=self.work_dir,
67
+ kill_session=True,
68
+ x=1000,
69
+ y=1000,
70
+ )
71
+ break
72
+ except TmuxObjectDoesNotExist as e:
73
+ last_error = e
74
+ if attempt < max_retries - 1:
75
+ logger.warning(
76
+ f"Tmux session creation failed (attempt {attempt + 1}/"
77
+ f"{max_retries}), retrying in {retry_delay}s: {e}"
78
+ )
79
+ time.sleep(retry_delay)
80
+ retry_delay *= 2 # Exponential backoff
81
+ else:
82
+ raise RuntimeError(
83
+ f"Failed to create tmux session after {max_retries} attempts"
84
+ ) from last_error
62
85
  for k, v in env.items():
63
86
  self.session.set_environment(k, v)
64
87
 
@@ -141,7 +141,7 @@ def escape_bash_special_chars(command: str) -> str:
141
141
  remaining = command[last_pos:]
142
142
  parts.append(remaining)
143
143
  return "".join(parts)
144
- except (ParsingError, NotImplementedError, TypeError):
144
+ except (ParsingError, NotImplementedError, TypeError, AttributeError):
145
145
  logger.debug(
146
146
  f"Failed to parse bash commands for special characters escape\n[input]: "
147
147
  f"{command}\n[warning]: {traceback.format_exc()}\nThe original command "
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openhands-tools
3
- Version: 1.10.0
3
+ Version: 1.11.0
4
4
  Summary: OpenHands Tools - Runtime tools for AI agents
5
5
  Project-URL: Source, https://github.com/OpenHands/software-agent-sdk
6
6
  Project-URL: Homepage, https://github.com/OpenHands/software-agent-sdk
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "openhands-tools"
3
- version = "1.10.0"
3
+ version = "1.11.0"
4
4
  description = "OpenHands Tools - Runtime tools for AI agents"
5
5
 
6
6
  requires-python = ">=3.12"