deer-agent-framework 0.2a1__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 (77) hide show
  1. {deer_agent_framework-0.2a1/deer_agent_framework.egg-info → deer_agent_framework-0.2.4}/PKG-INFO +9 -12
  2. {deer_agent_framework-0.2a1/deer/builtins/agents → deer_agent_framework-0.2.4/deer/builtins_agents}/__init__.py +1 -1
  3. deer_agent_framework-0.2.4/deer/builtins_agents/deer_introspection.py +43 -0
  4. {deer_agent_framework-0.2a1/deer/builtins/agents → deer_agent_framework-0.2.4/deer/builtins_agents}/operating_system.py +3 -6
  5. {deer_agent_framework-0.2a1/deer/builtins/agents → deer_agent_framework-0.2.4/deer/builtins_agents}/python_architect.py +3 -5
  6. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/cli/main.py +18 -20
  7. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/cli/parser.py +5 -5
  8. deer_agent_framework-0.2.4/deer/cli/repl.py +378 -0
  9. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/core/agent.py +52 -21
  10. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/core/executor.py +7 -2
  11. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/core/planner.py +6 -5
  12. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/core/validator.py +1 -1
  13. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/drivers/base.py +6 -4
  14. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/drivers/ollama.py +8 -5
  15. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/evals/__init__.py +0 -1
  16. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/evals/runner.py +13 -4
  17. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/memory/vector.py +16 -5
  18. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/models.py +3 -3
  19. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/base.py +29 -0
  20. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/presets.py +1 -0
  21. deer_agent_framework-0.2.4/deer/tools/providers/base/base_command_runner.py +54 -0
  22. deer_agent_framework-0.2.4/deer/tools/providers/code_searcher.py +370 -0
  23. deer_agent_framework-0.2.4/deer/tools/providers/git_manager.py +237 -0
  24. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/providers/python_struct_editor.py +15 -9
  25. deer_agent_framework-0.2.4/deer/tools/providers/runtime_manager.py +106 -0
  26. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/providers/system_observer.py +0 -1
  27. deer_agent_framework-0.2.4/deer/utils/__init__.py +0 -0
  28. deer_agent_framework-0.2.4/deer/utils/console.py +34 -0
  29. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/utils/plots/__init__.py +1 -3
  30. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/utils/plots/plot_traces.py +268 -286
  31. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4/deer_agent_framework.egg-info}/PKG-INFO +9 -12
  32. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer_agent_framework.egg-info/SOURCES.txt +6 -3
  33. deer_agent_framework-0.2.4/deer_agent_framework.egg-info/requires.txt +10 -0
  34. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/pyproject.toml +15 -12
  35. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/tests/test_planner.py +1 -1
  36. deer_agent_framework-0.2a1/deer/cli/repl.py +0 -263
  37. deer_agent_framework-0.2a1/deer/tools/providers/code_searcher.py +0 -218
  38. deer_agent_framework-0.2a1/deer/tools/providers/git_manager.py +0 -78
  39. deer_agent_framework-0.2a1/deer/tools/providers/runtime_manager.py +0 -147
  40. deer_agent_framework-0.2a1/deer/utils/console.py +0 -32
  41. deer_agent_framework-0.2a1/deer_agent_framework.egg-info/requires.txt +0 -13
  42. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/LICENSE +0 -0
  43. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/README.md +0 -0
  44. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/__init__.py +0 -0
  45. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/cli/__init__.py +0 -0
  46. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/core/__init__.py +0 -0
  47. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/drivers/__init__.py +0 -0
  48. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/drivers/azure.py +0 -0
  49. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/drivers/gemini.py +0 -0
  50. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/drivers/openai.py +0 -0
  51. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/memory/__init__.py +0 -0
  52. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/__init__.py +0 -0
  53. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/decorators.py +0 -0
  54. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/providers/__init__.py +0 -0
  55. {deer_agent_framework-0.2a1/deer/utils → deer_agent_framework-0.2.4/deer/tools/providers/base}/__init__.py +0 -0
  56. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/providers/csv_manager.py +0 -0
  57. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/providers/dependency_analyzer.py +0 -0
  58. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/providers/file_manager.py +0 -0
  59. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/providers/http_client.py +0 -0
  60. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/providers/json_editor.py +0 -0
  61. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/providers/logic_provider.py +0 -0
  62. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/providers/process_manager.py +0 -0
  63. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/providers/sqlite_manager.py +0 -0
  64. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/providers/toml_editor.py +0 -0
  65. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/providers/xml_editor.py +0 -0
  66. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/providers/yaml_editor.py +0 -0
  67. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/registry.py +0 -0
  68. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/schemas.py +0 -0
  69. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer_agent_framework.egg-info/dependency_links.txt +0 -0
  70. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer_agent_framework.egg-info/entry_points.txt +0 -0
  71. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer_agent_framework.egg-info/top_level.txt +0 -0
  72. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/setup.cfg +0 -0
  73. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/tests/test_driver.py +0 -0
  74. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/tests/test_executor.py +0 -0
  75. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/tests/test_tools.py +0 -0
  76. {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/tests/test_tools_definition.py +0 -0
  77. {deer_agent_framework-0.2a1 → 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.2a1
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
@@ -1,6 +1,6 @@
1
1
  import os
2
2
 
3
- available_agents = {
3
+ agents = {
4
4
  "python_architect": os.path.join(os.path.dirname(__file__), "python_architect.py"),
5
5
  "operating_system": os.path.join(os.path.dirname(__file__), "operating_system.py"),
6
6
  }
@@ -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,18 +4,16 @@ 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(
18
- description="OS Agent",
15
+ OperatingSystemAgent = DeterministicAgent(
16
+ description="Advanced OS Agent for secure system management.",
19
17
  identity=(
20
18
  "You are a capable Operating System Agent. "
21
19
  "Your goal is to assist the user by managing the system, executing tasks, and solving problems efficiently. "
@@ -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,40 +1,40 @@
1
1
  from rich.text import Text
2
- from deer.utils.console import console, error, info
3
- from deer.builtins.agents import available_agents
2
+ from deer.utils.console import ConsoleColor
3
+ from deer.builtins_agents import agents
4
4
  import importlib.util
5
5
  import sys
6
6
  import logging
7
7
 
8
8
  from .parser import drivers_parser
9
9
 
10
- logger = logging.getLogger("DEER")
10
+ 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]")
31
- for name in available_agents:
32
- console.print(f" • [green]{name}[/green]")
33
- console.print()
29
+ ConsoleColor.console.print()
30
+ ConsoleColor.console.print("[bold]Available agents[/bold]")
31
+ for name in agents:
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(available_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):
@@ -64,18 +64,16 @@ def main():
64
64
 
65
65
  if selected_agent := args.agent:
66
66
 
67
- if selected_agent in available_agents:
68
- info(f"Launching agent '{selected_agent}'")
69
- run_agent_in_process(
70
- available_agents[selected_agent], args.backend, args.model
71
- )
67
+ if selected_agent in agents:
68
+ ConsoleColor.on_info(f"Launching agent '{selected_agent}'")
69
+ run_agent_in_process(agents[selected_agent], args.backend, args.model)
72
70
  sys.exit(0)
73
71
 
74
- error(f"Unknown agent '{selected_agent}'")
72
+ ConsoleColor.on_error(f"Unknown agent '{selected_agent}'")
75
73
  agents_list()
76
74
  sys.exit(1)
77
75
 
78
- error("No agent selected")
76
+ ConsoleColor.on_error("No agent selected")
79
77
  example()
80
78
  agents_list()
81
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
@@ -0,0 +1,378 @@
1
+ import sys
2
+ import pickle
3
+ import logging
4
+ import subprocess
5
+
6
+ from rich.console import Console
7
+ from rich.markdown import Markdown
8
+ from rich.table import Table
9
+ from rich.text import Text
10
+ from prompt_toolkit import PromptSession
11
+ from prompt_toolkit.formatted_text import HTML
12
+ from prompt_toolkit.history import InMemoryHistory
13
+ from prompt_toolkit.completion import Completer, WordCompleter
14
+
15
+ from deer.core import DeterministicAgent
16
+ from deer import __version__
17
+
18
+ logger = logging.getLogger(f"DEER.{__name__}")
19
+
20
+ COMMANDS = {
21
+ "/tools": "Show tools and capabilities",
22
+ "/clear": "Clear the console and reset the session",
23
+ "/exit": "Terminate the session",
24
+ "/rollback": "Revert the system to the last stable state",
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.",
27
+ "/history": "Display the history of messages and commands",
28
+ "/clearhistory": "Purge all session history and reset memory state.",
29
+ # "/vhistory": "Display the history of verifier messages and commands",
30
+ "/infocontext": "Displays context memory usage information.",
31
+ "/loadcontext": "Restore a saved session context",
32
+ "/savecontext": "Save the current session context",
33
+ }
34
+
35
+ WELCOME_MESSAGE = """
36
+ # DEER - Deterministic Executable Engine for Runtime Agents - v{version}
37
+
38
+ An LLM agent orchestration framework built for:
39
+ - Strict runtime control
40
+ - Enforced tool contracts
41
+ - Full execution traceability
42
+ - Guaranteed reproducibility
43
+
44
+ ## Runtime Pipeline
45
+
46
+ 1. Plan → Generate a structured execution strategy
47
+ 2. Validate → Verify contracts, dependencies, and safety
48
+ 3. Execute → Run approved tools and workflows
49
+ 4. Verify → Validate outputs against expected objectives
50
+
51
+ ## System Guarantees
52
+
53
+ - Deterministic execution flow
54
+ - Typed tool interfaces
55
+ - Structured validation layers
56
+ - Full execution traceability
57
+ - Backend-governed orchestration
58
+
59
+ ## Commands
60
+
61
+ {commands}
62
+
63
+ ## System Escapes
64
+
65
+ - `!<command>` → Execute an OS shell command (e.g., `!ls -la` or `!pwd`)
66
+
67
+ ## Shortcuts
68
+
69
+ - `Tab` → Auto-complete commands and navigate suggestions
70
+ - `Ctrl+C` → Stop processing the current task
71
+ """
72
+
73
+
74
+ class SmartCommandCompleter(Completer):
75
+ def __init__(self, commands):
76
+ self.base_completer = WordCompleter(commands, ignore_case=False)
77
+
78
+ def get_completions(self, document, complete_event):
79
+ word_before_cursor = document.get_word_before_cursor()
80
+ if not word_before_cursor:
81
+ return []
82
+ return self.base_completer.get_completions(document, complete_event)
83
+
84
+
85
+ class AgentREPL:
86
+
87
+ def __init__(self, agent: DeterministicAgent):
88
+ self.agent = agent
89
+
90
+ self.prompt_history: InMemoryHistory
91
+ self.load_prompt_history()
92
+
93
+ self.console = Console()
94
+
95
+ @property
96
+ def completer(self):
97
+ internal_commands = list(COMMANDS.keys())
98
+ return SmartCommandCompleter(internal_commands)
99
+
100
+ def load_prompt_history(self):
101
+ history_file = self.agent.agent_dir / "prompt_history"
102
+
103
+ if not history_file.exists():
104
+ self.prompt_history = InMemoryHistory()
105
+ return
106
+
107
+ with open(history_file, "rb") as f:
108
+ self.prompt_history = pickle.load(f)
109
+ return
110
+
111
+ def save_prompt_history(self):
112
+ history_file = self.agent.agent_dir / "prompt_history"
113
+ with open(history_file, "wb") as f:
114
+ pickle.dump(self.prompt_history, f)
115
+
116
+ def clear_history(self):
117
+ self.history = []
118
+ self.trace = []
119
+
120
+ def pretty_print(self, out: str):
121
+ self.console.print(Markdown(out))
122
+
123
+ def show_identity(self):
124
+ self.pretty_print(
125
+ f">**Agent Profile** \n"
126
+ f"*{self.agent.description}* \n"
127
+ f"root: {self.agent.tool_registry.jail_path} \n"
128
+ f"{self.agent.driver}: {self.agent.driver.model_name} \n"
129
+ )
130
+ print("")
131
+
132
+ def show_welcome(self):
133
+ commands_formated = ""
134
+ for command in COMMANDS:
135
+ commands_formated += (
136
+ f"- {('`'+command+'`').ljust(12, ' ')} → {COMMANDS[command]}\n "
137
+ )
138
+
139
+ self.console.clear()
140
+ self.pretty_print(
141
+ WELCOME_MESSAGE.format(version=__version__, commands=commands_formated)
142
+ )
143
+ self.show_identity()
144
+
145
+ def run_command(self, command):
146
+ match command:
147
+ case "/exit":
148
+ self.agent.persist_state()
149
+ self.console.print(
150
+ "[dim]Context state persisted to disk.[/dim]\n",
151
+ Markdown("----"),
152
+ )
153
+ sys.exit(0)
154
+
155
+ case "/clear":
156
+ self.console.clear()
157
+ self.show_identity()
158
+ self.console.print(
159
+ "[dim]Console buffer cleared. Session state persists. Use /clearhistory for a full purge.[/dim]\n",
160
+ )
161
+
162
+ case "/clearhistory":
163
+ self.clear_history()
164
+ self.console.clear()
165
+ self.agent.clear_agent_history()
166
+ self.agent.clear_traces()
167
+ self.agent.persist_state()
168
+ self.show_identity()
169
+ self.console.print(
170
+ "[dim]Session history purged. Memory state reset.[/dim]\n",
171
+ )
172
+
173
+ case "/tools":
174
+ tools_description = self.agent.tool_registry.describe()
175
+ self.console.print(
176
+ Markdown(tools_description),
177
+ "\n",
178
+ f"[dim]Tool registry: {len(self.agent.tool_registry._tools)} modules active.[/dim]\n",
179
+ f"[dim]Registry description payload: {self.agent.format_bytes(len(tools_description))}[/dim]\n",
180
+ sep="",
181
+ )
182
+
183
+ case "/rollback":
184
+ self.console.print(
185
+ "[dim][bold]Rollback executed.[/bold] System reverted to the last stable state.[/dim]\n",
186
+ )
187
+
188
+ case "/trace":
189
+ trace = self.agent.traces["solution"]
190
+ for i, step in enumerate(trace):
191
+ self.console.print(f"[bold yellow]Trace {i+1}:[/bold yellow]")
192
+ print(f"{step}\n")
193
+
194
+ case "/history":
195
+ for i, chat in enumerate(self.agent.agent_history[1:]):
196
+ self.console.print(f"[bold yellow]Chat {i+1}:[/bold yellow]")
197
+ print(f"{chat}\n")
198
+ self.console.print(
199
+ f"[dim]Session history payload: {self.agent.format_bytes(len(str(self.agent.agent_history)))}[/dim]\n",
200
+ )
201
+
202
+ # case "/vhistory":
203
+ # for i, chat in enumerate(self.agent.verificator_history[1:]):
204
+ # self.console.print(f"[bold yellow]Chat {i+1}:[/bold yellow]")
205
+ # print(f"{chat}\n")
206
+
207
+ case "/infocontext":
208
+ bytes = sum(len(str(item)) for item in self.agent.agent_history)
209
+ self.console.print(
210
+ f"[dim][bold]History:[/bold] {len(self.agent.agent_history)} messages[/dim]\n",
211
+ f"[dim][bold]Context size:[/bold] ~{self.agent.format_bytes(bytes)}[/dim]\n",
212
+ sep="",
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
+
224
+ case "/savecontext":
225
+ try:
226
+ self.agent.persist_state()
227
+ self.console.print("[dim]Context state persisted to disk.[/dim]\n")
228
+ except Exception as e:
229
+ self.console.print(
230
+ f"[bold red]Context persistence error:[/bold red] {e}\n"
231
+ )
232
+
233
+ case "/loadcontext":
234
+ try:
235
+ self.agent.restore_state()
236
+ self.console.print(
237
+ "[dim]Context recovered. Session state synchronized.[/dim]\n"
238
+ )
239
+ except Exception as e:
240
+ self.console.print(
241
+ f"[bold red]Context restoration error:[/bold red] {e}\n"
242
+ )
243
+
244
+ self.console.print(
245
+ Markdown("----"),
246
+ end="",
247
+ )
248
+
249
+ def repl(self):
250
+ logger.setLevel(logging.CRITICAL)
251
+ self.show_welcome()
252
+ self.pretty_print(f"----")
253
+
254
+ session = PromptSession(
255
+ erase_when_done=True,
256
+ history=self.prompt_history,
257
+ completer=self.completer,
258
+ mouse_support=True,
259
+ )
260
+
261
+ def as_table(prompt, message="", color="cyan"):
262
+ table = Table.grid(expand=True)
263
+ table.add_column(width=len(prompt) + 1)
264
+ table.add_column()
265
+
266
+ table.add_row(
267
+ Text(prompt, style=f"bold {color}"),
268
+ Markdown(message),
269
+ )
270
+ return table
271
+
272
+ input_prompt = "&gt;&gt;&gt; " # >>>
273
+ input_prompt_history = "USER |"
274
+ output_prompt_history = "DEER |"
275
+
276
+ while True:
277
+ try:
278
+ msg = session.prompt(
279
+ HTML(f"<ansicyan><b>{input_prompt}</b></ansicyan>")
280
+ )
281
+ self.save_prompt_history()
282
+
283
+ if self.agent.load_context:
284
+ self.agent.persist_state()
285
+
286
+ except KeyboardInterrupt:
287
+ continue
288
+ except EOFError:
289
+ break
290
+
291
+ msg = msg.strip()
292
+ if not msg:
293
+ continue
294
+
295
+ match msg:
296
+ case command if command in COMMANDS:
297
+ self.console.print(
298
+ as_table(f"[{command}]", color="cyan"),
299
+ Text("\n"),
300
+ end="",
301
+ )
302
+ self.run_command(command)
303
+
304
+ case command if command.startswith("!"):
305
+ # Remove the '!' from the start to get only the command
306
+ cmd_to_run = command[1:].strip()
307
+
308
+ self.console.print(
309
+ as_table(f"[{cmd_to_run}]", color="cyan"),
310
+ Text("\n"),
311
+ end="",
312
+ )
313
+
314
+ try:
315
+ # Execute the command
316
+ result = subprocess.run(
317
+ cmd_to_run,
318
+ shell=True,
319
+ capture_output=True,
320
+ text=True,
321
+ check=True,
322
+ )
323
+ self.console.print(
324
+ Text(result.stdout),
325
+ Markdown("----"),
326
+ sep="",
327
+ )
328
+
329
+ except subprocess.CalledProcessError as e:
330
+ self.console.print(
331
+ f"[dim]Error executing command: {e.stderr}[/dim]",
332
+ Markdown("----"),
333
+ sep="",
334
+ )
335
+ except Exception as e:
336
+ self.console.print(
337
+ f"[dim]An unexpected error occurred: {e}[/dim]",
338
+ Markdown("----"),
339
+ sep="",
340
+ )
341
+
342
+ case _:
343
+ if msg.startswith("/"):
344
+ self.pretty_print(
345
+ f"Command `{msg}` not recognized. Did you mean one of these: {list(COMMANDS)}"
346
+ )
347
+ continue
348
+ with self.console.status(
349
+ "[dim]processing request[/]",
350
+ spinner="point",
351
+ spinner_style="dim",
352
+ ):
353
+ try:
354
+ self.console.print(
355
+ as_table(input_prompt_history, msg),
356
+ Text("\n"),
357
+ end="",
358
+ )
359
+
360
+ # Call the LLM model
361
+ response = self.agent.run(msg)
362
+
363
+ self.console.print(
364
+ as_table(output_prompt_history, response),
365
+ Text("\n"),
366
+ Markdown("----"),
367
+ end="",
368
+ )
369
+
370
+ except KeyboardInterrupt:
371
+ self.console.print(
372
+ "[dim]Command aborted by user[/dim]\n",
373
+ Markdown("----"),
374
+ sep="",
375
+ )
376
+ continue
377
+ except EOFError:
378
+ break