from __future__ import annotations import json from pathlib import Path from scripts import leo_behavior_manifest as manifest ROOT = Path(__file__).resolve().parents[1] def _write(path: Path, value: str) -> None: path.parent.mkdir(parents=True, exist_ok=True) path.write_text(value, encoding="utf-8") def test_behavior_manifest_is_deterministic_and_never_emits_config_secrets(tmp_path: Path) -> None: profile = tmp_path / "profile" hermes = tmp_path / "hermes" _write( profile / "config.yaml", """model: provider: openrouter default: anthropic/claude-sonnet-4-6 smart_routing: true smart_routing_model: google/gemini-2.5-flash memory: enabled: true search: sqlite_fts5 gateway: telegram: bot_token: super-secret-token """, ) _write(profile / "SOUL.md", "# Leo\n") _write(profile / "skills" / "teleo-kb-bridge" / "SKILL.md", "# Bridge\n") _write(profile / "plugins" / "leo-db-context" / "__init__.py", "# plugin\n") _write(profile / "bin" / "kb_tool.py", "# tool\n") _write(profile / "memories" / "USER.md", "private participant fact\n") _write(profile / "sessions" / "one.jsonl", '{"private":"conversation"}\n') _write(profile / "state.db", "runtime-state") _write(hermes / "run_agent.py", "# runtime\n") _write(hermes / "agent" / "prompt_builder.py", "# prompts\n") first = manifest.build_manifest(profile, hermes) second = manifest.build_manifest(profile, hermes) assert first["behavior_sha256"] == second["behavior_sha256"] assert first["model_runtime"]["default_model"] == "anthropic/claude-sonnet-4-6" assert first["components"]["persistent_memory"]["content"]["file_count"] == 1 encoded = json.dumps(first) assert "super-secret-token" not in encoded assert "private participant fact" not in encoded assert '{"private":"conversation"}' not in encoded def test_behavior_manifest_changes_when_a_behavior_layer_changes(tmp_path: Path) -> None: profile = tmp_path / "profile" hermes = tmp_path / "hermes" _write(profile / "config.yaml", "model:\n provider: openrouter\n default: model-a\n") _write(profile / "SOUL.md", "identity-a\n") _write(hermes / "run_agent.py", "runtime\n") _write(hermes / "agent" / "prompt_builder.py", "prompts\n") before = manifest.build_manifest(profile, hermes) _write(profile / "SOUL.md", "identity-b\n") after = manifest.build_manifest(profile, hermes) assert before["behavior_sha256"] != after["behavior_sha256"] assert ( before["components"]["runtime_identity"]["content"]["sha256"] != after["components"]["runtime_identity"]["content"]["sha256"] ) def test_behavior_manifest_hashes_content_behind_symlinks(tmp_path: Path) -> None: profile = tmp_path / "profile" hermes = tmp_path / "hermes" target = tmp_path / "deployed-skill.md" _write(profile / "config.yaml", "model:\n provider: test\n") _write(profile / "SOUL.md", "identity\n") _write(target, "version-a\n") (profile / "skills").mkdir(parents=True) (profile / "skills" / "SKILL.md").symlink_to(target) _write(hermes / "run_agent.py", "runtime\n") _write(hermes / "agent" / "prompt_builder.py", "prompts\n") before = manifest.build_manifest(profile, hermes) _write(target, "version-b\n") after = manifest.build_manifest(profile, hermes) assert before["behavior_sha256"] != after["behavior_sha256"] symlinks = after["components"]["procedural_skills"]["content"]["symlinks"] assert symlinks[0]["path"] == "skills/SKILL.md" assert symlinks[0]["target_fingerprint"]["kind"] == "symlink" assert symlinks[0]["target_fingerprint"]["resolved"]["kind"] == "file" def test_live_handler_harness_excludes_prior_dynamic_state_and_compares_live_fingerprints() -> None: source = (ROOT / "scripts" / "run_leo_direct_claim_handler_suite.py").read_text(encoding="utf-8") for directory in ("sessions", "state", "memories"): assert f'"{directory}"' in source assert '".skills_prompt_snapshot.json"' in source assert "build_behavior_manifest" in source assert 'report["changed_live_profile"] =' in source assert '"changed_live_profile": False' not in source