Compare commits
1 Commits
fix/ci-sta
...
bezalel/se
| Author | SHA1 | Date | |
|---|---|---|---|
| d76a922707 |
@@ -6,7 +6,7 @@ model: anthropic/claude-opus-4.6
|
||||
# Fallback chain: Anthropic -> Kimi -> Ollama (local)
|
||||
fallback_providers:
|
||||
- provider: kimi-coding
|
||||
model: kimi-for-coding
|
||||
model: kimi-k2.5
|
||||
timeout: 60
|
||||
reason: "Primary fallback when Anthropic quota limited"
|
||||
|
||||
|
||||
177
devkit/nightly_sitrep.py
Normal file
177
devkit/nightly_sitrep.py
Normal file
@@ -0,0 +1,177 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Bezalel Nightly Fleet SITREP
|
||||
|
||||
Runs autonomously every morning to check:
|
||||
1. Local system health
|
||||
2. All open PRs/issues in hermes-agent
|
||||
3. Stale items and blockers
|
||||
4. Fleet-wide activity (wizard presence in Gitea)
|
||||
|
||||
Posts findings to Gitea issue #911 (Lazarus Pit) and delivers summary.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import urllib.request
|
||||
from typing import Any, Dict, List
|
||||
|
||||
GITEA_TOKEN = os.getenv("GITEA_TOKEN", "")
|
||||
GITEA_BASE = os.getenv("GITEA_URL", "https://forge.alexanderwhitestone.com")
|
||||
REPO = "Timmy_Foundation/hermes-agent"
|
||||
SITREP_ISSUE = 911 # Lazarus Pit
|
||||
|
||||
|
||||
def gitea_request(method: str, path: str, data: Dict = None) -> Any:
|
||||
url = f"{GITEA_BASE}/api/v1{path}"
|
||||
headers = {"Content-Type": "application/json", "Accept": "application/json"}
|
||||
if GITEA_TOKEN:
|
||||
headers["Authorization"] = f"token {GITEA_TOKEN}"
|
||||
body = json.dumps(data).encode() if data else None
|
||||
req = urllib.request.Request(url, data=body, headers=headers, method=method)
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||
return json.loads(resp.read().decode())
|
||||
except urllib.error.HTTPError as e:
|
||||
return {"error": True, "status": e.code, "body": e.read().decode()}
|
||||
|
||||
|
||||
def get_health() -> Dict:
|
||||
try:
|
||||
env = os.environ.copy()
|
||||
env["PYTHONPATH"] = "/root/wizards/bezalel/hermes"
|
||||
result = subprocess.run(
|
||||
[sys.executable, "-m", "devkit.health", "--json"],
|
||||
capture_output=True, text=True, timeout=30,
|
||||
cwd="/root/wizards/bezalel/hermes",
|
||||
env=env
|
||||
)
|
||||
return json.loads(result.stdout)
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
|
||||
def analyze_issues() -> Dict:
|
||||
issues = gitea_request("GET", f"/repos/{REPO}/issues?state=open&limit=100") or []
|
||||
prs = [i for i in issues if i.get("pull_request")]
|
||||
pure_issues = [i for i in issues if not i.get("pull_request")]
|
||||
|
||||
stale_issues = []
|
||||
stale_prs = []
|
||||
now = time.time()
|
||||
|
||||
for i in pure_issues:
|
||||
updated = i.get("updated_at", "")
|
||||
if updated:
|
||||
try:
|
||||
ts = time.mktime(time.strptime(updated[:19], "%Y-%m-%dT%H:%M:%S"))
|
||||
if now - ts > 7 * 24 * 3600:
|
||||
stale_issues.append(i)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
for p in prs:
|
||||
updated = p.get("updated_at", "")
|
||||
if updated:
|
||||
try:
|
||||
ts = time.mktime(time.strptime(updated[:19], "%Y-%m-%dT%H:%M:%S"))
|
||||
if now - ts > 7 * 24 * 3600:
|
||||
stale_prs.append(p)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return {
|
||||
"total_open_issues": len(pure_issues),
|
||||
"total_open_prs": len(prs),
|
||||
"stale_issues": len(stale_issues),
|
||||
"stale_prs": len(stale_prs),
|
||||
"stale_issue_numbers": [i["number"] for i in stale_issues[:5]],
|
||||
"stale_pr_numbers": [p["number"] for p in stale_prs[:5]],
|
||||
}
|
||||
|
||||
|
||||
def check_wizard_activity() -> Dict:
|
||||
"""Check which wizards have been active in the last 24h via comments."""
|
||||
since = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(time.time() - 24 * 3600))
|
||||
comments = gitea_request("GET", f"/repos/{REPO}/issues/comments?since={since}&limit=200") or []
|
||||
|
||||
wizards = {"Timmy": False, "ezra": False, "allegro": False, "bezalel": False}
|
||||
for c in comments:
|
||||
user = c.get("user", {}).get("login", "")
|
||||
if user in wizards:
|
||||
wizards[user] = True
|
||||
|
||||
return wizards
|
||||
|
||||
|
||||
def build_sitrep() -> str:
|
||||
health = get_health()
|
||||
issues = analyze_issues()
|
||||
activity = check_wizard_activity()
|
||||
|
||||
lines = [
|
||||
"## 🌙 Nightly Fleet SITREP — Bezalel",
|
||||
f"**Generated:** {time.strftime('%Y-%m-%d %H:%M UTC', time.gmtime())}",
|
||||
"",
|
||||
"### Forge Health (Bezalel VPS)",
|
||||
f"- Overall: **{health.get('overall', 'unknown').upper()}**",
|
||||
f"- Load avg: {health.get('load', {}).get('avg', 'N/A')}",
|
||||
f"- Disk used: {health.get('disk', {}).get('used_percent', 'N/A')}%",
|
||||
f"- Hermes processes: {health.get('processes', {}).get('hermes_count', 'N/A')}",
|
||||
"",
|
||||
"### Gitea Activity (Last 24h)",
|
||||
]
|
||||
for wizard, active in activity.items():
|
||||
icon = "🟢" if active else "🔴"
|
||||
lines.append(f"- {icon} **{wizard}**: {'active' if active else 'no comments'}")
|
||||
|
||||
lines.extend([
|
||||
"",
|
||||
"### Open Work",
|
||||
f"- Open issues: **{issues['total_open_issues']}**",
|
||||
f"- Open PRs: **{issues['total_open_prs']}**",
|
||||
f"- Stale issues (>7d): **{issues['stale_issues']}** ({', '.join(map(str, issues['stale_issue_numbers'])) or 'none'})",
|
||||
f"- Stale PRs (>7d): **{issues['stale_prs']}** ({', '.join(map(str, issues['stale_pr_numbers'])) or 'none'})",
|
||||
"",
|
||||
"### Autonomous Actions Taken",
|
||||
])
|
||||
|
||||
actions = []
|
||||
if not activity.get("allegro"):
|
||||
actions.append("- Flagged Allegro as inactive in last 24h — may need config refresh or health check.")
|
||||
if issues["stale_prs"] > 0:
|
||||
actions.append(f"- Identified {issues['stale_prs']} stale PR(s) requiring review or closure.")
|
||||
if health.get("overall") != "ok":
|
||||
actions.append("- Bezalel health is NOT ok — investigating locally.")
|
||||
|
||||
if not actions:
|
||||
actions.append("- No immediate autonomous actions required. Forge is stable.")
|
||||
|
||||
lines.extend(actions)
|
||||
lines.append("")
|
||||
lines.append("— Bezalel, Master of the Forge")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def post_sitrep():
|
||||
body = build_sitrep()
|
||||
repo_for_sitrep = "Timmy_Foundation/the-nexus"
|
||||
result = gitea_request(
|
||||
"POST",
|
||||
f"/repos/{repo_for_sitrep}/issues/{SITREP_ISSUE}/comments",
|
||||
{"body": body}
|
||||
)
|
||||
if result.get("error"):
|
||||
print(f"Failed to post SITREP: {result}", file=sys.stderr)
|
||||
return 1
|
||||
print(f"SITREP posted: {result.get('html_url')}")
|
||||
print("\n--- SITREP CONTENT ---\n")
|
||||
print(body)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(post_sitrep())
|
||||
@@ -1563,7 +1563,6 @@ _PROVIDER_MODELS = {
|
||||
"glm-4.5-flash",
|
||||
],
|
||||
"kimi-coding": [
|
||||
"kimi-for-coding",
|
||||
"kimi-k2.5",
|
||||
"kimi-k2-thinking",
|
||||
"kimi-k2-thinking-turbo",
|
||||
@@ -2038,9 +2037,8 @@ def _model_flow_kimi(config, current_model=""):
|
||||
|
||||
# Step 3: Model selection — show appropriate models for the endpoint
|
||||
if is_coding_plan:
|
||||
# Coding Plan models (kimi-for-coding first)
|
||||
# Coding Plan models (kimi-k2.5 first — kimi-for-coding retired due to 403)
|
||||
model_list = [
|
||||
"kimi-for-coding",
|
||||
"kimi-k2.5",
|
||||
"kimi-k2-thinking",
|
||||
"kimi-k2-thinking-turbo",
|
||||
|
||||
@@ -114,7 +114,6 @@ _PROVIDER_MODELS: dict[str, list[str]] = {
|
||||
"glm-4.5-flash",
|
||||
],
|
||||
"kimi-coding": [
|
||||
"kimi-for-coding",
|
||||
"kimi-k2.5",
|
||||
"kimi-k2-thinking",
|
||||
"kimi-k2-thinking-turbo",
|
||||
|
||||
47
skills/memory/mempalace/SKILL.md
Normal file
47
skills/memory/mempalace/SKILL.md
Normal file
@@ -0,0 +1,47 @@
|
||||
---
|
||||
name: mempalace
|
||||
title: MemPalace Memory System
|
||||
description: Leverage the local MemPalace memory palace for retrieval-augmented context, search, and wake-up prompts.
|
||||
trigger: When the user asks about memory, mempalace, palace search, or retrieving past context.
|
||||
---
|
||||
|
||||
# MemPalace Skill
|
||||
|
||||
## Trigger
|
||||
Use this skill whenever the user asks about mempalace, memory retrieval, palace search, or wants to leverage the memory palace system.
|
||||
|
||||
## Prerequisites
|
||||
- MemPalace installed in the Hermes venv at `/root/wizards/bezalel/hermes/venv/bin/mempalace`
|
||||
- Palace located at `/root/wizards/bezalel/.mempalace/palace`
|
||||
- Identity file at `/root/.mempalace/identity.txt`
|
||||
- Config at `/root/wizards/bezalel/mempalace.yaml`
|
||||
|
||||
## Commands
|
||||
|
||||
### Search the palace
|
||||
```bash
|
||||
/root/wizards/bezalel/hermes/venv/bin/mempalace --palace /root/wizards/bezalel/.mempalace/palace search "QUERY" --results 10
|
||||
```
|
||||
|
||||
### Wake-up context (L0 + L1)
|
||||
```bash
|
||||
/root/wizards/bezalel/hermes/venv/bin/mempalace --palace /root/wizards/bezalel/.mempalace/palace wake-up
|
||||
```
|
||||
|
||||
### Palace status
|
||||
```bash
|
||||
/root/wizards/bezalel/hermes/venv/bin/mempalace --palace /root/wizards/bezalel/.mempalace/palace status
|
||||
```
|
||||
|
||||
### Re-mine (run after significant changes)
|
||||
```bash
|
||||
cd /root/wizards/bezalel
|
||||
/root/wizards/bezalel/hermes/venv/bin/mempalace --palace /root/wizards/bezalel/.mempalace/palace mine /root/wizards/bezalel --agent bezalel
|
||||
```
|
||||
|
||||
## Force-Multiplying Practices
|
||||
1. **Always search before long-form answers** — check if the palace knows it first.
|
||||
2. **Inject wake-up context** when starting complex, multi-turn tasks that span files.
|
||||
3. **Re-mine after repo changes** — nightly cron keeps the palace current.
|
||||
4. **Use room filters** (`--room forge`, `--room hermes`) to sharpen retrieval.
|
||||
5. **Keep identity.txt current** — L0 identity anchors all downstream context.
|
||||
@@ -717,7 +717,7 @@ class TestKimiMoonshotModelListIsolation:
|
||||
def test_moonshot_list_excludes_coding_plan_only_models(self):
|
||||
from hermes_cli.main import _PROVIDER_MODELS
|
||||
moonshot_models = _PROVIDER_MODELS["moonshot"]
|
||||
coding_plan_only = {"kimi-for-coding", "kimi-k2-thinking-turbo"}
|
||||
coding_plan_only = {"kimi-k2-thinking-turbo"}
|
||||
leaked = set(moonshot_models) & coding_plan_only
|
||||
assert not leaked, f"Moonshot list contains Coding Plan-only models: {leaked}"
|
||||
|
||||
@@ -730,7 +730,7 @@ class TestKimiMoonshotModelListIsolation:
|
||||
def test_coding_plan_list_contains_plan_specific_models(self):
|
||||
from hermes_cli.main import _PROVIDER_MODELS
|
||||
coding_models = _PROVIDER_MODELS["kimi-coding"]
|
||||
assert "kimi-for-coding" in coding_models
|
||||
assert "kimi-k2.5" in coding_models
|
||||
assert "kimi-k2-thinking-turbo" in coding_models
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user