openhands-tools 1.27.0__tar.gz → 1.28.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 (101) hide show
  1. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/PKG-INFO +1 -1
  2. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/terminal/definition.py +49 -0
  3. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/terminal/impl.py +29 -0
  4. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands_tools.egg-info/PKG-INFO +1 -1
  5. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/pyproject.toml +1 -1
  6. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/__init__.py +0 -0
  7. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/apply_patch/__init__.py +0 -0
  8. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/apply_patch/core.py +0 -0
  9. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/apply_patch/definition.py +0 -0
  10. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/browser_use/__init__.py +0 -0
  11. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/browser_use/definition.py +0 -0
  12. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/browser_use/event_storage.py +0 -0
  13. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/browser_use/impl.py +0 -0
  14. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/browser_use/logging_fix.py +0 -0
  15. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/browser_use/recording.py +0 -0
  16. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/browser_use/server.py +0 -0
  17. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/delegate/__init__.py +0 -0
  18. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/delegate/definition.py +0 -0
  19. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/delegate/impl.py +0 -0
  20. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/delegate/templates/delegate_tool_description.j2 +0 -0
  21. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/delegate/visualizer.py +0 -0
  22. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/file_editor/__init__.py +0 -0
  23. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/file_editor/definition.py +0 -0
  24. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/file_editor/editor.py +0 -0
  25. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/file_editor/exceptions.py +0 -0
  26. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/file_editor/impl.py +0 -0
  27. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/file_editor/utils/__init__.py +0 -0
  28. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/file_editor/utils/config.py +0 -0
  29. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/file_editor/utils/constants.py +0 -0
  30. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/file_editor/utils/diff.py +0 -0
  31. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/file_editor/utils/encoding.py +0 -0
  32. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/file_editor/utils/file_cache.py +0 -0
  33. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/file_editor/utils/history.py +0 -0
  34. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/file_editor/utils/shell.py +0 -0
  35. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/gemini/__init__.py +0 -0
  36. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/gemini/edit/__init__.py +0 -0
  37. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/gemini/edit/definition.py +0 -0
  38. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/gemini/edit/impl.py +0 -0
  39. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/gemini/list_directory/__init__.py +0 -0
  40. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/gemini/list_directory/definition.py +0 -0
  41. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/gemini/list_directory/impl.py +0 -0
  42. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/gemini/read_file/__init__.py +0 -0
  43. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/gemini/read_file/definition.py +0 -0
  44. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/gemini/read_file/impl.py +0 -0
  45. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/gemini/write_file/__init__.py +0 -0
  46. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/gemini/write_file/definition.py +0 -0
  47. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/gemini/write_file/impl.py +0 -0
  48. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/glob/__init__.py +0 -0
  49. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/glob/definition.py +0 -0
  50. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/glob/impl.py +0 -0
  51. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/grep/__init__.py +0 -0
  52. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/grep/definition.py +0 -0
  53. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/grep/impl.py +0 -0
  54. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/planning_file_editor/__init__.py +0 -0
  55. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/planning_file_editor/definition.py +0 -0
  56. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/planning_file_editor/impl.py +0 -0
  57. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/preset/__init__.py +0 -0
  58. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/preset/default.py +0 -0
  59. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/preset/gemini.py +0 -0
  60. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/preset/gpt5.py +0 -0
  61. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/preset/planning.py +0 -0
  62. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/preset/subagents/bash_runner.md +0 -0
  63. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/preset/subagents/code_explorer.md +0 -0
  64. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/preset/subagents/default.md +0 -0
  65. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/preset/subagents/web_researcher.md +0 -0
  66. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/py.typed +0 -0
  67. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/task/__init__.py +0 -0
  68. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/task/definition.py +0 -0
  69. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/task/impl.py +0 -0
  70. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/task/manager.py +0 -0
  71. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/task_tracker/__init__.py +0 -0
  72. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/task_tracker/definition.py +0 -0
  73. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/terminal/__init__.py +0 -0
  74. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/terminal/constants.py +0 -0
  75. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/terminal/descriptions.py +0 -0
  76. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/terminal/metadata.py +0 -0
  77. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/terminal/terminal/__init__.py +0 -0
  78. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/terminal/terminal/factory.py +0 -0
  79. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/terminal/terminal/interface.py +0 -0
  80. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/terminal/terminal/subprocess_terminal.py +0 -0
  81. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/terminal/terminal/terminal_session.py +0 -0
  82. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/terminal/terminal/tmux_pane_pool.py +0 -0
  83. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/terminal/terminal/tmux_terminal.py +0 -0
  84. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/terminal/terminal/windows_terminal.py +0 -0
  85. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/terminal/timeout_policy.py +0 -0
  86. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/terminal/utils/__init__.py +0 -0
  87. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/terminal/utils/command.py +0 -0
  88. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/terminal/utils/escape_filter.py +0 -0
  89. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/tom_consult/__init__.py +0 -0
  90. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/tom_consult/definition.py +0 -0
  91. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/tom_consult/executor.py +0 -0
  92. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/utils/__init__.py +0 -0
  93. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/utils/timeout.py +0 -0
  94. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/workflow/__init__.py +0 -0
  95. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/workflow/definition.py +0 -0
  96. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands/tools/workflow/impl.py +0 -0
  97. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands_tools.egg-info/SOURCES.txt +0 -0
  98. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands_tools.egg-info/dependency_links.txt +0 -0
  99. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands_tools.egg-info/requires.txt +0 -0
  100. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/openhands_tools.egg-info/top_level.txt +0 -0
  101. {openhands_tools-1.27.0 → openhands_tools-1.28.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openhands-tools
3
- Version: 1.27.0
3
+ Version: 1.28.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
@@ -34,6 +34,55 @@ from openhands.tools.terminal.descriptions import (
34
34
  from openhands.tools.terminal.metadata import CmdOutputMetadata
35
35
 
36
36
 
37
+ _LITERAL_ARG_HINT_TEMPLATE = (
38
+ "[Tool-argument error] The `command` argument looks like a Python/JSON "
39
+ "{literal_kind}, not a shell command. It starts with: {head!r}\n\n"
40
+ "The `terminal` tool runs exactly ONE shell command at a time. To pass "
41
+ "structured data or multi-line code:\n"
42
+ " - Write a script first with `file_editor` "
43
+ '(command="create", path="/tmp/run.py", ...), then invoke it: '
44
+ "`python /tmp/run.py`.\n"
45
+ " - Or use a heredoc inline, e.g.:\n"
46
+ " python - <<'EOF'\n"
47
+ " DATABASES = {{'default': {{...}}}}\n"
48
+ " # your code here\n"
49
+ " EOF\n\n"
50
+ "Do not put a Python list/dict literal into the `command` field; the shell "
51
+ "cannot interpret it."
52
+ )
53
+
54
+
55
+ def looks_like_python_literal_argument(command: str) -> str | None:
56
+ """Detect when a tool call has packed structured data into `command`.
57
+
58
+ Returns a short reason string (``"list literal"``, ``"nested list literal"``
59
+ or ``"dict literal"``) if ``command`` appears to be a Python/JSON literal
60
+ rather than a shell command, otherwise ``None``.
61
+
62
+ Carefully distinguishes legitimate bash uses of ``[`` and ``[[`` (which
63
+ are always followed by whitespace) from Python-style literals. See the
64
+ accompanying tests for the full matrix.
65
+ """
66
+ stripped = command.lstrip()
67
+ if len(stripped) < 2:
68
+ return None
69
+ a, b = stripped[0], stripped[1]
70
+ # Top-level list literals: [{...}], ["..."], ['...']
71
+ if a == "[" and b in ("{", '"', "'"):
72
+ return "list literal"
73
+ # Nested list literals: [[...]] — but bash `[[ -f x ]]` is followed by a
74
+ # whitespace char, so we only flag `[[` followed by non-whitespace.
75
+ if a == "[" and b == "[":
76
+ if len(stripped) >= 3 and stripped[2] not in (" ", "\t"):
77
+ return "nested list literal"
78
+ return None
79
+ # Top-level dict literals: {"key": ...} or {'key': ...}.
80
+ # Bash group commands `{ ls; }` always have a space after `{`.
81
+ if a == "{" and b in ('"', "'"):
82
+ return "dict literal"
83
+ return None
84
+
85
+
37
86
  class TerminalAction(Action):
38
87
  """Schema for terminal command execution."""
39
88
 
@@ -15,8 +15,10 @@ if TYPE_CHECKING:
15
15
  from openhands.sdk.conversation import LocalConversation
16
16
  from openhands.tools.terminal.constants import CMD_OUTPUT_PS1_END
17
17
  from openhands.tools.terminal.definition import (
18
+ _LITERAL_ARG_HINT_TEMPLATE,
18
19
  TerminalAction,
19
20
  TerminalObservation,
21
+ looks_like_python_literal_argument,
20
22
  )
21
23
  from openhands.tools.terminal.terminal.factory import (
22
24
  _is_tmux_available,
@@ -538,6 +540,33 @@ class TerminalExecutor(ToolExecutor[TerminalAction, TerminalObservation]):
538
540
  if action.reset and action.is_input:
539
541
  raise ValueError("Cannot use reset=True with is_input=True")
540
542
 
543
+ # Short-circuit obvious tool-call malformation: Python/JSON literals
544
+ # passed where the model should have sent a shell command. The shell
545
+ # would otherwise echo a confusing `command not found` and the model
546
+ # rarely self-corrects without a structured hint. Skip the check when
547
+ # `is_input=True` because that path forwards raw bytes (e.g. keystrokes
548
+ # like `C-c`) to a running process and is not a fresh shell command.
549
+ if not action.is_input:
550
+ literal_kind = looks_like_python_literal_argument(action.command)
551
+ if literal_kind is not None:
552
+ head = action.command.lstrip()[:60]
553
+ logger.warning(
554
+ "Rejected terminal call: command argument looks like a "
555
+ "Python/JSON %s (head=%r). Returning structured hint to "
556
+ "the model instead of executing.",
557
+ literal_kind,
558
+ head,
559
+ )
560
+ return TerminalObservation.from_text(
561
+ _LITERAL_ARG_HINT_TEMPLATE.format(
562
+ literal_kind=literal_kind,
563
+ head=head,
564
+ ),
565
+ is_error=True,
566
+ command=action.command,
567
+ exit_code=None,
568
+ )
569
+
541
570
  if self._pool is not None:
542
571
  return self._execute_pooled(action, conversation)
543
572
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openhands-tools
3
- Version: 1.27.0
3
+ Version: 1.28.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.27.0"
3
+ version = "1.28.0"
4
4
  description = "OpenHands Tools - Runtime tools for AI agents"
5
5
 
6
6
  requires-python = ">=3.12"