rlmy 0.1.0__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.
- rlmy/__init__.py +8 -0
- rlmy/agent/__init__.py +14 -0
- rlmy/agent/commands.py +155 -0
- rlmy/agent/main.py +988 -0
- rlmy/agent/rlm.py +1085 -0
- rlmy/agent/sandbox.py +290 -0
- rlmy/agent/trajectory.py +481 -0
- rlmy/cli.py +142 -0
- rlmy/config.py +115 -0
- rlmy/tools/__init__.py +13 -0
- rlmy/tools/edit.py +503 -0
- rlmy/tools/mcp.py +343 -0
- rlmy/tools/shell.py +346 -0
- rlmy/wizard.py +184 -0
- rlmy-0.1.0.dist-info/METADATA +114 -0
- rlmy-0.1.0.dist-info/RECORD +19 -0
- rlmy-0.1.0.dist-info/WHEEL +5 -0
- rlmy-0.1.0.dist-info/entry_points.txt +2 -0
- rlmy-0.1.0.dist-info/top_level.txt +1 -0
rlmy/__init__.py
ADDED
rlmy/agent/__init__.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""
|
|
2
|
+
RLMY Agent — core agent logic, trajectory, and workspace management.
|
|
3
|
+
|
|
4
|
+
Key exports:
|
|
5
|
+
InterruptableRLM — interrupt-safe RLM subclass with prior trajectory injection
|
|
6
|
+
RLMContext — mutable context passed to contextual tools
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# NOTE: Heavy imports (InterruptableRLM, etc.) are deferred to avoid loading
|
|
10
|
+
# the entire agent stack on `import rlmy`. Import directly from submodules:
|
|
11
|
+
# from rlmy.agent.rlm import InterruptableRLM, RLMContext
|
|
12
|
+
# from rlmy.agent.trajectory import save_trajectory, load_trajectory
|
|
13
|
+
# from rlmy.agent.sandbox import SandboxManager
|
|
14
|
+
|
rlmy/agent/commands.py
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Purpose: Slash command registry — decoupled matching, dispatch signaling, help display.
|
|
3
|
+
Usage:
|
|
4
|
+
from rlmy.agent.commands import REGISTRY, SlashCommandSignal
|
|
5
|
+
cmd = REGISTRY.match(user_text) # returns SlashCommand or None
|
|
6
|
+
raise SlashCommandSignal(cmd) # for context-dependent commands
|
|
7
|
+
Key Components:
|
|
8
|
+
SlashCommand — immutable command descriptor (name, aliases, action key, description)
|
|
9
|
+
SlashCommandSignal — exception raised when a context-dependent command is triggered
|
|
10
|
+
CommandRegistry — register, match, help panel
|
|
11
|
+
REGISTRY — module-level singleton with all commands pre-registered
|
|
12
|
+
Conventions:
|
|
13
|
+
Commands return action keys (strings), NOT callbacks.
|
|
14
|
+
Self-contained commands (exit, help) are handled by prompt_user() directly.
|
|
15
|
+
Context-dependent commands (compact) raise SlashCommandSignal for the caller to handle.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from dataclasses import dataclass, field
|
|
19
|
+
from typing import Optional
|
|
20
|
+
|
|
21
|
+
from rich.panel import Panel
|
|
22
|
+
from rich.text import Text
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True)
|
|
26
|
+
class SlashCommand:
|
|
27
|
+
"""
|
|
28
|
+
Purpose: Immutable descriptor for a slash command.
|
|
29
|
+
Attributes:
|
|
30
|
+
name: Primary command string (e.g., "/compact")
|
|
31
|
+
aliases: Alternative triggers (e.g., ["/c"])
|
|
32
|
+
action: Action key dispatched to callers (e.g., "compact"). NOT a callback.
|
|
33
|
+
description: Human-readable description for /help display.
|
|
34
|
+
self_contained: If True, prompt_user() handles it directly (e.g., exit, help).
|
|
35
|
+
If False, SlashCommandSignal is raised for the caller.
|
|
36
|
+
"""
|
|
37
|
+
name: str
|
|
38
|
+
action: str
|
|
39
|
+
description: str
|
|
40
|
+
aliases: list[str] = field(default_factory=list)
|
|
41
|
+
self_contained: bool = False
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class SlashCommandSignal(Exception):
|
|
45
|
+
"""
|
|
46
|
+
Purpose: Raised by prompt_user() when user triggers a context-dependent command.
|
|
47
|
+
|
|
48
|
+
Why an exception: prompt_user() doesn't own the conversation loop or trajectory.
|
|
49
|
+
It can't execute context-dependent commands itself. The exception propagates to
|
|
50
|
+
the caller (contextual_ask_user_guidance or mainmcp's between-turns loop) which
|
|
51
|
+
has the necessary context to dispatch.
|
|
52
|
+
|
|
53
|
+
Attributes:
|
|
54
|
+
command: The matched SlashCommand
|
|
55
|
+
action: Shortcut to command.action for easy dispatch
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
def __init__(self, command: SlashCommand):
|
|
59
|
+
self.command = command
|
|
60
|
+
self.action = command.action
|
|
61
|
+
super().__init__(f"Slash command: {command.name}")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class CommandRegistry:
|
|
65
|
+
"""
|
|
66
|
+
Purpose: Central registry for slash commands. Match user input, render help.
|
|
67
|
+
|
|
68
|
+
Usage Patterns:
|
|
69
|
+
REGISTRY.register(SlashCommand("/foo", "foo", "Do foo"))
|
|
70
|
+
cmd = REGISTRY.match("/foo") # returns SlashCommand
|
|
71
|
+
cmd = REGISTRY.match("hello") # returns None
|
|
72
|
+
panel = REGISTRY.help_panel() # Rich Panel for /help display
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
def __init__(self):
|
|
76
|
+
self._commands: list[SlashCommand] = []
|
|
77
|
+
# Lookup: normalized trigger string → SlashCommand
|
|
78
|
+
self._lookup: dict[str, SlashCommand] = {}
|
|
79
|
+
|
|
80
|
+
def register(self, cmd: SlashCommand) -> None:
|
|
81
|
+
"""Register a command. All triggers (name + aliases) are indexed."""
|
|
82
|
+
self._commands.append(cmd)
|
|
83
|
+
for trigger in [cmd.name] + cmd.aliases:
|
|
84
|
+
key = trigger.strip().lower()
|
|
85
|
+
if key in self._lookup:
|
|
86
|
+
raise ValueError(
|
|
87
|
+
f"Duplicate trigger '{key}': already registered to '{self._lookup[key].name}'"
|
|
88
|
+
)
|
|
89
|
+
self._lookup[key] = cmd
|
|
90
|
+
|
|
91
|
+
def match(self, text: str) -> Optional[SlashCommand]:
|
|
92
|
+
"""
|
|
93
|
+
Match user input against registered commands.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
text: The full user input text (already stripped by prompt_user).
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
SlashCommand if the ENTIRE text matches a trigger, else None.
|
|
100
|
+
Partial matches (e.g., "/comp" for "/compact") do NOT match.
|
|
101
|
+
"""
|
|
102
|
+
return self._lookup.get(text.lower())
|
|
103
|
+
|
|
104
|
+
def help_panel(self) -> Panel:
|
|
105
|
+
"""Build a Rich Panel listing all registered commands."""
|
|
106
|
+
lines = Text()
|
|
107
|
+
for cmd in self._commands:
|
|
108
|
+
triggers = ", ".join([cmd.name] + cmd.aliases)
|
|
109
|
+
lines.append(f" {triggers}", style="bold cyan")
|
|
110
|
+
lines.append(f" — {cmd.description}\n", style="dim")
|
|
111
|
+
return Panel(
|
|
112
|
+
lines,
|
|
113
|
+
title="[bold]Available Commands[/bold]",
|
|
114
|
+
border_style="blue",
|
|
115
|
+
padding=(1, 2),
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
# =============================================================================
|
|
120
|
+
# Module-level singleton — all commands registered here
|
|
121
|
+
# =============================================================================
|
|
122
|
+
|
|
123
|
+
REGISTRY = CommandRegistry()
|
|
124
|
+
|
|
125
|
+
REGISTRY.register(SlashCommand(
|
|
126
|
+
name="/quit",
|
|
127
|
+
action="exit",
|
|
128
|
+
description="Exit the program",
|
|
129
|
+
aliases=["/q"],
|
|
130
|
+
self_contained=True,
|
|
131
|
+
))
|
|
132
|
+
|
|
133
|
+
REGISTRY.register(SlashCommand(
|
|
134
|
+
name="/help",
|
|
135
|
+
action="help",
|
|
136
|
+
description="Show available commands",
|
|
137
|
+
aliases=["/h", "/?"],
|
|
138
|
+
self_contained=True,
|
|
139
|
+
))
|
|
140
|
+
|
|
141
|
+
REGISTRY.register(SlashCommand(
|
|
142
|
+
name="/compact",
|
|
143
|
+
action="compact",
|
|
144
|
+
description="Compact trajectory to free LLM attention (irreversible)",
|
|
145
|
+
aliases=["/c"],
|
|
146
|
+
self_contained=False,
|
|
147
|
+
))
|
|
148
|
+
|
|
149
|
+
REGISTRY.register(SlashCommand(
|
|
150
|
+
name="/reset",
|
|
151
|
+
action="reset",
|
|
152
|
+
description="Clear trajectory and start fresh",
|
|
153
|
+
aliases=[],
|
|
154
|
+
self_contained=False,
|
|
155
|
+
))
|