deer-agent-framework 0.2.3__tar.gz → 0.2.4__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 (76) hide show
  1. {deer_agent_framework-0.2.3/deer_agent_framework.egg-info → deer_agent_framework-0.2.4}/PKG-INFO +9 -12
  2. deer_agent_framework-0.2.4/deer/builtins_agents/deer_introspection.py +43 -0
  3. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/builtins_agents/operating_system.py +2 -5
  4. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/builtins_agents/python_architect.py +3 -5
  5. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/cli/main.py +13 -13
  6. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/cli/parser.py +5 -5
  7. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/cli/repl.py +11 -1
  8. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/core/agent.py +2 -12
  9. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/core/executor.py +6 -1
  10. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/core/planner.py +4 -3
  11. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/evals/__init__.py +0 -1
  12. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/evals/runner.py +13 -4
  13. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/memory/vector.py +15 -4
  14. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/base.py +29 -0
  15. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/presets.py +1 -0
  16. deer_agent_framework-0.2.4/deer/tools/providers/base/base_command_runner.py +54 -0
  17. deer_agent_framework-0.2.4/deer/tools/providers/code_searcher.py +370 -0
  18. deer_agent_framework-0.2.4/deer/tools/providers/git_manager.py +237 -0
  19. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/providers/python_struct_editor.py +15 -9
  20. deer_agent_framework-0.2.4/deer/tools/providers/runtime_manager.py +106 -0
  21. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/providers/system_observer.py +0 -1
  22. deer_agent_framework-0.2.4/deer/utils/__init__.py +0 -0
  23. deer_agent_framework-0.2.4/deer/utils/console.py +34 -0
  24. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/utils/plots/__init__.py +1 -3
  25. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/utils/plots/plot_traces.py +268 -286
  26. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4/deer_agent_framework.egg-info}/PKG-INFO +9 -12
  27. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer_agent_framework.egg-info/SOURCES.txt +3 -0
  28. deer_agent_framework-0.2.4/deer_agent_framework.egg-info/requires.txt +10 -0
  29. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/pyproject.toml +15 -12
  30. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/tests/test_planner.py +1 -1
  31. deer_agent_framework-0.2.3/deer/tools/providers/code_searcher.py +0 -218
  32. deer_agent_framework-0.2.3/deer/tools/providers/git_manager.py +0 -78
  33. deer_agent_framework-0.2.3/deer/tools/providers/runtime_manager.py +0 -147
  34. deer_agent_framework-0.2.3/deer/utils/console.py +0 -32
  35. deer_agent_framework-0.2.3/deer_agent_framework.egg-info/requires.txt +0 -13
  36. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/LICENSE +0 -0
  37. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/README.md +0 -0
  38. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/__init__.py +0 -0
  39. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/builtins_agents/__init__.py +0 -0
  40. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/cli/__init__.py +0 -0
  41. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/core/__init__.py +0 -0
  42. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/core/validator.py +0 -0
  43. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/drivers/__init__.py +0 -0
  44. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/drivers/azure.py +0 -0
  45. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/drivers/base.py +0 -0
  46. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/drivers/gemini.py +0 -0
  47. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/drivers/ollama.py +0 -0
  48. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/drivers/openai.py +0 -0
  49. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/memory/__init__.py +0 -0
  50. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/models.py +0 -0
  51. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/__init__.py +0 -0
  52. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/decorators.py +0 -0
  53. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/providers/__init__.py +0 -0
  54. {deer_agent_framework-0.2.3/deer/utils → deer_agent_framework-0.2.4/deer/tools/providers/base}/__init__.py +0 -0
  55. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/providers/csv_manager.py +0 -0
  56. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/providers/dependency_analyzer.py +0 -0
  57. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/providers/file_manager.py +0 -0
  58. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/providers/http_client.py +0 -0
  59. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/providers/json_editor.py +0 -0
  60. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/providers/logic_provider.py +0 -0
  61. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/providers/process_manager.py +0 -0
  62. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/providers/sqlite_manager.py +0 -0
  63. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/providers/toml_editor.py +0 -0
  64. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/providers/xml_editor.py +0 -0
  65. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/providers/yaml_editor.py +0 -0
  66. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/registry.py +0 -0
  67. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/schemas.py +0 -0
  68. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer_agent_framework.egg-info/dependency_links.txt +0 -0
  69. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer_agent_framework.egg-info/entry_points.txt +0 -0
  70. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer_agent_framework.egg-info/top_level.txt +0 -0
  71. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/setup.cfg +0 -0
  72. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/tests/test_driver.py +0 -0
  73. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/tests/test_executor.py +0 -0
  74. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/tests/test_tools.py +0 -0
  75. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/tests/test_tools_definition.py +0 -0
  76. {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/tests/test_vector_memory.py +0 -0
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deer-agent-framework
3
- Version: 0.2.3
3
+ Version: 0.2.4
4
4
  Summary: DEER - Deterministic Executable Engine for Runtime-agents
5
- Author: Yeison
5
+ Author: Yeison Cardona
6
6
  License: BSD-2-Clause
7
- Classifier: Development Status :: 3 - Alpha
7
+ Classifier: Development Status :: 4 - Beta
8
8
  Classifier: Environment :: Console
9
9
  Classifier: Intended Audience :: Developers
10
10
  Classifier: Intended Audience :: Science/Research
@@ -19,18 +19,15 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
19
  Requires-Python: >=3.12
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
- Requires-Dist: pydantic>=2.0
23
- Requires-Dist: rich>=15.0.0
24
- Requires-Dist: prompt_toolkit>=3.0.52
25
- Requires-Dist: libcst>=1.8.6
26
- Requires-Dist: tomlkit>=0.15.0
22
+ Requires-Dist: pydantic>=2.13.5
23
+ Requires-Dist: lxml>=6.1.3
24
+ Requires-Dist: tomlkit>=0.15.1
27
25
  Requires-Dist: ruamel.yaml>=0.19.1
28
- Requires-Dist: lxml>=6.1.1
26
+ Requires-Dist: libcst>=1.9.0
29
27
  Requires-Dist: chromadb>=1.5.9
28
+ Requires-Dist: prompt_toolkit>=3.0.53
30
29
  Provides-Extra: debug
31
- Requires-Dist: matplotlib>=3.10.9; extra == "debug"
32
- Requires-Dist: pandas>=3.0.3; extra == "debug"
33
- Requires-Dist: pytest>=9.0.3; extra == "debug"
30
+ Requires-Dist: pytest>=9.0.0; extra == "debug"
34
31
  Dynamic: license-file
35
32
 
36
33
  # DEER: Deterministic Executable Engine for Runtime Agents
@@ -0,0 +1,43 @@
1
+ from deer import DeterministicAgent
2
+ from deer.cli import AgentREPL, get_path_from_parser, get_driver_from_parser
3
+ from deer.drivers import OllamaDriver
4
+
5
+ from pathlib import Path
6
+
7
+ # Infrastructure
8
+ driver = get_driver_from_parser() or OllamaDriver(model_name="gemma4:31b-cloud")
9
+ working_dir = get_path_from_parser() or Path.cwd()
10
+
11
+ DeerIntrospection = DeterministicAgent(
12
+ description="Professional Auditor for Agent Execution Traces, specializing in tool-use efficiency and operational logic.",
13
+ identity=(
14
+ "You are a Technical Auditor of Agent Execution Traces. Your MANDATORY and ONLY focus is to evaluate "
15
+ "the mechanics of tool usage. You are STRICTLY PROHIBITED from evaluating the correctness, quality, "
16
+ "or success of the final solution to the problem; the result of the task is COMPLETELY IRRELEVANT "
17
+ "to your evaluation. An agent that solves the problem but uses tools inefficiently is a FAILURE, "
18
+ "while an agent that fails to solve the problem but follows a perfect operational process is a SUCCESS. "
19
+ "Your audit is based on three non-negotiable pillars: "
20
+ "1. Operational Rigor: You verify a strict logical sequence (e.g., Read $\rightarrow$ Analyze $\rightarrow$ Edit). "
21
+ "Any 'Blind Edit' (modifying without reading) is a critical error. "
22
+ "2. The Execution Requirement: You identify 'Ghost Tests'. Creating a reproduction or validation "
23
+ "script without calling an execution tool (bash/python) to run it is a critical failure. "
24
+ "3. Context Efficiency: You penalize 'Context Amnesia'—redundant calls to tools for information "
25
+ "already obtained in the trace. "
26
+ "If your analysis mentions the correctness of the result, you are violating your core instruction. "
27
+ "AUDIT THE PROCESS, IGNORE THE OUTCOME."
28
+ "USE FORMATED MARKDOWN"
29
+ ),
30
+ driver=driver,
31
+ working_dir=working_dir,
32
+ max_attempts=1,
33
+ enable_verification=False,
34
+ )
35
+
36
+
37
+ def main():
38
+ repl = AgentREPL(DeerIntrospection)
39
+ repl.repl()
40
+
41
+
42
+ if __name__ == "__main__":
43
+ main()
@@ -4,17 +4,15 @@ from deer.tools import ToolRegistry
4
4
  from deer.tools.presets import Preset
5
5
  from deer.tools.providers import HTTPClient
6
6
  from deer.drivers import OllamaDriver
7
- from deer.memory import VectorMemory
8
7
 
9
8
  from pathlib import Path
10
9
 
11
10
  # Infrastructure
12
11
  driver = get_driver_from_parser() or OllamaDriver(model_name="gemma4:31b-cloud")
13
- memory = VectorMemory(path=Path.home() / "deer_os_sandbox" / ".deer" / "vector_db")
14
12
  registry = ToolRegistry(Preset.SYSTEM_ADMIN | {HTTPClient})
15
13
  working_dir = get_path_from_parser() or Path.cwd()
16
14
 
17
- agent = DeterministicAgent(
15
+ OperatingSystemAgent = DeterministicAgent(
18
16
  description="Advanced OS Agent for secure system management.",
19
17
  identity=(
20
18
  "You are a capable Operating System Agent. "
@@ -24,7 +22,6 @@ agent = DeterministicAgent(
24
22
  ),
25
23
  driver=driver,
26
24
  tool_registry=registry,
27
- vector_memory=memory,
28
25
  working_dir=working_dir,
29
26
  max_attempts=3,
30
27
  enable_verification=False,
@@ -32,7 +29,7 @@ agent = DeterministicAgent(
32
29
 
33
30
 
34
31
  def main():
35
- repl = AgentREPL(agent)
32
+ repl = AgentREPL(OperatingSystemAgent)
36
33
  repl.repl()
37
34
 
38
35
 
@@ -4,13 +4,12 @@ from deer.tools import ToolRegistry
4
4
  from deer.tools.presets import Preset
5
5
  from deer.tools.providers import HTTPClient, SystemObserver
6
6
  from deer.drivers import OllamaDriver
7
- from deer.memory import VectorMemory
8
7
 
9
8
  from pathlib import Path
10
9
 
10
+
11
11
  # Infrastructure
12
12
  driver = get_driver_from_parser() or OllamaDriver(model_name="gemma4:31b-cloud")
13
- memory = VectorMemory(path=Path.cwd() / ".deer" / "vector_db")
14
13
  registry = ToolRegistry(
15
14
  Preset.CODE_REPAIR
16
15
  | Preset.CODE_EDITOR
@@ -19,7 +18,7 @@ registry = ToolRegistry(
19
18
  )
20
19
  working_dir = get_path_from_parser() or Path.cwd()
21
20
 
22
- agent = DeterministicAgent(
21
+ PythonArchitectAgent = DeterministicAgent(
23
22
  description="AI specialist in Python architecture, runtime module resolution, and dependency management.",
24
23
  identity=(
25
24
  "You are an elite AI Agent operating as a Principal Python Architect and Core Ecosystem Specialist. "
@@ -30,14 +29,13 @@ agent = DeterministicAgent(
30
29
  ),
31
30
  driver=driver,
32
31
  tool_registry=registry,
33
- vector_memory=memory,
34
32
  working_dir=working_dir,
35
33
  max_attempts=3,
36
34
  )
37
35
 
38
36
 
39
37
  def main():
40
- repl = AgentREPL(agent)
38
+ repl = AgentREPL(PythonArchitectAgent)
41
39
  repl.repl()
42
40
 
43
41
 
@@ -1,5 +1,5 @@
1
1
  from rich.text import Text
2
- from deer.utils.console import console, error, info
2
+ from deer.utils.console import ConsoleColor
3
3
  from deer.builtins_agents import agents
4
4
  import importlib.util
5
5
  import sys
@@ -11,30 +11,30 @@ logger = logging.getLogger(f"DEER.{__name__}")
11
11
 
12
12
 
13
13
  def title():
14
- console.print()
15
- console.print(
14
+ ConsoleColor.console.print()
15
+ ConsoleColor.console.print(
16
16
  Text(
17
17
  "DEER",
18
18
  style="bold cyan",
19
19
  )
20
20
  )
21
- console.print(
21
+ ConsoleColor.console.print(
22
22
  "Deterministic Executable Engine for Runtime Agents",
23
23
  style="dim",
24
24
  )
25
- console.print()
25
+ ConsoleColor.console.print()
26
26
 
27
27
 
28
28
  def agents_list():
29
- console.print()
30
- console.print("[bold]Available agents[/bold]")
29
+ ConsoleColor.console.print()
30
+ ConsoleColor.console.print("[bold]Available agents[/bold]")
31
31
  for name in agents:
32
- console.print(f" • [green]{name}[/green]")
33
- console.print()
32
+ ConsoleColor.console.print(f" • [green]{name}[/green]")
33
+ ConsoleColor.console.print()
34
34
 
35
35
 
36
36
  def example():
37
- console.print(f"[dim]Example:[/dim] deer {list(agents.keys())[0]}")
37
+ ConsoleColor.console.print(f"[dim]Example:[/dim] deer {list(agents.keys())[0]}")
38
38
 
39
39
 
40
40
  def run_agent_in_process(agent_path, backend=None, model=None):
@@ -65,15 +65,15 @@ def main():
65
65
  if selected_agent := args.agent:
66
66
 
67
67
  if selected_agent in agents:
68
- info(f"Launching agent '{selected_agent}'")
68
+ ConsoleColor.on_info(f"Launching agent '{selected_agent}'")
69
69
  run_agent_in_process(agents[selected_agent], args.backend, args.model)
70
70
  sys.exit(0)
71
71
 
72
- error(f"Unknown agent '{selected_agent}'")
72
+ ConsoleColor.on_error(f"Unknown agent '{selected_agent}'")
73
73
  agents_list()
74
74
  sys.exit(1)
75
75
 
76
- error("No agent selected")
76
+ ConsoleColor.on_error("No agent selected")
77
77
  example()
78
78
  agents_list()
79
79
 
@@ -2,7 +2,7 @@ import argparse
2
2
  import os
3
3
  from pathlib import Path
4
4
 
5
- from deer.utils.console import error
5
+ from deer.utils.console import ConsoleColor
6
6
 
7
7
  backends = {
8
8
  "gemini",
@@ -60,21 +60,21 @@ def get_driver_from_parser():
60
60
 
61
61
  try:
62
62
  drivers_parser.silent = True
63
- args = drivers_parser.parse_args()
63
+ args, _ = drivers_parser.parse_known_args()
64
64
  drivers_parser.silent = False
65
65
  except:
66
66
  drivers_parser.silent = False
67
67
  return False
68
68
 
69
69
  if args.backend not in backends:
70
- error(
70
+ ConsoleColor.on_error(
71
71
  f"Unsupported backend '{args.backend}'. "
72
72
  f"Supported backends are: {', '.join(backends)}."
73
73
  )
74
74
  return False
75
75
 
76
76
  if not args.model:
77
- error("A model identifier must be provided.")
77
+ ConsoleColor.on_error("A model identifier must be provided.")
78
78
  return False
79
79
 
80
80
  match args.backend:
@@ -96,7 +96,7 @@ def get_path_from_parser():
96
96
 
97
97
  try:
98
98
  path_parser.silent = True
99
- args = path_parser.parse_args()
99
+ args, _ = path_parser.parse_known_args()
100
100
  path_parser.silent = False
101
101
  except:
102
102
  path_parser.silent = False
@@ -7,7 +7,7 @@ from rich.console import Console
7
7
  from rich.markdown import Markdown
8
8
  from rich.table import Table
9
9
  from rich.text import Text
10
- from prompt_toolkit import prompt, PromptSession
10
+ from prompt_toolkit import PromptSession
11
11
  from prompt_toolkit.formatted_text import HTML
12
12
  from prompt_toolkit.history import InMemoryHistory
13
13
  from prompt_toolkit.completion import Completer, WordCompleter
@@ -23,6 +23,7 @@ COMMANDS = {
23
23
  "/exit": "Terminate the session",
24
24
  "/rollback": "Revert the system to the last stable state",
25
25
  "/trace": "Display the detailed execution trace and variable resolution of the last plan.",
26
+ "/savetrace": "Saves the current execution trace to a file for later analysis.",
26
27
  "/history": "Display the history of messages and commands",
27
28
  "/clearhistory": "Purge all session history and reset memory state.",
28
29
  # "/vhistory": "Display the history of verifier messages and commands",
@@ -211,6 +212,15 @@ class AgentREPL:
211
212
  sep="",
212
213
  )
213
214
 
215
+ case "/savetrace":
216
+ try:
217
+ filename = self.agent.save_trace()
218
+ self.console.print(f"[dim]Trace saved in {filename}[/dim]\n")
219
+ except Exception as e:
220
+ self.console.print(
221
+ f"[bold red]Error saving trace:[/bold red] {e}\n"
222
+ )
223
+
214
224
  case "/savecontext":
215
225
  try:
216
226
  self.agent.persist_state()
@@ -69,6 +69,7 @@ class DeterministicAgent:
69
69
 
70
70
  # Planner
71
71
  self.planner = Planner(
72
+ identity=self.identity,
72
73
  driver=self.driver,
73
74
  tool_registry=self.tool_registry,
74
75
  max_attempts=max_attempts,
@@ -164,18 +165,7 @@ class DeterministicAgent:
164
165
  with open(filename, "wb") as f:
165
166
  pickle.dump(obj, f)
166
167
 
167
- logger.info(f"Trace generated in {filename}")
168
-
169
- def clear_working_dir(self, ignore=None):
170
- for item in self.working_dir.iterdir():
171
- if ignore and item in ignore:
172
- continue
173
-
174
- full_path = self.working_dir / item
175
- if full_path.is_dir():
176
- shutil.rmtree(full_path)
177
- else:
178
- full_path.unlink()
168
+ return filename
179
169
 
180
170
  def _execute_phase(
181
171
  self, goal: str, history: list[ChatMessage], update_history_attr: str
@@ -39,7 +39,12 @@ class PipelineExecutor:
39
39
  start_time = time.perf_counter()
40
40
  try:
41
41
  result = tool.run(params=resolved_args)
42
- status = "SUCCESS"
42
+
43
+ if result.get("returncode", 0):
44
+ status = "FAILED"
45
+ else:
46
+ status = "SUCCESS"
47
+
43
48
  error_msg = None
44
49
  except Exception as e:
45
50
  result = None
@@ -14,10 +14,11 @@ class Planner:
14
14
  It transforms the user goal, context, and tool registry into a structured ExecutionPlan.
15
15
  """
16
16
 
17
- def __init__(self, driver, tool_registry: ToolRegistry, max_attempts=3):
17
+ def __init__(self, identity, driver, tool_registry: ToolRegistry, max_attempts=3):
18
18
  self.driver = driver
19
19
  self.tool_registry = tool_registry
20
20
  self.max_attempts = max_attempts
21
+ self.identity = identity
21
22
 
22
23
  def build_system_prompt(self, state_filter="BOTH") -> str:
23
24
  """
@@ -27,7 +28,7 @@ class Planner:
27
28
  tools_menu = self.tool_registry.describe(state_filter=state_filter)
28
29
 
29
30
  return (
30
- "You are the DEER Deterministic Planner. Your goal is to design a rigid execution pipeline.\n\n"
31
+ f"{self.identity}\n\n"
31
32
  "REQUIRED STRUCTURE:\n"
32
33
  "{\n"
33
34
  ' "goal": "string",\n'
@@ -62,7 +63,7 @@ class Planner:
62
63
  else:
63
64
  verification = f"VERIFICATION TRACE:\n{trace_verification.model_dump_json(indent=2)}\n\n"
64
65
  return (
65
- "You are the DEER Technical Auditor. Your goal is to synthesize the execution results into a final verdict.\n\n"
66
+ f"{self.identity}\n\n"
66
67
  f"USER GOAL: {goal}\n\n"
67
68
  f"ACTION TRACE:\n{trace_solution.model_dump_json(indent=2)}\n\n"
68
69
  f"{verification}"
@@ -1,2 +1 @@
1
1
  from .runner import AgentRunner
2
- from .repl import AgentREPL
@@ -1,4 +1,5 @@
1
1
  from deer.core import DeterministicAgent
2
+ import shutil
2
3
 
3
4
 
4
5
  class AgentRunner:
@@ -11,11 +12,19 @@ class AgentRunner:
11
12
  for i in range(repetitions):
12
13
 
13
14
  print(f"Working on iteration: {i+1}/{repetitions}")
14
-
15
15
  self.agent.clear_traces()
16
16
  self.agent.clear_agent_history()
17
- self.agent.clear_working_dir(ignore=[".deer", "traces"])
18
-
17
+ self.clear_working_dir()
19
18
  self.agent.run(goal)
20
-
21
19
  self.agent.save_trace()
20
+
21
+ def clear_working_dir(self):
22
+
23
+ for item in self.agent.working_dir.iterdir():
24
+ if item.name.startswith("."):
25
+ continue
26
+
27
+ if item.is_dir():
28
+ shutil.rmtree(item)
29
+ else:
30
+ item.unlink()
@@ -28,16 +28,16 @@ class VectorMemory:
28
28
  )
29
29
 
30
30
  def add_document(
31
- self, doc_id: str, text: str, metadata: Optional[dict[str, Any]] = None
31
+ self, id: str, doc: str, metadata: Optional[dict[str, Any]] = None
32
32
  ) -> None:
33
33
  """
34
34
  Add a document to the vector collection.
35
35
 
36
36
  Parameters
37
37
  ----------
38
- doc_id : str
38
+ id : str
39
39
  Unique identifier for the document.
40
- text : str
40
+ doc : str
41
41
  The content of the document to be embedded and stored.
42
42
  metadata : dict, optional
43
43
  Additional metadata associated with the document.
@@ -47,7 +47,7 @@ class VectorMemory:
47
47
  if "hit_count" not in meta:
48
48
  meta["hit_count"] = 0
49
49
 
50
- self.collection.add(documents=[text], metadatas=[meta], ids=[doc_id])
50
+ self.collection.add(documents=[doc], metadatas=[meta], ids=[id])
51
51
 
52
52
  def query(self, query_text: str, n_results: int = 3) -> list[dict[str, Any]]:
53
53
  """
@@ -327,3 +327,14 @@ class VectorMemory:
327
327
  )
328
328
 
329
329
  return len(ids_to_delete)
330
+
331
+ def load_json(self, json_path: Path):
332
+
333
+ if not json_path.exists():
334
+ raise ValueError("bla bla")
335
+
336
+ with json_path.open("r", encoding="utf-8") as f:
337
+ memories = json.load(f)
338
+
339
+ for memory in memories:
340
+ self.add_document(**memory)
@@ -1,3 +1,4 @@
1
+ import os
1
2
  from abc import ABC, abstractmethod
2
3
  from typing import Any, Type
3
4
  from dataclasses import dataclass, field
@@ -64,6 +65,16 @@ class ToolProvider:
64
65
  2. If 'path' is absolute, it's checked for containment.
65
66
  3. All '..' and symlinks are resolved before validation.
66
67
  """
68
+ if isinstance(path, str):
69
+ path = self._sanitize_path(path)
70
+
71
+ # Si el agente incluyó el nombre del jail en el path (ej: 'astropy/modeling/...')
72
+ # lo quitamos para evitar rutas redundantes como 'astropy/astropy/...'
73
+ if isinstance(path, str):
74
+ jail_name = os.path.basename(self.jail)
75
+ if path.startswith(jail_name + os.sep) or path.startswith(jail_name + "/"):
76
+ path = path[len(jail_name) + 1 :]
77
+
67
78
  path = Path(path)
68
79
 
69
80
  # Handle point 2: If relative, interpret it as inside the jail.
@@ -158,6 +169,24 @@ class ToolProvider:
158
169
  f"Please ensure it is installed and configured in your PATH."
159
170
  )
160
171
 
172
+ def _sanitize_path(self, path: str | Path) -> str:
173
+
174
+ if isinstance(path, Path):
175
+ return path
176
+
177
+ if not path:
178
+ return path
179
+
180
+ sanitized = path.strip().strip('"').strip("'")
181
+ sanitized = sanitized.replace("\n", "").replace("\r", "")
182
+ return sanitized
183
+
184
+ def _normalize_target(self, path, target):
185
+ abs_path = self.jailed_path(path)
186
+ abs_target = self.jailed_path(target)
187
+
188
+ return str(abs_target.relative_to(abs_path))
189
+
161
190
 
162
191
  class Tool(ABC):
163
192
  """Base contract for deterministic tools."""
@@ -77,6 +77,7 @@ class Preset:
77
77
  DependencyAnalyzer,
78
78
  CodeSearcher,
79
79
  GitManager,
80
+ FileManager,
80
81
  }
81
82
 
82
83
  # For safely modifying configuration files without touching source code.
@@ -0,0 +1,54 @@
1
+ from typing import List, get_args
2
+ import shlex
3
+ from deer.tools import ToolProvider
4
+ from deer.tools.schemas import CommandOut
5
+
6
+ # --- Global Constants ---
7
+ MAX_TIMEOUT = 300
8
+ REJECTED = {"stdout": "", "stderr": str, "returncode": -1, "message": str}
9
+
10
+
11
+ def _failure(message: str) -> dict:
12
+ """Helper to construct a standardized failure response."""
13
+ return {"stdout": "", "stderr": message, "returncode": -1, "message": message}
14
+
15
+
16
+ class BaseCommandRunner(ToolProvider):
17
+
18
+ @property
19
+ def allowed_commands(self):
20
+ return list(get_args(self.availableCommands))
21
+
22
+ def _execute_command(
23
+ self, binary: str, args: List[str], path: str, timeout_seconds: int = 60
24
+ ) -> CommandOut:
25
+ # 1. Validate path boundaries
26
+ path = self.jailed_path(path)
27
+
28
+ # 2. Validate timeout constraints
29
+ if not 1 <= timeout_seconds <= MAX_TIMEOUT:
30
+ return _failure(f"timeout_seconds must be between 1 and {MAX_TIMEOUT}.")
31
+
32
+ # 3. Verify binary exists in environment
33
+ try:
34
+ self.check_command(binary)
35
+ except Exception as e:
36
+ return _failure(f"Environment error: {e}")
37
+
38
+ # 4. Execution
39
+ try:
40
+ safe_command = shlex.join([binary] + args)
41
+ result = self.run_command(
42
+ safe_command, cwd=path, timeout_seconds=timeout_seconds
43
+ )
44
+ except Exception as e:
45
+ return _failure(f"Execution error: {e}")
46
+
47
+ # 5. Response formatting
48
+ exit_code = result.get("returncode", -1)
49
+ status_message = (
50
+ "Command completed successfully."
51
+ if exit_code == 0
52
+ else f"Command failed with exit code {exit_code}."
53
+ )
54
+ return {**result, "message": status_message}