fix(letta): mirror official Python hook integration

This commit is contained in:
ZacharyZhang-NY committed 2026-07-20 18:48:01 -04:00
1 parent 24a9d64978
commit 53476feca7
19 files changed
+2443 -212

No files matched your search

+289
View File
@@ -0,0 +1,289 @@
from __future__ import annotations
from dataclasses import dataclass
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import json
import ntpath
import os
from pathlib import Path
import subprocess # nosec B404
import sys
from threading import Thread
from typing import Any, Mapping, Optional
import pytest
from elydora.plugins.letta import LettaPlugin
from elydora.utils import base64url_encode
AGENT_ID = "agent-1"
MISSING = object()
VALID_PRIVATE_KEY = base64url_encode(bytes(range(32)))
@dataclass
class LettaFixture:
root_dir: Path
home_dir: Path
project_dir: Path
global_path: Path
project_path: Path
local_path: Path
agent_dir: Path
guard_path: Path
audit_path: Path
runtime_config_path: Path
private_key_path: Path
plugin: LettaPlugin
config: dict[str, str]
def install(self) -> None:
self.plugin.install(self.config)
def source(self, file_path: Optional[Path] = None) -> str:
path = file_path or self.global_path
with path.open("r", encoding="utf-8", newline="") as file:
return file.read()
def settings(self, file_path: Optional[Path] = None) -> dict[str, Any]:
return json.loads(self.source(file_path))
class ElydoraApiHandler(BaseHTTPRequestHandler):
agent_status: Any = "active"
operation_status = 201
requests: list[dict[str, Any]] = []
@classmethod
def reset(cls, status: Any, operation_status: int) -> None:
cls.agent_status = status
cls.operation_status = operation_status
cls.requests = []
def _respond(self, status: int, value: object) -> None:
body = json.dumps(value).encode()
self.send_response(status)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def do_GET(self) -> None:
type(self).requests.append({
"method": "GET",
"path": self.path,
"authorization": self.headers.get("Authorization"),
})
self._respond(200, {"agent": {"status": type(self).agent_status}})
def do_POST(self) -> None:
length = int(self.headers.get("Content-Length", "0"))
raw = self.rfile.read(length)
type(self).requests.append({
"method": "POST",
"path": self.path,
"authorization": self.headers.get("Authorization"),
"raw": raw,
"json": json.loads(raw),
})
self._respond(
type(self).operation_status,
{"operation": {"accepted": True}},
)
def log_message(self, _format: str, *_args: object) -> None:
return
def start_api_server(
*, status: Any = "active", operation_status: int = 201
) -> ThreadingHTTPServer:
ElydoraApiHandler.reset(status, operation_status)
server = ThreadingHTTPServer(("127.0.0.1", 0), ElydoraApiHandler)
Thread(target=server.serve_forever, daemon=True).start()
return server
def server_base_url(server: ThreadingHTTPServer) -> str:
return f"http://127.0.0.1:{server.server_port}"
def write_text(path: Path, source: str, mode: int = 0o600) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(source, encoding="utf-8", newline="")
os.chmod(path, mode)
def write_json(path: Path, value: object, mode: int = 0o600) -> None:
source = value if isinstance(value, str) else json.dumps(value, indent=2) + "\n"
write_text(path, source, mode)
def prepare_fixture(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
*,
global_settings: object = MISSING,
project_settings: object = MISSING,
local_settings: object = MISSING,
base_url: str = "http://127.0.0.1:9",
) -> LettaFixture:
home_dir = tmp_path / "home with spaces and 'quote $HOME %HOME%"
project_dir = tmp_path / "project with spaces"
project_dir.mkdir(parents=True)
monkeypatch.setenv("HOME", str(home_dir))
monkeypatch.setenv("USERPROFILE", str(home_dir))
monkeypatch.chdir(project_dir)
global_path = home_dir / ".letta" / "settings.json"
project_path = project_dir / ".letta" / "settings.json"
local_path = project_dir / ".letta" / "settings.local.json"
agent_dir = home_dir / ".elydora" / AGENT_ID
guard_path = agent_dir / "guard.py"
audit_path = agent_dir / "hook.py"
runtime_config_path = agent_dir / "config.json"
private_key_path = agent_dir / "private.key"
for path, value in (
(global_path, global_settings),
(project_path, project_settings),
(local_path, local_settings),
):
if value is not MISSING:
write_json(path, value)
return LettaFixture(
root_dir=tmp_path,
home_dir=home_dir,
project_dir=project_dir,
global_path=global_path,
project_path=project_path,
local_path=local_path,
agent_dir=agent_dir,
guard_path=guard_path,
audit_path=audit_path,
runtime_config_path=runtime_config_path,
private_key_path=private_key_path,
plugin=LettaPlugin(),
config={
"org_id": "org-1",
"agent_id": AGENT_ID,
"agent_name": "letta",
"private_key": VALID_PRIVATE_KEY,
"kid": "kid-1",
"token": "token-1",
"base_url": base_url,
"guard_script_path": str(guard_path),
},
)
def managed_handler(
settings: Mapping[str, Any], event: str
) -> dict[str, Any]:
for group in settings.get("hooks", {}).get(event, []):
if set(group) != {"matcher", "hooks"} or group.get("matcher") != "*":
continue
for handler in group["hooks"]:
if (
set(handler) == {"type", "command", "timeout"}
and handler.get("type") == "command"
and handler.get("timeout") == 10_000
):
return handler
raise AssertionError(f"managed handler for {event!r} not found")
def assert_managed_triple(settings: Mapping[str, Any]) -> dict[str, Any]:
guard = managed_handler(settings, "PreToolUse")
audit = managed_handler(settings, "PostToolUse")
failure = managed_handler(settings, "PostToolUseFailure")
assert audit["command"] == failure["command"]
return {"guard": guard, "audit": audit, "failure": failure}
def legacy_group(script_path: Path, event: str) -> dict[str, Any]:
command = (
f'"{sys.executable}" {script_path}'
if event == "PreToolUse"
else str(script_path)
)
return {
"matcher": "*",
"hooks": [{"type": "command", "command": command}],
}
def run_handler(
handler: Mapping[str, Any], payload: bytes, fixture: LettaFixture
) -> subprocess.CompletedProcess[bytes]:
environment = {
**os.environ,
"HOME": str(fixture.home_dir),
"USERPROFILE": str(fixture.home_dir),
}
if os.name == "nt":
powershell = ntpath.join(
os.environ.get("SystemRoot", r"C:\Windows"),
"System32",
"WindowsPowerShell",
"v1.0",
"powershell.exe",
)
command = [
powershell,
"-NoLogo",
"-NoProfile",
"-NonInteractive",
"-Command",
str(handler["command"]),
]
else:
command = ["/bin/bash", "-c", str(handler["command"])]
return subprocess.run( # nosec B603
command,
input=payload,
capture_output=True,
check=False,
cwd=fixture.project_dir,
env=environment,
timeout=15,
)
def official_input(fixture: LettaFixture, event: str) -> dict[str, Any]:
payload: dict[str, Any] = {
"event_type": event,
"working_directory": str(fixture.project_dir),
"session_id": "session-1",
"tool_name": "Bash",
"tool_input": {"command": "echo test"},
"tool_call_id": "call-1",
"agent_id": "letta-agent-1",
}
if event == "PostToolUse":
payload["tool_result"] = {"status": "success", "output": "test"}
if event == "PostToolUseFailure":
payload.update({
"error_message": "Command failed",
"error_type": "ProcessError",
})
return payload
def run_cli(
fixture: LettaFixture, arguments: list[str]
) -> subprocess.CompletedProcess[str]:
environment = dict(os.environ)
repository = str(Path(__file__).resolve().parents[1])
existing = environment.get("PYTHONPATH")
environment["PYTHONPATH"] = (
repository + os.pathsep + existing if existing else repository
)
return subprocess.run( # nosec B603
[sys.executable, "-m", "elydora.cli", *arguments],
text=True,
capture_output=True,
check=False,
cwd=fixture.project_dir,
env=environment,
timeout=30,
)
-2
View File
@@ -18,7 +18,6 @@ from elydora.plugins import (
copilot,
cursor,
kiroide,
letta,
opencode,
)
from elydora.plugins.hook_template import generate_hook_script
@@ -375,7 +374,6 @@ def test_atomic_secret_write_preserves_existing_file_on_replace_failure(
(copilot, copilot.CopilotPlugin, "copilot"),
(cursor, cursor.CursorPlugin, "cursor"),
(kiroide, kiroide.KiroIdePlugin, "kiroide"),
(letta, letta.LettaPlugin, "letta"),
(opencode, opencode.OpenCodePlugin, "opencode"),
],
)
+180
View File
@@ -0,0 +1,180 @@
from __future__ import annotations
import json
import os
from pathlib import Path
import shutil
import subprocess # nosec B404
import pytest
from letta_support import (
ElydoraApiHandler,
managed_handler,
prepare_fixture,
server_base_url,
start_api_server,
)
SOURCE_ROOT = os.environ.get("ELYDORA_LETTA_SOURCE")
BUN_BINARY = os.environ.get("ELYDORA_BUN_BINARY") or shutil.which("bun")
def _module_url(relative_path: str) -> str:
if SOURCE_ROOT is None:
raise RuntimeError("ELYDORA_LETTA_SOURCE is required")
return (Path(SOURCE_ROOT) / relative_path).resolve().as_uri()
def _result_json(stdout: str) -> dict:
return json.loads(stdout.strip().splitlines()[-1])
def _run_official_probe(fixture: object, mode: str) -> subprocess.CompletedProcess[str]:
if SOURCE_ROOT is None or BUN_BINARY is None:
raise RuntimeError("official Letta source and Bun are required")
global_path = getattr(fixture, "global_path")
settings = json.loads(Path(global_path).read_text(encoding="utf-8"))
commands = {
"guard": managed_handler(settings, "PreToolUse")["command"],
"audit": managed_handler(settings, "PostToolUse")["command"],
"failure": managed_handler(settings, "PostToolUseFailure")["command"],
}
source = f"""
import {{ settingsManager }} from {json.dumps(_module_url('src/settings-manager.ts'))};
import {{ getHooksForEvent }} from {json.dumps(_module_url('src/hooks/loader.ts'))};
import {{ executeCommandHook }} from {json.dumps(_module_url('src/hooks/executor.ts'))};
import {{ runPostToolUseHooks }} from {json.dumps(_module_url('src/hooks/index.ts'))};
const workspace = process.env.ELYDORA_OFFICIAL_WORKSPACE;
const commands = JSON.parse(process.env.ELYDORA_OFFICIAL_COMMANDS);
await settingsManager.initialize();
await settingsManager.loadProjectSettings(workspace);
await settingsManager.loadLocalProjectSettings(workspace);
const pre = await getHooksForEvent('PreToolUse', 'Bash', workspace);
const post = await getHooksForEvent('PostToolUse', 'Bash', workspace);
const failure = await getHooksForEvent('PostToolUseFailure', 'Bash', workspace);
const managed = (hooks, command) => hooks.find((hook) => (
hook.type === 'command' && hook.command === command
));
if (process.env.ELYDORA_OFFICIAL_MODE === 'active') {{
const guardResult = await executeCommandHook(
managed(pre, commands.guard),
{{
event_type: 'PreToolUse',
working_directory: workspace,
tool_name: 'Bash',
tool_input: {{ command: 'echo official' }},
tool_call_id: 'official-pre',
agent_id: 'letta-agent',
}},
workspace,
);
const postResult = await executeCommandHook(
managed(post, commands.audit),
{{
event_type: 'PostToolUse',
working_directory: workspace,
tool_name: 'Bash',
tool_input: {{ command: 'echo official' }},
tool_result: {{ status: 'success', output: 'official' }},
tool_call_id: 'official-post',
}},
workspace,
);
const failureResult = await executeCommandHook(
managed(failure, commands.failure),
{{
event_type: 'PostToolUseFailure',
working_directory: workspace,
tool_name: 'Bash',
tool_input: {{ command: 'exit 1' }},
error_message: 'official failure',
error_type: 'ProcessError',
tool_call_id: 'official-failure',
}},
workspace,
);
console.log(JSON.stringify({{
counts: [pre.length, post.length, failure.length],
exits: [guardResult.exitCode, postResult.exitCode, failureResult.exitCode],
}}));
}} else {{
const result = await runPostToolUseHooks(
'Bash',
{{ command: 'echo unavailable' }},
{{ status: 'success', output: 'unavailable' }},
'official-post-error',
workspace,
);
console.log(JSON.stringify({{
blocked: result.blocked,
errored: result.errored,
exits: result.results.map((entry) => entry.exitCode),
}}));
}}
await settingsManager.flush();
"""
environment = {
**os.environ,
"HOME": str(getattr(fixture, "home_dir")),
"USERPROFILE": str(getattr(fixture, "home_dir")),
"LETTA_SKIP_KEYCHAIN_CHECK": "1",
"ELYDORA_OFFICIAL_COMMANDS": json.dumps(commands),
"ELYDORA_OFFICIAL_MODE": mode,
"ELYDORA_OFFICIAL_WORKSPACE": str(getattr(fixture, "project_dir")),
}
return subprocess.run( # nosec B603
[BUN_BINARY, "--silent", "--eval", source],
text=True,
capture_output=True,
check=False,
cwd=SOURCE_ROOT,
env=environment,
timeout=60,
)
@pytest.mark.skipif(
SOURCE_ROOT is None or BUN_BINARY is None,
reason="official Letta source and Bun are required",
)
def test_official_letta_code_loads_and_executes_all_managed_hooks(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
assert SOURCE_ROOT is not None
manifest = json.loads(
(Path(SOURCE_ROOT) / "package.json").read_text(encoding="utf-8")
)
assert manifest["version"] == "0.28.13"
server = start_api_server()
fixture = prepare_fixture(
monkeypatch, tmp_path, base_url=server_base_url(server)
)
server_closed = False
try:
fixture.install()
active = _run_official_probe(fixture, "active")
assert active.returncode == 0, active.stderr
assert _result_json(active.stdout) == {
"counts": [1, 1, 1],
"exits": [0, 0, 0],
}
assert len([
request for request in ElydoraApiHandler.requests
if request["method"] == "POST"
]) == 2
server.shutdown()
server.server_close()
server_closed = True
unavailable = _run_official_probe(fixture, "unavailable")
assert unavailable.returncode == 0, unavailable.stderr
assert _result_json(unavailable.stdout) == {
"blocked": False,
"errored": True,
"exits": [1],
}
finally:
if not server_closed:
server.shutdown()
server.server_close()
+395
View File
@@ -0,0 +1,395 @@
from __future__ import annotations
import json
import os
from pathlib import Path
from typing import Any
import pytest
from elydora import cli
from elydora.plugins import letta
from elydora.plugins.registry import SUPPORTED_AGENTS
from letta_support import (
AGENT_ID,
VALID_PRIVATE_KEY,
assert_managed_triple,
legacy_group,
prepare_fixture,
run_cli,
write_json,
write_text,
)
def test_letta_is_registered_in_the_sdk_and_cli() -> None:
assert SUPPORTED_AGENTS["letta"] == {
"name": "Letta Code",
"hook_event": "PreToolUse/PostToolUse/PostToolUseFailure",
"config_path": "~/.letta/settings.json",
}
assert cli.PLUGIN_MAP["letta"] is letta.LettaPlugin
def test_install_preserves_every_source_and_is_idempotent(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
capsys: pytest.CaptureFixture[str],
) -> None:
global_source = (
"{\r\n"
' "theme": "dark",\r\n'
' "hooks": {\r\n'
' "FutureEvent": [null],\r\n'
' "PreToolUse": [{ "matcher": "Bash", "hooks": '
'[{ "type": "command", "command": "user-hook", "quiet": true }] }]\r\n'
" }\r\n"
"}\r\n"
)
fixture = prepare_fixture(
monkeypatch,
tmp_path,
global_settings=global_source,
project_settings={"owner": "project"},
local_settings={"owner": "local"},
)
project_before = fixture.source(fixture.project_path)
local_before = fixture.source(fixture.local_path)
fixture.install()
first_source = fixture.source()
settings = fixture.settings()
assert "\r\n" in first_source
assert settings["theme"] == "dark"
assert settings["hooks"]["FutureEvent"] == [None]
assert settings["hooks"]["PreToolUse"][0]["hooks"][0]["command"] == "user-hook"
assert_managed_triple(settings)
assert fixture.source(fixture.project_path) == project_before
assert fixture.source(fixture.local_path) == local_before
for file_path in (
fixture.guard_path,
fixture.audit_path,
fixture.runtime_config_path,
fixture.private_key_path,
):
assert file_path.is_file()
fixture.install()
assert fixture.source() == first_source
output = capsys.readouterr().out
assert "run /hooks" in output
assert "restart active sessions" in output
def test_letta_validates_known_schemas_and_preserves_future_events(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
fixture = prepare_fixture(monkeypatch, tmp_path, global_settings={
"hooks": {
"FutureEvent": [None],
"PreToolUse": [{
"matcher": "[",
"hooks": [{
"type": "command",
"command": "user-command",
"timeout": 0,
"quiet": False,
"futureField": True,
}],
}],
"Stop": [{
"hooks": [{
"type": "prompt",
"prompt": "Evaluate $ARGUMENTS",
"model": "fast",
"timeout": 30,
}],
}],
},
})
fixture.install()
settings = fixture.settings()
assert settings["hooks"]["FutureEvent"] == [None]
assert settings["hooks"]["PreToolUse"][0]["matcher"] == "["
assert settings["hooks"]["PreToolUse"][0]["hooks"][0]["futureField"] is True
assert settings["hooks"]["Stop"][0]["hooks"][0]["type"] == "prompt"
@pytest.mark.parametrize(
"source",
[
"{ malformed",
"[]",
'{ "theme": true, }',
'{ "theme": true // comment\n }',
'{ "hooks": {}, "hooks": {} }',
'{ "hooks": null }',
'{ "hooks": [] }',
'{ "hooks": { "disabled": "yes" } }',
'{ "hooks": { "PreToolUse": null } }',
'{ "hooks": { "PreToolUse": [null] } }',
'{ "hooks": { "PreToolUse": [{ "hooks": [] }] } }',
'{ "hooks": { "Stop": [{ "matcher": "*", "hooks": [] }] } }',
'{ "hooks": { "PreToolUse": [{ "matcher": "*" }] } }',
'{ "hooks": { "Stop": [{ "hooks": [null] }] } }',
'{ "hooks": { "Stop": [{ "hooks": [{ "type": "http" }] }] } }',
'{ "hooks": { "PreToolUse": [{ "matcher": "*", "hooks": [{ "type": "command" }] }] } }',
'{ "hooks": { "Stop": [{ "hooks": [{ "type": "prompt" }] }] } }',
'{ "hooks": { "Stop": [{ "hooks": [{ "type": "prompt", "prompt": "x", "model": 1 }] }] } }',
'{ "hooks": { "Stop": [{ "hooks": [{ "type": "command", "command": "x", "quiet": 1 }] }] } }',
'{ "hooks": { "Stop": [{ "hooks": [{ "type": "command", "command": "x", "timeout": -1 }] }] } }',
],
)
def test_install_rejects_malformed_global_settings_before_every_write(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path, source: str
) -> None:
fixture = prepare_fixture(
monkeypatch, tmp_path, global_settings=source
)
with pytest.raises((OSError, ValueError), match="Letta Code"):
fixture.install()
assert fixture.source() == source
assert not fixture.agent_dir.exists()
@pytest.mark.parametrize("kind", ["project", "local"])
def test_install_surfaces_malformed_read_only_sources(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path, kind: str
) -> None:
options = (
{"project_settings": "{ malformed"}
if kind == "project"
else {"local_settings": "{ malformed"}
)
fixture = prepare_fixture(monkeypatch, tmp_path, **options)
label = "project-local" if kind == "local" else kind
with pytest.raises(ValueError, match=f"Letta Code {label}"):
fixture.install()
assert not fixture.global_path.exists()
assert not fixture.agent_dir.exists()
@pytest.mark.parametrize(
("global_settings", "project_settings", "local_settings", "enabled"),
[
({"hooks": {"disabled": True}}, {}, {}, False),
({}, {"hooks": {"disabled": True}}, {}, False),
({}, {}, {"hooks": {"disabled": True}}, False),
(
{"hooks": {"disabled": False}},
{"hooks": {"disabled": True}},
{"hooks": {"disabled": True}},
True,
),
],
)
def test_hooks_disabled_uses_official_precedence(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
global_settings: object,
project_settings: object,
local_settings: object,
enabled: bool,
) -> None:
fixture = prepare_fixture(
monkeypatch,
tmp_path,
global_settings=global_settings,
project_settings=project_settings,
local_settings=local_settings,
)
if enabled:
fixture.install()
assert_managed_triple(fixture.settings())
else:
with pytest.raises(ValueError, match="hooks.disabled"):
fixture.install()
assert not fixture.agent_dir.exists()
def test_install_migrates_exact_legacy_and_preserves_lookalikes(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
fixture = prepare_fixture(monkeypatch, tmp_path)
guard_legacy = legacy_group(fixture.guard_path, "PreToolUse")
guard_lookalike = legacy_group(fixture.guard_path, "PreToolUse")
guard_lookalike["hooks"][0]["quiet"] = True
audit_legacy = legacy_group(fixture.audit_path, "PostToolUse")
write_json(fixture.global_path, {
"owner": "user",
"hooks": {
"PreToolUse": [guard_legacy, guard_lookalike],
"PostToolUse": [audit_legacy],
},
})
fixture.install()
settings = fixture.settings()
assert_managed_triple(settings)
assert any(
group["hooks"][0].get("quiet") is True
for group in settings["hooks"]["PreToolUse"]
)
managed = next(
group for group in settings["hooks"]["PreToolUse"]
if group["hooks"][0].get("timeout") == 10_000
)
managed["userField"] = "preserve-group"
managed["hooks"].append({"type": "command", "command": "user-command"})
write_json(fixture.global_path, settings)
fixture.plugin.uninstall(AGENT_ID)
remaining = fixture.settings()
assert remaining["owner"] == "user"
assert any(
group["hooks"][0].get("quiet") is True
for group in remaining["hooks"]["PreToolUse"]
)
assert any(
handler.get("command") == "user-command"
for group in remaining["hooks"]["PreToolUse"]
for handler in group["hooks"]
)
assert "PostToolUse" not in remaining["hooks"]
assert "PostToolUseFailure" not in remaining["hooks"]
def test_uninstall_removes_owned_file_and_preserves_user_settings(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
user = prepare_fixture(
monkeypatch,
tmp_path / "user",
global_settings={"theme": "dark", "hooks": {"Notification": []}},
)
user.install()
user.plugin.uninstall(AGENT_ID)
assert user.settings() == {
"theme": "dark",
"hooks": {"Notification": []},
}
owned = prepare_fixture(monkeypatch, tmp_path / "owned")
owned.install()
owned.plugin.uninstall(AGENT_ID)
assert not owned.global_path.exists()
def test_status_requires_effective_contract_and_strict_runtime_identity(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
fixture = prepare_fixture(monkeypatch, tmp_path, project_settings={})
fixture.install()
assert fixture.plugin.status()["installed"] is True
write_json(fixture.project_path, {"hooks": {"disabled": True}})
assert fixture.plugin.status()["installed"] is False
write_json(fixture.project_path, {})
settings = fixture.settings()
settings["hooks"]["PostToolUseFailure"].append(
settings["hooks"]["PostToolUseFailure"][-1]
)
write_json(fixture.global_path, settings)
assert fixture.plugin.status()["installed"] is False
fixture.install()
write_text(fixture.private_key_path, "invalid")
with pytest.raises(ValueError, match="private key"):
fixture.plugin.status()
fixture.install()
write_text(fixture.audit_path, "tampered")
assert fixture.plugin.status()["installed"] is False
fixture.install()
runtime = json.loads(fixture.runtime_config_path.read_text())
runtime["agent_name"] = "other"
write_json(fixture.runtime_config_path, runtime)
with pytest.raises(ValueError, match="runtime identity"):
fixture.plugin.status()
def test_preflight_rejects_unverifiable_runtime_and_invalid_agent_id(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
identity = prepare_fixture(monkeypatch, tmp_path / "identity")
write_text(identity.audit_path, "unverified")
with pytest.raises(ValueError, match="without config.json"):
identity.install()
assert not identity.global_path.exists()
invalid = prepare_fixture(monkeypatch, tmp_path / "invalid")
invalid.config["agent_id"] = "../escape"
with pytest.raises(ValueError, match="Invalid agent ID"):
invalid.install()
assert not invalid.global_path.exists()
def test_letta_cli_install_status_and_uninstall_end_to_end(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
fixture = prepare_fixture(
monkeypatch, tmp_path, global_settings={"theme": "dark"}
)
key_file = tmp_path / "install-private.key"
token_file = tmp_path / "install-token.txt"
write_text(key_file, VALID_PRIVATE_KEY + "\n")
write_text(token_file, "token-1\n")
install = run_cli(fixture, [
"install",
"--agent", "letta",
"--org_id", "org-1",
"--agent_id", AGENT_ID,
"--kid", "kid-1",
"--private_key_file", str(key_file),
"--token_file", str(token_file),
"--base_url", "http://127.0.0.1:9",
])
assert install.returncode == 0, install.stderr
assert_managed_triple(fixture.settings())
status = run_cli(fixture, ["status"])
assert status.returncode == 0, status.stderr
assert "Letta Code" in status.stdout and "[installed]" in status.stdout
uninstall = run_cli(fixture, [
"uninstall", "--agent", "letta", "--agent_id", AGENT_ID
])
assert uninstall.returncode == 0, uninstall.stderr
assert fixture.settings() == {"theme": "dark"}
assert not fixture.agent_dir.exists()
@pytest.mark.skipif(
os.name != "nt", reason="Windows resolves HOME and USERPROFILE separately"
)
def test_windows_cli_separates_letta_home_from_runtime_home(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
fixture = prepare_fixture(
monkeypatch, tmp_path, global_settings={"theme": "dark"}
)
runtime_home = tmp_path / "windows profile"
runtime_agent_dir = runtime_home / ".elydora" / AGENT_ID
monkeypatch.setenv("USERPROFILE", str(runtime_home))
key_file = tmp_path / "install-private.key"
write_text(key_file, VALID_PRIVATE_KEY + "\n")
install = run_cli(fixture, [
"install",
"--agent", "letta",
"--org_id", "org-1",
"--agent_id", AGENT_ID,
"--kid", "kid-1",
"--private_key_file", str(key_file),
"--base_url", "http://127.0.0.1:9",
])
assert install.returncode == 0, install.stderr
assert_managed_triple(fixture.settings())
assert (runtime_agent_dir / "guard.py").is_file()
assert (runtime_agent_dir / "hook.py").is_file()
status = run_cli(fixture, ["status"])
assert status.returncode == 0, status.stderr
assert "Letta Code" in status.stdout and "[installed]" in status.stdout
uninstall = run_cli(fixture, [
"uninstall", "--agent", "letta", "--agent_id", AGENT_ID
])
assert uninstall.returncode == 0, uninstall.stderr
assert fixture.settings() == {"theme": "dark"}
assert not runtime_agent_dir.exists()
+147
View File
@@ -0,0 +1,147 @@
from __future__ import annotations
import json
import os
from pathlib import Path
import pytest
from letta_support import (
ElydoraApiHandler,
assert_managed_triple,
official_input,
prepare_fixture,
run_handler,
server_base_url,
start_api_server,
)
def _payload(value: object) -> bytes:
return json.dumps(value).encode()
def test_guard_accepts_active_agents_with_native_exit_semantics(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
server = start_api_server(status="active")
fixture = prepare_fixture(
monkeypatch, tmp_path, base_url=server_base_url(server)
)
try:
fixture.install()
guard = assert_managed_triple(fixture.settings())["guard"]
result = run_handler(
guard,
_payload(official_input(fixture, "PreToolUse")),
fixture,
)
assert result.returncode == 0, result.stderr
assert result.stdout == b""
assert len([
request for request in ElydoraApiHandler.requests
if request["method"] == "GET"
]) == 1
finally:
server.shutdown()
server.server_close()
@pytest.mark.parametrize("status", ["frozen", "revoked"])
def test_guard_propagates_blocking_states_through_exit_code_two(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path, status: str
) -> None:
server = start_api_server(status=status)
fixture = prepare_fixture(
monkeypatch, tmp_path, base_url=server_base_url(server)
)
try:
fixture.install()
guard = assert_managed_triple(fixture.settings())["guard"]
result = run_handler(
guard,
_payload(official_input(fixture, "PreToolUse")),
fixture,
)
assert result.returncode == 2, result.stderr
assert result.stdout == b""
assert status.encode() in result.stderr.lower()
finally:
server.shutdown()
server.server_close()
def test_audit_forwards_complete_success_and_failure_payloads(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
server = start_api_server()
fixture = prepare_fixture(
monkeypatch, tmp_path, base_url=server_base_url(server)
)
try:
fixture.install()
handlers = assert_managed_triple(fixture.settings())
for handler_name, event in (
("audit", "PostToolUse"),
("failure", "PostToolUseFailure"),
):
payload = {
**official_input(fixture, event),
"future_provider_field": {"preserved": True},
}
result = run_handler(
handlers[handler_name], _payload(payload), fixture
)
assert result.returncode == 0, result.stderr
assert result.stdout == b""
posts = [
request for request in ElydoraApiHandler.requests
if request["method"] == "POST"
]
assert len(posts) == 2
assert posts[0]["json"]["payload"] == {
**official_input(fixture, "PostToolUse"),
"future_provider_field": {"preserved": True},
}
assert posts[1]["json"]["payload"] == {
**official_input(fixture, "PostToolUseFailure"),
"future_provider_field": {"preserved": True},
}
finally:
server.shutdown()
server.server_close()
def test_audit_failure_is_observable_and_fails_closed(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
fixture = prepare_fixture(monkeypatch, tmp_path)
fixture.install()
audit = assert_managed_triple(fixture.settings())["audit"]
result = run_handler(
audit,
_payload(official_input(fixture, "PostToolUse")),
fixture,
)
assert result.returncode == 1
assert b"Elydora audit" in result.stderr
error_log = fixture.agent_dir / "error.log"
source = error_log.read_text(encoding="utf-8")
assert "[elydora-hook]" in source
assert "URLError" in source
@pytest.mark.skipif(os.name == "nt", reason="POSIX mode bits apply on Unix")
def test_runtime_artifacts_use_private_modes(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
fixture = prepare_fixture(monkeypatch, tmp_path)
fixture.install()
for file_path, expected in (
(fixture.runtime_config_path, 0o600),
(fixture.private_key_path, 0o600),
(fixture.guard_path, 0o700),
(fixture.audit_path, 0o700),
(fixture.global_path, 0o600),
):
assert file_path.stat().st_mode & 0o777 == expected
+111
View File
@@ -0,0 +1,111 @@
from __future__ import annotations
import json
import os
from pathlib import Path
from typing import Any
import pytest
from elydora.plugins import _transaction
from elydora.plugins.letta_config import render_letta_document
from elydora.plugins.letta_contract import build_letta_group
from elydora.plugins.letta_installation import (
commit_letta_installation,
preflight_letta_installation,
prepare_letta_installation,
)
from elydora.plugins.letta_sources import read_letta_sources
from letta_support import prepare_fixture, write_json
def _groups(guard_path: str, audit_path: str) -> dict:
return {
"PreToolUse": build_letta_group(guard_path),
"PostToolUse": build_letta_group(audit_path),
"PostToolUseFailure": build_letta_group(audit_path),
}
def test_transaction_aborts_when_read_only_source_changes(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
fixture = prepare_fixture(
monkeypatch, tmp_path, project_settings={"owner": "before"}
)
sources = read_letta_sources()
paths = preflight_letta_installation(fixture.config, sources)
rendered = render_letta_document(
sources.global_settings,
None,
_groups(paths.guard_path, paths.audit_path),
)
changes = prepare_letta_installation(fixture.config, paths, rendered)
write_json(fixture.project_path, {"owner": "after"})
with pytest.raises(OSError, match="project settings changed"):
commit_letta_installation(changes, sources)
assert json.loads(fixture.project_path.read_text()) == {"owner": "after"}
assert not fixture.global_path.exists()
assert not fixture.agent_dir.exists()
def test_transaction_rolls_back_every_runtime_and_settings_change(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
fixture = prepare_fixture(
monkeypatch, tmp_path, global_settings={"owner": "user"}
)
original = fixture.source()
real_replace = _transaction.os.replace
failed = False
def fail_settings_commit(source: Any, destination: Any) -> None:
nonlocal failed
if not failed and Path(destination) == fixture.global_path:
failed = True
raise OSError("simulated Letta settings commit failure")
real_replace(source, destination)
monkeypatch.setattr(_transaction.os, "replace", fail_settings_commit)
with pytest.raises(OSError, match="Install Letta Code hooks"):
fixture.install()
assert failed
assert fixture.source() == original
for file_path in (
fixture.guard_path,
fixture.audit_path,
fixture.runtime_config_path,
fixture.private_key_path,
):
assert not file_path.exists()
assert [
path
for path in fixture.home_dir.rglob("*")
if path.suffix in {".tmp", ".rollback"}
] == []
def test_install_rejects_linked_configuration_directory(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
fixture = prepare_fixture(monkeypatch, tmp_path)
redirected = tmp_path / "redirected-letta"
redirected.mkdir()
fixture.home_dir.mkdir(parents=True)
try:
os.symlink(redirected, fixture.global_path.parent, target_is_directory=True)
except (NotImplementedError, OSError):
pytest.skip("directory links require platform privileges")
with pytest.raises(OSError, match="global configuration directory"):
fixture.install()
assert not fixture.agent_dir.exists()
def test_install_rejects_directory_at_settings_path(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
fixture = prepare_fixture(monkeypatch, tmp_path)
fixture.global_path.mkdir(parents=True)
with pytest.raises(OSError, match="global settings path"):
fixture.install()
assert not fixture.agent_dir.exists()
@@ -1,15 +1,39 @@
from __future__ import annotations
from elydora.plugins.letta import _is_elydora_hook as is_letta_hook
from pathlib import Path
import sys
import pytest
from elydora.plugins.letta_command import build_letta_command
from elydora.plugins.letta_contract import managed_letta_removals
def test_hook_detection_handles_only_string_commands() -> None:
assert is_letta_hook({"command": 42}, "") is False
assert is_letta_hook({"hooks": [{"command": None}]}, "") is False
assert is_letta_hook(
{"hooks": [{"command": "run .elydora/agent-1/hook.py"}]}, ""
) is True
assert is_letta_hook(
{"hooks": [{"command": "run .elydora/agent-1/hook.py"}]},
"agent-2",
) is False
def test_letta_hook_detection_requires_exact_owned_commands(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
home = tmp_path / "home"
guard = home / ".elydora" / "agent-1" / "guard.py"
monkeypatch.setenv("HOME", str(home))
monkeypatch.setenv("USERPROFILE", str(home))
hooks = {
"PreToolUse": [{
"matcher": "*",
"hooks": [{
"type": "command",
"command": build_letta_command(str(guard)),
"timeout": 10_000,
}],
}],
}
assert len(managed_letta_removals(hooks)) == 1
hooks["PreToolUse"][0]["hooks"][0]["quiet"] = True
assert managed_letta_removals(hooks) == []
hooks["PreToolUse"][0]["hooks"][0] = {
"type": "command",
"command": f'"{sys.executable}" {guard}',
}
assert len(managed_letta_removals(hooks, "agent-1")) == 1
assert managed_letta_removals(hooks, "agent-2") == []