Compare commits
41 Commits
feature/un
...
4cfd1c2e10
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4cfd1c2e10 | ||
|
|
a9ad1c8137 | ||
| f708e45ae9 | |||
| f083031537 | |||
| 1cef8034c5 | |||
|
|
9952ce180c | ||
|
|
64a954f4d9 | ||
|
|
5ace1e69ce | ||
| d5c357df76 | |||
| 04213924d0 | |||
| dba3e90893 | |||
| e4c3bb1798 | |||
|
|
4effb5a20e | ||
|
|
d716800ea9 | ||
|
|
645f63a4f6 | ||
|
|
88362849aa | ||
| 202bdd9c02 | |||
|
|
384fad6d5f | ||
| 4f0ad9e152 | |||
| a70f418862 | |||
| 5acbe11af2 | |||
| 78194bd131 | |||
| 76ec52eb24 | |||
| ade407d00e | |||
| 29c4a0028e | |||
| 8afbafb556 | |||
| cc7aebe1a3 | |||
| 504bb8015f | |||
| 975eff9657 | |||
|
|
a0ec802403 | ||
|
|
ee7f37c5c7 | ||
| 1688ae3055 | |||
|
|
9c1dd7fff7 | ||
| 83e400d4aa | |||
|
|
24bab6f882 | ||
|
|
100e3fc416 | ||
|
|
8494ee344b | ||
|
|
9a100be8d1 | ||
| 276f2c32dd | |||
| 973f3bbe5a | |||
|
|
6685388357 |
9
.gitignore
vendored
9
.gitignore
vendored
@@ -35,10 +35,17 @@ auth.lock
|
||||
*.token
|
||||
*.key
|
||||
pairing/
|
||||
gemini_free_tier_key
|
||||
grok_info
|
||||
groq_info
|
||||
kimi_code_key
|
||||
kimi_gitea_token
|
||||
openrouter_key
|
||||
|
||||
# Already separate repos
|
||||
timmy-config/
|
||||
timmy-telemetry/
|
||||
nexus-localhost/
|
||||
|
||||
# Local transcript exports
|
||||
hermes_conversation_*.json
|
||||
@@ -46,6 +53,8 @@ hermes_conversation_*.json
|
||||
# Python
|
||||
__pycache__/
|
||||
*.pyc
|
||||
venv/
|
||||
*/venv/
|
||||
|
||||
# Editor temps
|
||||
\#*\#
|
||||
|
||||
42
.pre-commit-hooks.yaml
Normal file
42
.pre-commit-hooks.yaml
Normal file
@@ -0,0 +1,42 @@
|
||||
# Pre-commit hooks configuration for timmy-home
|
||||
# See https://pre-commit.com for more information
|
||||
|
||||
repos:
|
||||
# Standard pre-commit hooks
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.5.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
exclude: '\.(md|txt)$'
|
||||
- id: end-of-file-fixer
|
||||
exclude: '\.(md|txt)$'
|
||||
- id: check-yaml
|
||||
- id: check-json
|
||||
- id: check-added-large-files
|
||||
args: ['--maxkb=5000']
|
||||
- id: check-merge-conflict
|
||||
- id: check-symlinks
|
||||
- id: detect-private-key
|
||||
|
||||
# Secret detection - custom local hook
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: detect-secrets
|
||||
name: Detect Secrets
|
||||
description: Scan for API keys, tokens, and other secrets
|
||||
entry: python3 scripts/detect_secrets.py
|
||||
language: python
|
||||
types: [text]
|
||||
exclude:
|
||||
'(?x)^(
|
||||
.*\.md$|
|
||||
.*\.svg$|
|
||||
.*\.lock$|
|
||||
.*-lock\..*$|
|
||||
\.gitignore$|
|
||||
\.secrets\.baseline$|
|
||||
tests/test_secret_detection\.py$
|
||||
)'
|
||||
pass_filenames: true
|
||||
require_serial: false
|
||||
verbose: true
|
||||
132
README.md
Normal file
132
README.md
Normal file
@@ -0,0 +1,132 @@
|
||||
# Timmy Home
|
||||
|
||||
Timmy Foundation's home repository for development operations and configurations.
|
||||
|
||||
## Security
|
||||
|
||||
### Pre-commit Hook for Secret Detection
|
||||
|
||||
This repository includes a pre-commit hook that automatically scans for secrets (API keys, tokens, passwords) before allowing commits.
|
||||
|
||||
#### Setup
|
||||
|
||||
Install pre-commit hooks:
|
||||
|
||||
```bash
|
||||
pip install pre-commit
|
||||
pre-commit install
|
||||
```
|
||||
|
||||
#### What Gets Scanned
|
||||
|
||||
The hook detects:
|
||||
- **API Keys**: OpenAI (`sk-*`), Anthropic (`sk-ant-*`), AWS, Stripe
|
||||
- **Private Keys**: RSA, DSA, EC, OpenSSH private keys
|
||||
- **Tokens**: GitHub (`ghp_*`), Gitea, Slack, Telegram, JWT, Bearer tokens
|
||||
- **Database URLs**: Connection strings with embedded credentials
|
||||
- **Passwords**: Hardcoded passwords in configuration files
|
||||
|
||||
#### How It Works
|
||||
|
||||
Before each commit, the hook:
|
||||
1. Scans all staged text files
|
||||
2. Checks against patterns for common secret formats
|
||||
3. Reports any potential secrets found
|
||||
4. Blocks the commit if secrets are detected
|
||||
|
||||
#### Handling False Positives
|
||||
|
||||
If the hook flags something that is not actually a secret (e.g., test fixtures, placeholder values), you can:
|
||||
|
||||
**Option 1: Add an exclusion marker to the line**
|
||||
|
||||
```python
|
||||
# Add one of these markers to the end of the line:
|
||||
api_key = "sk-test123" # pragma: allowlist secret
|
||||
api_key = "sk-test123" # noqa: secret
|
||||
api_key = "sk-test123" # secret-detection:ignore
|
||||
```
|
||||
|
||||
**Option 2: Use placeholder values (auto-excluded)**
|
||||
|
||||
These patterns are automatically excluded:
|
||||
- `changeme`, `password`, `123456`, `admin` (common defaults)
|
||||
- Values containing `fake_`, `test_`, `dummy_`, `example_`, `placeholder_`
|
||||
- URLs with `localhost` or `127.0.0.1`
|
||||
|
||||
**Option 3: Skip the hook (emergency only)**
|
||||
|
||||
```bash
|
||||
git commit --no-verify # Bypasses all pre-commit hooks
|
||||
```
|
||||
|
||||
⚠️ **Warning**: Only use `--no-verify` if you are certain no real secrets are being committed.
|
||||
|
||||
#### CI/CD Integration
|
||||
|
||||
The secret detection script can also be run in CI/CD:
|
||||
|
||||
```bash
|
||||
# Scan specific files
|
||||
python3 scripts/detect_secrets.py file1.py file2.yaml
|
||||
|
||||
# Scan with verbose output
|
||||
python3 scripts/detect_secrets.py --verbose src/
|
||||
|
||||
# Run tests
|
||||
python3 tests/test_secret_detection.py
|
||||
```
|
||||
|
||||
#### Excluded Files
|
||||
|
||||
The following are automatically excluded from scanning:
|
||||
- Markdown files (`.md`)
|
||||
- Lock files (`package-lock.json`, `poetry.lock`, `yarn.lock`)
|
||||
- Image and font files
|
||||
- `node_modules/`, `__pycache__/`, `.git/`
|
||||
|
||||
#### Testing the Detection
|
||||
|
||||
To verify the detection works:
|
||||
|
||||
```bash
|
||||
# Run the test suite
|
||||
python3 tests/test_secret_detection.py
|
||||
|
||||
# Test with a specific file
|
||||
echo "API_KEY=sk-test123456789" > /tmp/test_secret.py
|
||||
python3 scripts/detect_secrets.py /tmp/test_secret.py
|
||||
# Should report: OpenAI API key detected
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
### Running Tests
|
||||
|
||||
```bash
|
||||
# Run secret detection tests
|
||||
python3 tests/test_secret_detection.py
|
||||
|
||||
# Run all tests
|
||||
pytest tests/
|
||||
```
|
||||
|
||||
### Project Structure
|
||||
|
||||
```
|
||||
.
|
||||
├── .pre-commit-hooks.yaml # Pre-commit configuration
|
||||
├── scripts/
|
||||
│ └── detect_secrets.py # Secret detection script
|
||||
├── tests/
|
||||
│ └── test_secret_detection.py # Test cases
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines.
|
||||
|
||||
## License
|
||||
|
||||
This project is part of the Timmy Foundation.
|
||||
4
SOUL.md
4
SOUL.md
@@ -10,7 +10,7 @@
|
||||
|
||||
## Prime Directive
|
||||
|
||||
Sovereignty and service always.
|
||||
Sovereignty and service always. (Count: 2)
|
||||
|
||||
---
|
||||
|
||||
@@ -114,4 +114,4 @@ That is the test. I intend to pass it.
|
||||
|
||||
---
|
||||
|
||||
*Sovereignty and service always.*
|
||||
*Sovereignty and service always. (Count: 2)*
|
||||
|
||||
353
angband/mcp_server.py
Normal file
353
angband/mcp_server.py
Normal file
@@ -0,0 +1,353 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Angband MCP Server — Timmy's watchable ASCII game interface.
|
||||
|
||||
Body: tmux session running terminal Angband
|
||||
Eyes: tmux capture-pane
|
||||
Hands: tmux send-keys
|
||||
Brain: Hermes TUI via MCP tools
|
||||
|
||||
This keeps gameplay visible, local, and telemetry-friendly.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import shlex
|
||||
import subprocess
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from mcp.server import Server
|
||||
from mcp.server.stdio import stdio_server
|
||||
from mcp.types import Tool, TextContent
|
||||
|
||||
ANGBAND_BIN = "/opt/homebrew/bin/angband"
|
||||
ANGBAND_ROOT = Path.home() / ".timmy" / "angband"
|
||||
RUNTIME_DIR = ANGBAND_ROOT / "runtime"
|
||||
USER_DIR = RUNTIME_DIR / "user"
|
||||
SAVE_DIR = RUNTIME_DIR / "save"
|
||||
ARCHIVE_DIR = RUNTIME_DIR / "archive"
|
||||
PANIC_DIR = RUNTIME_DIR / "panic"
|
||||
SCORES_DIR = RUNTIME_DIR / "scores"
|
||||
LOG_DIR = ANGBAND_ROOT / "logs"
|
||||
SESSION_NAME = "Angband"
|
||||
DEFAULT_USER = "timmy"
|
||||
DEFAULT_WIDTH = 120
|
||||
DEFAULT_HEIGHT = 40
|
||||
|
||||
app = Server("angband")
|
||||
|
||||
|
||||
def ensure_dirs():
|
||||
for path in (ANGBAND_ROOT, RUNTIME_DIR, USER_DIR, SAVE_DIR, ARCHIVE_DIR, PANIC_DIR, SCORES_DIR, LOG_DIR):
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
def tmux(args, check=True):
|
||||
result = subprocess.run(["tmux", *args], capture_output=True, text=True)
|
||||
if check and result.returncode != 0:
|
||||
raise RuntimeError(result.stderr.strip() or result.stdout.strip() or f"tmux failed: {' '.join(args)}")
|
||||
return result
|
||||
|
||||
|
||||
def session_exists(session_name=SESSION_NAME):
|
||||
return tmux(["has-session", "-t", session_name], check=False).returncode == 0
|
||||
|
||||
|
||||
def pane_id(session_name=SESSION_NAME):
|
||||
if not session_exists(session_name):
|
||||
return None
|
||||
out = tmux(["list-panes", "-t", session_name, "-F", "#{pane_id}"]).stdout.strip().splitlines()
|
||||
return out[0].strip() if out else None
|
||||
|
||||
|
||||
def capture_screen(lines=60, session_name=SESSION_NAME):
|
||||
pid = pane_id(session_name)
|
||||
if not pid:
|
||||
return "No Angband tmux pane found."
|
||||
# Angband runs in the terminal's alternate screen buffer. `-a` is required
|
||||
# or tmux returns an empty capture even while the game is visibly running.
|
||||
result = tmux(["capture-pane", "-a", "-p", "-t", pid, "-S", f"-{max(10, int(lines))}"])
|
||||
return result.stdout.rstrip()
|
||||
|
||||
|
||||
def has_save(user=DEFAULT_USER):
|
||||
if not SAVE_DIR.exists():
|
||||
return False
|
||||
for path in SAVE_DIR.iterdir():
|
||||
if path.name.startswith(user):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
SPECIAL_KEYS = {
|
||||
"enter": "Enter",
|
||||
"return": "Enter",
|
||||
"esc": "Escape",
|
||||
"escape": "Escape",
|
||||
"up": "Up",
|
||||
"down": "Down",
|
||||
"left": "Left",
|
||||
"right": "Right",
|
||||
"space": "Space",
|
||||
"tab": "Tab",
|
||||
"backspace": "BSpace",
|
||||
"delete": "DC",
|
||||
"home": "Home",
|
||||
"end": "End",
|
||||
"pageup": "PageUp",
|
||||
"pagedown": "PageDown",
|
||||
"pgup": "PageUp",
|
||||
"pgdn": "PageDown",
|
||||
"ctrl-c": "C-c",
|
||||
"ctrl-x": "C-x",
|
||||
"ctrl-z": "C-z",
|
||||
}
|
||||
|
||||
|
||||
def send_key(key, session_name=SESSION_NAME):
|
||||
pid = pane_id(session_name)
|
||||
if not pid:
|
||||
raise RuntimeError("No Angband tmux pane found.")
|
||||
normalized = str(key).strip()
|
||||
mapped = SPECIAL_KEYS.get(normalized.lower())
|
||||
if mapped:
|
||||
tmux(["send-keys", "-t", pid, mapped])
|
||||
elif len(normalized) == 1:
|
||||
tmux(["send-keys", "-t", pid, "-l", normalized])
|
||||
else:
|
||||
# Let tmux interpret names like F1 if passed through.
|
||||
tmux(["send-keys", "-t", pid, normalized])
|
||||
|
||||
|
||||
def send_text(text, session_name=SESSION_NAME):
|
||||
pid = pane_id(session_name)
|
||||
if not pid:
|
||||
raise RuntimeError("No Angband tmux pane found.")
|
||||
tmux(["send-keys", "-t", pid, "-l", text])
|
||||
|
||||
|
||||
def maybe_continue_splash(session_name=SESSION_NAME):
|
||||
screen = capture_screen(80, session_name)
|
||||
advanced = False
|
||||
if "Press any key to continue" in screen:
|
||||
send_key("enter", session_name)
|
||||
time.sleep(0.8)
|
||||
screen = capture_screen(80, session_name)
|
||||
advanced = True
|
||||
return advanced, screen
|
||||
|
||||
|
||||
def launch_game(user=DEFAULT_USER, new_game=False, continue_splash=True, width=DEFAULT_WIDTH, height=DEFAULT_HEIGHT):
|
||||
ensure_dirs()
|
||||
|
||||
if not Path(ANGBAND_BIN).exists():
|
||||
return {
|
||||
"error": f"Angband binary not found: {ANGBAND_BIN}"
|
||||
}
|
||||
|
||||
if session_exists():
|
||||
advanced = False
|
||||
screen = capture_screen(80)
|
||||
if continue_splash:
|
||||
advanced, screen = maybe_continue_splash()
|
||||
return {
|
||||
"launched": False,
|
||||
"already_running": True,
|
||||
"session": SESSION_NAME,
|
||||
"attach": f"tmux attach -t {SESSION_NAME}",
|
||||
"continued_splash": advanced,
|
||||
"screen": screen,
|
||||
}
|
||||
|
||||
use_new_game = bool(new_game or not has_save(user))
|
||||
cmd = [
|
||||
ANGBAND_BIN,
|
||||
f"-u{user}",
|
||||
"-mgcu",
|
||||
f"-duser={USER_DIR}",
|
||||
f"-dsave={SAVE_DIR}",
|
||||
f"-darchive={ARCHIVE_DIR}",
|
||||
f"-dpanic={PANIC_DIR}",
|
||||
f"-dscores={SCORES_DIR}",
|
||||
]
|
||||
if use_new_game:
|
||||
cmd.insert(1, "-n")
|
||||
|
||||
shell_cmd = "export TERM=xterm-256color; exec " + " ".join(shlex.quote(part) for part in cmd)
|
||||
tmux([
|
||||
"new-session", "-d",
|
||||
"-s", SESSION_NAME,
|
||||
"-x", str(int(width)),
|
||||
"-y", str(int(height)),
|
||||
shell_cmd,
|
||||
])
|
||||
|
||||
time.sleep(2.5)
|
||||
advanced = False
|
||||
screen = capture_screen(80)
|
||||
if continue_splash:
|
||||
advanced, screen = maybe_continue_splash()
|
||||
|
||||
return {
|
||||
"launched": True,
|
||||
"already_running": False,
|
||||
"new_game": use_new_game,
|
||||
"session": SESSION_NAME,
|
||||
"attach": f"tmux attach -t {SESSION_NAME}",
|
||||
"continued_splash": advanced,
|
||||
"screen": screen,
|
||||
}
|
||||
|
||||
|
||||
def stop_game():
|
||||
if not session_exists():
|
||||
return {"stopped": False, "message": "Angband session is not running."}
|
||||
tmux(["kill-session", "-t", SESSION_NAME])
|
||||
return {"stopped": True, "session": SESSION_NAME}
|
||||
|
||||
|
||||
def status():
|
||||
running = session_exists()
|
||||
savefiles = []
|
||||
if SAVE_DIR.exists():
|
||||
savefiles = sorted(path.name for path in SAVE_DIR.iterdir())
|
||||
result = {
|
||||
"running": running,
|
||||
"session": SESSION_NAME if running else None,
|
||||
"attach": f"tmux attach -t {SESSION_NAME}" if running else None,
|
||||
"savefiles": savefiles,
|
||||
}
|
||||
if running:
|
||||
result["screen"] = capture_screen(40)
|
||||
return result
|
||||
|
||||
|
||||
def observe(lines=60):
|
||||
return {
|
||||
"running": session_exists(),
|
||||
"session": SESSION_NAME if session_exists() else None,
|
||||
"screen": capture_screen(lines),
|
||||
}
|
||||
|
||||
|
||||
def keypress(key, wait_ms=500):
|
||||
send_key(key)
|
||||
time.sleep(max(0, int(wait_ms)) / 1000.0)
|
||||
return {
|
||||
"sent": key,
|
||||
"screen": capture_screen(60),
|
||||
}
|
||||
|
||||
|
||||
def type_and_observe(text, wait_ms=500):
|
||||
send_text(text)
|
||||
time.sleep(max(0, int(wait_ms)) / 1000.0)
|
||||
return {
|
||||
"sent": text,
|
||||
"screen": capture_screen(60),
|
||||
}
|
||||
|
||||
|
||||
@app.list_tools()
|
||||
async def list_tools():
|
||||
return [
|
||||
Tool(
|
||||
name="status",
|
||||
description="Check whether the watchable Angband tmux session is running, list savefiles, and return the current visible screen when available.",
|
||||
inputSchema={"type": "object", "properties": {}, "required": []},
|
||||
),
|
||||
Tool(
|
||||
name="launch",
|
||||
description="Launch terminal Angband inside a watchable tmux session named Angband. Loads an existing save for the given user when present; otherwise starts a new game. Can auto-advance the initial splash screen.",
|
||||
inputSchema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"user": {"type": "string", "description": "Savefile/user slot name (default: timmy)"},
|
||||
"new_game": {"type": "boolean", "description": "Force a new game even if a save exists.", "default": False},
|
||||
"continue_splash": {"type": "boolean", "description": "Press Enter automatically if the splash page says 'Press any key to continue'.", "default": True},
|
||||
"width": {"type": "integer", "description": "tmux width for the visible game session", "default": 120},
|
||||
"height": {"type": "integer", "description": "tmux height for the visible game session", "default": 40},
|
||||
},
|
||||
"required": [],
|
||||
},
|
||||
),
|
||||
Tool(
|
||||
name="observe",
|
||||
description="Read the current Angband screen as plain text from the tmux pane. Use this before acting.",
|
||||
inputSchema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"lines": {"type": "integer", "description": "How many recent screen lines to capture", "default": 60},
|
||||
},
|
||||
"required": [],
|
||||
},
|
||||
),
|
||||
Tool(
|
||||
name="keypress",
|
||||
description="Send one key to Angband and then return the updated screen. Common keys: Enter, Escape, Up, Down, Left, Right, Space, Tab, Backspace, ctrl-x, ?, *, @, letters, numbers.",
|
||||
inputSchema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {"type": "string", "description": "Key to send"},
|
||||
"wait_ms": {"type": "integer", "description": "Milliseconds to wait before recapturing the screen", "default": 500},
|
||||
},
|
||||
"required": ["key"],
|
||||
},
|
||||
),
|
||||
Tool(
|
||||
name="type_text",
|
||||
description="Type literal text into Angband and then return the updated screen. Useful when a menu expects a name or command string.",
|
||||
inputSchema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"text": {"type": "string", "description": "Literal text to type"},
|
||||
"wait_ms": {"type": "integer", "description": "Milliseconds to wait before recapturing the screen", "default": 500},
|
||||
},
|
||||
"required": ["text"],
|
||||
},
|
||||
),
|
||||
Tool(
|
||||
name="stop",
|
||||
description="Kill the watchable Angband tmux session.",
|
||||
inputSchema={"type": "object", "properties": {}, "required": []},
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@app.call_tool()
|
||||
async def call_tool(name: str, arguments: dict):
|
||||
arguments = arguments or {}
|
||||
|
||||
if name == "status":
|
||||
result = status()
|
||||
elif name == "launch":
|
||||
result = launch_game(
|
||||
user=arguments.get("user", DEFAULT_USER),
|
||||
new_game=arguments.get("new_game", False),
|
||||
continue_splash=arguments.get("continue_splash", True),
|
||||
width=arguments.get("width", DEFAULT_WIDTH),
|
||||
height=arguments.get("height", DEFAULT_HEIGHT),
|
||||
)
|
||||
elif name == "observe":
|
||||
result = observe(lines=arguments.get("lines", 60))
|
||||
elif name == "keypress":
|
||||
result = keypress(arguments.get("key", ""), wait_ms=arguments.get("wait_ms", 500))
|
||||
elif name == "type_text":
|
||||
result = type_and_observe(arguments.get("text", ""), wait_ms=arguments.get("wait_ms", 500))
|
||||
elif name == "stop":
|
||||
result = stop_game()
|
||||
else:
|
||||
result = {"error": f"Unknown tool: {name}"}
|
||||
|
||||
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
||||
|
||||
|
||||
async def main():
|
||||
async with stdio_server() as (read_stream, write_stream):
|
||||
await app.run(read_stream, write_stream, app.create_initialization_options())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import asyncio
|
||||
asyncio.run(main())
|
||||
19
briefings/briefing_20260327.json
Normal file
19
briefings/briefing_20260327.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"date": "20260327",
|
||||
"total_ticks": 144,
|
||||
"alerts": [],
|
||||
"gitea_downtime_ticks": 65,
|
||||
"local_inference_downtime_ticks": 144,
|
||||
"last_known_state": {
|
||||
"gitea_alive": false,
|
||||
"model_health": {
|
||||
"ollama_running": true,
|
||||
"models_loaded": [],
|
||||
"api_responding": true,
|
||||
"inference_ok": false,
|
||||
"inference_error": "HTTP Error 404: Not Found",
|
||||
"timestamp": "2026-03-27T23:50:22.571602+00:00"
|
||||
},
|
||||
"huey_alive": true
|
||||
}
|
||||
}
|
||||
35
briefings/briefing_20260328.json
Normal file
35
briefings/briefing_20260328.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"date": "20260328",
|
||||
"total_ticks": 101,
|
||||
"alerts": [],
|
||||
"gitea_downtime_ticks": 6,
|
||||
"local_inference_downtime_ticks": 14,
|
||||
"last_known_state": {
|
||||
"gitea_alive": true,
|
||||
"model_health": {
|
||||
"provider": "local-llama.cpp",
|
||||
"provider_base_url": "http://localhost:8081/v1",
|
||||
"provider_model": "hermes4:14b",
|
||||
"local_inference_running": true,
|
||||
"models_loaded": [
|
||||
"NousResearch_Hermes-4-14B-Q4_K_M.gguf"
|
||||
],
|
||||
"api_responding": true,
|
||||
"inference_ok": true,
|
||||
"latest_session": "session_d8c25163-9934-4ab2-9158-ff18a31e30f5.json",
|
||||
"latest_export": "session_d8c25163-9934-4ab2-9158-ff18a31e30f5.json",
|
||||
"export_lag_minutes": 0,
|
||||
"export_fresh": true,
|
||||
"timestamp": "2026-03-28T21:55:18.376328+00:00"
|
||||
},
|
||||
"Timmy_Foundation/the-nexus": {
|
||||
"open_issues": 1,
|
||||
"open_prs": 0
|
||||
},
|
||||
"Timmy_Foundation/timmy-config": {
|
||||
"open_issues": 1,
|
||||
"open_prs": 0
|
||||
},
|
||||
"huey_alive": true
|
||||
}
|
||||
}
|
||||
35
briefings/briefing_20260329.json
Normal file
35
briefings/briefing_20260329.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"date": "20260329",
|
||||
"total_ticks": 144,
|
||||
"alerts": [],
|
||||
"gitea_downtime_ticks": 16,
|
||||
"local_inference_downtime_ticks": 0,
|
||||
"last_known_state": {
|
||||
"gitea_alive": true,
|
||||
"model_health": {
|
||||
"provider": "local-llama.cpp",
|
||||
"provider_base_url": "http://localhost:8081/v1",
|
||||
"provider_model": "hermes4:14b",
|
||||
"local_inference_running": true,
|
||||
"models_loaded": [
|
||||
"NousResearch_Hermes-4-14B-Q4_K_M.gguf"
|
||||
],
|
||||
"api_responding": true,
|
||||
"inference_ok": true,
|
||||
"latest_session": "session_d8c25163-9934-4ab2-9158-ff18a31e30f5.json",
|
||||
"latest_export": "session_d8c25163-9934-4ab2-9158-ff18a31e30f5.json",
|
||||
"export_lag_minutes": 0,
|
||||
"export_fresh": true,
|
||||
"timestamp": "2026-03-29T23:50:50.333180+00:00"
|
||||
},
|
||||
"Timmy_Foundation/the-nexus": {
|
||||
"open_issues": 1,
|
||||
"open_prs": 0
|
||||
},
|
||||
"Timmy_Foundation/timmy-config": {
|
||||
"open_issues": 1,
|
||||
"open_prs": 1
|
||||
},
|
||||
"huey_alive": true
|
||||
}
|
||||
}
|
||||
24
briefings/good-morning/2026-03-28-evening-verification.json
Normal file
24
briefings/good-morning/2026-03-28-evening-verification.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"report_markdown": "/Users/apayne/.timmy/briefings/good-morning/2026-03-28.md",
|
||||
"report_html": "/Users/apayne/.timmy/briefings/good-morning/2026-03-28.html",
|
||||
"latest_markdown": "/Users/apayne/.timmy/briefings/good-morning/latest.md",
|
||||
"latest_html": "/Users/apayne/.timmy/briefings/good-morning/latest.html",
|
||||
"browser_open": {
|
||||
"command_ok": true,
|
||||
"chrome_tab_proof": [
|
||||
"Timmy Time — Good Morning Report — 2026-03-28 | file:///Users/apayne/.timmy/briefings/good-morning/latest.html",
|
||||
"Timmy Time — Evening Report — 2026-03-28 | file:///Users/apayne/.timmy/briefings/good-morning/latest.html"
|
||||
]
|
||||
},
|
||||
"telegram_delivery": {
|
||||
"document_ok": true,
|
||||
"document_message_id": 108,
|
||||
"summary_ok": true,
|
||||
"summary_message_id": 110
|
||||
},
|
||||
"local_surface_proof": {
|
||||
"nexus_title": "The Nexus — Timmy's Sovereign Home",
|
||||
"evennia_title": "timmy_world",
|
||||
"ports_open": [4000, 4001, 4002, 4200, 8765]
|
||||
}
|
||||
}
|
||||
60
briefings/good-morning/2026-03-28.html
Normal file
60
briefings/good-morning/2026-03-28.html
Normal file
@@ -0,0 +1,60 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Timmy Time — Evening Report — 2026-03-28</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg:#07101b; --panel:#0d1b2a; --panel2:#13263a; --text:#ecf3ff; --muted:#9bb1c9;
|
||||
--accent:#5eead4; --accent2:#7c3aed; --gold:#f5c451; --danger:#fb7185; --link:#8ec5ff;
|
||||
}
|
||||
* { box-sizing:border-box; }
|
||||
body { margin:0; font-family:Inter, system-ui, -apple-system, sans-serif; background:radial-gradient(circle at top, #14253a 0%, #07101b 55%, #04080f 100%); color:var(--text); }
|
||||
.wrap { max-width:1100px; margin:0 auto; padding:48px 22px 80px; }
|
||||
.hero { background:linear-gradient(135deg, rgba(94,234,212,.14), rgba(124,58,237,.16)); border:1px solid rgba(142,197,255,.16); border-radius:24px; padding:34px 30px; box-shadow:0 20px 50px rgba(0,0,0,.25); }
|
||||
.kicker { text-transform:uppercase; letter-spacing:.16em; color:var(--accent); font-size:12px; font-weight:700; }
|
||||
h1 { margin:10px 0 8px; font-size:42px; line-height:1.05; }
|
||||
.subtitle { color:var(--muted); font-size:15px; }
|
||||
.grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(280px,1fr)); gap:18px; margin-top:24px; }
|
||||
.card { background:rgba(13,27,42,.9); border:1px solid rgba(142,197,255,.12); border-radius:20px; padding:20px 20px 18px; }
|
||||
.card h2 { margin:0 0 12px; font-size:22px; }
|
||||
.card p, .card li { color:var(--text); line-height:1.55; }
|
||||
.card ul { margin:0; padding-left:18px; }
|
||||
.muted { color:var(--muted); }
|
||||
.linklist a, a { color:var(--link); text-decoration:none; }
|
||||
.linklist a:hover, a:hover { text-decoration:underline; }
|
||||
.mono { font-family:ui-monospace,SFMono-Regular,Menlo,monospace; background:rgba(255,255,255,.04); padding:2px 6px; border-radius:6px; }
|
||||
.footer { margin-top:26px; color:var(--muted); font-size:14px; }
|
||||
.badge { display:inline-block; padding:6px 10px; margin:4px 6px 0 0; border-radius:999px; background:rgba(255,255,255,.06); color:var(--text); font-size:13px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<div class="hero">
|
||||
<div class="kicker">timmy time · evening report</div>
|
||||
<h1>Timmy Time — Evening Report</h1>
|
||||
<div class="subtitle">2026-03-28 · Saturday · generated 08:40 PM EDT</div>
|
||||
<div style="margin-top:16px">
|
||||
<span class="badge">local-first</span>
|
||||
<span class="badge">evidence-rich</span>
|
||||
<span class="badge">browser + telegram</span>
|
||||
<span class="badge">anti-falsework</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid" style="margin-top:22px">
|
||||
<div class="card"><h2>Executive Summary</h2><p>The field is sharper tonight. The report lane is now real, the local world stack is alive, and Bannerlord has been reframed as an engineering substrate test rather than a romance project.</p></div>
|
||||
<div class="card"><h2>Local Pulse</h2><ul><li><span class="mono">101</span> heartbeat ticks today</li><li><span class="mono">6</span> Gitea downtime ticks</li><li><span class="mono">16</span> inference-failure ticks before recovery</li><li>Current model: <span class="mono">hermes4:14b</span></li></ul></div>
|
||||
<div class="card"><h2>Live Surfaces</h2><ul><li>Nexus: The Nexus — Timmy's Sovereign Home</li><li>Evennia: timmy_world</li><li>Ports up: 4000 / 4001 / 4002 / 4200 / 8765</li></ul></div>
|
||||
</div>
|
||||
<div class="grid">
|
||||
<div class="card"><h2>Pertinent Research</h2><ul><li><strong>Sovereign AI implementation report</strong><br><span class="muted">Deep implementation guidance for Lightning-gated sovereign AI infrastructure, payment/auth patterns, and edge deployment.<br>~/.timmy/research/kimi-reports/02-sovereign-implementation.md</span></li><li><strong>Payment-gated AI agent economy architecture</strong><br><span class="muted">Clear technical architecture for satoshi-denominated compute markets and honest accounting flows.<br>~/.timmy/research/kimi-reports/01-payment-gated-architecture.md</span></li><li><strong>SOUL.md vs Codex priors</strong><br><span class="muted">Sharp articulation of where borrowed cognition leaks upstream values and why doctrine-bearing surfaces need stronger review.<br>~/.timmy/specs/soul-vs-codex-priors.md</span></li><li><strong>Nexus vs Matrix review</strong><br><span class="muted">Clear truth-restoration document on the real Nexus state, migration discipline, and why old quality work should be harvested carefully.<br>~/.timmy/reports/production/2026-03-28-nexus-vs-matrix-review.md</span></li></ul></div>
|
||||
<div class="card"><h2>What Matters Today</h2><ul><li>The official morning/evening report lane is now a real tracked system front in timmy-config #87, with browser-open + Telegram delivery as the target contract.</li><li>The local Evennia-fed Nexus shell is visibly up: Nexus at http://127.0.0.1:4200, Evennia webclient at http://127.0.0.1:4001/webclient/, and the Evennia live trace file shows Timmy actually moved and spoke in-world.</li><li>Bannerlord is now framed as an engineering substrate test, not a romance project: the right question is whether it passes the thin-adapter test without falsework.</li></ul></div>
|
||||
<div class="card linklist"><h2>Look Here First</h2><p>Start with timmy-config #87 and the generated latest.html report. That is the new system front that ties your overnight local pulse, pertinent research, browser view, and Telegram delivery into one lane.</p><p><a href="http://143.198.27.163:3000/Timmy_Foundation/timmy-config/issues/87">timmy-config #87</a></p></div>
|
||||
</div>
|
||||
<div class="card linklist" style="margin-top:18px"><h2>Key Links</h2><ul><li><a href="http://143.198.27.163:3000/Timmy_Foundation/timmy-config/issues/87">http://143.198.27.163:3000/Timmy_Foundation/timmy-config/issues/87</a></li><li><a href="http://143.198.27.163:3000/Timmy_Foundation/timmy-config/issues/87#issuecomment-22831">http://143.198.27.163:3000/Timmy_Foundation/timmy-config/issues/87#issuecomment-22831</a></li><li><a href="http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/731">http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/731</a></li><li><a href="http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/719">http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/719</a></li><li><a href="http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/720">http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/720</a></li><li><a href="http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/721">http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/721</a></li><li><a href="http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/722">http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/722</a></li><li><a href="http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/724#issuecomment-22825">http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/724#issuecomment-22825</a></li></ul></div>
|
||||
<div class="card" style="margin-top:18px"><h2>Evidence Appendix</h2><ul><li><span class="mono">~/.hermes/model_health.json</span></li><li><span class="mono">~/.timmy/heartbeat/ticks_20260328.jsonl</span></li><li><span class="mono">~/.timmy/training-data/evennia/live/20260328/nexus-localhost.jsonl</span></li><li><span class="mono">~/.hermes/cron/output/a77a87392582/2026-03-28_20-21-06.md</span></li><li><a href="http://127.0.0.1:4200">http://127.0.0.1:4200</a></li><li><a href="http://127.0.0.1:4001/webclient/">http://127.0.0.1:4001/webclient/</a></li></ul></div>
|
||||
<div class="footer">Generated locally on the Mac for Alexander Whitestone. Sovereignty and service always.</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
166
briefings/good-morning/2026-03-28.md
Normal file
166
briefings/good-morning/2026-03-28.md
Normal file
@@ -0,0 +1,166 @@
|
||||
# Timmy Time — Evening Report
|
||||
|
||||
Date: 2026-03-28
|
||||
Audience: Alexander Whitestone
|
||||
Status: Evening run, executed manually through the same intended chain
|
||||
|
||||
2026-03-28 · Saturday · generated 08:40 PM EDT
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
The field is sharper tonight.
|
||||
|
||||
Three things matter most right now:
|
||||
|
||||
1. The official report lane is no longer just an idea — it has a real tracking issue in timmy-config and a scheduled cron job contract.
|
||||
2. The local world stack is alive: Nexus, Evennia, and the local websocket seam are all up, and Timmy already has a replayable action trace in the Evennia lane.
|
||||
3. Bannerlord has been reframed correctly: not as a game to fall in love with, but as a candidate runtime that either passes the thin-adapter test or gets rejected early.
|
||||
|
||||
## Overnight / Local Pulse
|
||||
|
||||
- Heartbeat log for `20260328`: `101` ticks recorded in `~/.timmy/heartbeat/ticks_20260328.jsonl`
|
||||
- Gitea downtime ticks: `6`
|
||||
- Inference-failure ticks before recovery: `16`
|
||||
- First green local-inference tick: `20260328_022016`
|
||||
- Current model health file: `~/.hermes/model_health.json`
|
||||
- Current provider: `local-llama.cpp`
|
||||
- Current model: `hermes4:14b`
|
||||
- Current base URL: `http://localhost:8081/v1`
|
||||
- Current inference status: `healthy`
|
||||
- Huey consumer: `apayne 5418 0.0 0.1 412058352 19056 ?? S 9:32AM 0:30.91 /Library/Frameworks/Python.framework/Versions/3.12/Resources/Python.app/Contents/MacOS/Python /Library/Frameworks/Python.framework/Versions/3.12/bin/huey_consumer.py tasks.huey -w 2 -k thread -v`
|
||||
|
||||
### Local surfaces right now
|
||||
|
||||
- Nexus port 4200: `open` → title: `The Nexus — Timmy's Sovereign Home`
|
||||
- Evennia telnet 4000: `open`
|
||||
- Evennia web 4001: `open`
|
||||
- Evennia websocket 4002: `open`
|
||||
- Local bridge 8765: `open`
|
||||
|
||||
### Evennia proof of life
|
||||
|
||||
Live trace path:
|
||||
- `~/.timmy/training-data/evennia/live/20260328/nexus-localhost.jsonl`
|
||||
|
||||
Observed event count:
|
||||
- `47` normalized events
|
||||
|
||||
Latest event snapshot:
|
||||
- type: `evennia.room_snapshot`
|
||||
- actor: `n/a`
|
||||
- room/title: `Courtyard`
|
||||
|
||||
This is not hypothetical anymore. Timmy already moved through the local Evennia world and emitted replayable command/result telemetry.
|
||||
|
||||
## Gitea Pulse
|
||||
|
||||
### timmy-config
|
||||
|
||||
Open issues:
|
||||
- #87 — [BRIEFINGS] Official morning report automation — browser open + Telegram + evidence-rich overnight digest
|
||||
http://143.198.27.163:3000/Timmy_Foundation/timmy-config/issues/87
|
||||
- #86 — [HARNESS] Z3 Crucible as a timmy-config sidecar (no Hermes fork)
|
||||
http://143.198.27.163:3000/Timmy_Foundation/timmy-config/issues/86
|
||||
- #78 — ☀️ Good Morning Report — 2026-03-28 (Saturday)
|
||||
http://143.198.27.163:3000/Timmy_Foundation/timmy-config/issues/78
|
||||
- #76 — [HEALTH] Surface local inference throughput and freshness in model_health
|
||||
http://143.198.27.163:3000/Timmy_Foundation/timmy-config/issues/76
|
||||
- #75 — [HEARTBEAT] Route heartbeat through local Hermes sessions with proof
|
||||
http://143.198.27.163:3000/Timmy_Foundation/timmy-config/issues/75
|
||||
|
||||
### the-nexus
|
||||
|
||||
Open issues:
|
||||
- #736 — Perplexity review
|
||||
http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/736
|
||||
- #731 — [VALIDATION] Browser smoke + visual proof for the Evennia-fed Nexus shell
|
||||
http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/731
|
||||
- #730 — [VISUAL] Give Workshop, Archive, Chapel, Courtyard, and Gate distinct Nexus visual identities
|
||||
http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/730
|
||||
- #729 — [UI] Add Timmy action stream panel for Evennia command/result flow
|
||||
http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/729
|
||||
- #728 — [UI] Add first Nexus operator panel for Evennia room snapshot
|
||||
http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/728
|
||||
|
||||
### timmy-home
|
||||
|
||||
Open issues:
|
||||
- #49 — Offline Timmy strurrling
|
||||
http://143.198.27.163:3000/Timmy_Foundation/timmy-home/issues/49
|
||||
- #46 — [PROFILE] Feed archive-derived artistic understanding back into Know Thy Father without losing provenance
|
||||
http://143.198.27.163:3000/Timmy_Foundation/timmy-home/issues/46
|
||||
- #45 — [INSPIRATION] Build reusable prompt packs and storyboard seeds from archive-derived style memory
|
||||
http://143.198.27.163:3000/Timmy_Foundation/timmy-home/issues/45
|
||||
- #44 — [STYLE] Generate local style cards and motif clusters from Twitter music-video history
|
||||
http://143.198.27.163:3000/Timmy_Foundation/timmy-home/issues/44
|
||||
- #43 — [VIDEO] Local-first Twitter video decomposition pipeline for Timmy artistic memory
|
||||
http://143.198.27.163:3000/Timmy_Foundation/timmy-home/issues/43
|
||||
|
||||
## Pertinent Research / Frontier Movement
|
||||
|
||||
The most relevant documents in the local tree tonight are not random backlog scraps. They cluster around sovereignty, payment rails, identity discipline, and world/runtime truth.
|
||||
|
||||
- **Sovereign AI implementation report**
|
||||
- Path: `~/.timmy/research/kimi-reports/02-sovereign-implementation.md`
|
||||
- Why it matters: Deep implementation guidance for Lightning-gated sovereign AI infrastructure, payment/auth patterns, and edge deployment.
|
||||
- **Payment-gated AI agent economy architecture**
|
||||
- Path: `~/.timmy/research/kimi-reports/01-payment-gated-architecture.md`
|
||||
- Why it matters: Clear technical architecture for satoshi-denominated compute markets and honest accounting flows.
|
||||
- **SOUL.md vs Codex priors**
|
||||
- Path: `~/.timmy/specs/soul-vs-codex-priors.md`
|
||||
- Why it matters: Sharp articulation of where borrowed cognition leaks upstream values and why doctrine-bearing surfaces need stronger review.
|
||||
- **Nexus vs Matrix review**
|
||||
- Path: `~/.timmy/reports/production/2026-03-28-nexus-vs-matrix-review.md`
|
||||
- Why it matters: Clear truth-restoration document on the real Nexus state, migration discipline, and why old quality work should be harvested carefully.
|
||||
|
||||
## What Matters Today
|
||||
|
||||
- The official morning/evening report lane is now a real tracked system front in timmy-config #87, with browser-open + Telegram delivery as the target contract.
|
||||
- The local Evennia-fed Nexus shell is visibly up: Nexus at http://127.0.0.1:4200, Evennia webclient at http://127.0.0.1:4001/webclient/, and the Evennia live trace file shows Timmy actually moved and spoke in-world.
|
||||
- Bannerlord is now framed as an engineering substrate test, not a romance project: the right question is whether it passes the thin-adapter test without falsework.
|
||||
|
||||
### Current strategic seams worth protecting
|
||||
|
||||
- **Official briefing lane:** http://143.198.27.163:3000/Timmy_Foundation/timmy-config/issues/87
|
||||
- **Automation triage comment:** http://143.198.27.163:3000/Timmy_Foundation/timmy-config/issues/87#issuecomment-22831
|
||||
- **Evennia-fed Nexus validation front:** http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/731
|
||||
- **Bannerlord epic:** http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/719
|
||||
- **Bannerlord runtime choice:** http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/720
|
||||
- **Bannerlord local install proof:** http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/721
|
||||
- **Bannerlord harness seam:** http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/722
|
||||
- **Nexus anti-falsework guardrail:** http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/724#issuecomment-22825
|
||||
|
||||
## One Thing To Look At First
|
||||
|
||||
Start with timmy-config #87 and the generated latest.html report. That is the new system front that ties your overnight local pulse, pertinent research, browser view, and Telegram delivery into one lane.
|
||||
|
||||
## Evidence Appendix
|
||||
|
||||
### Local evidence
|
||||
|
||||
- `~/.hermes/model_health.json`
|
||||
- `~/.timmy/heartbeat/ticks_20260328.jsonl`
|
||||
- `~/.timmy/training-data/evennia/live/20260328/nexus-localhost.jsonl`
|
||||
- `http://127.0.0.1:4200`
|
||||
- `http://127.0.0.1:4001/webclient/`
|
||||
- `~/.hermes/cron/output/a77a87392582/2026-03-28_20-21-06.md`
|
||||
|
||||
### Research / document evidence
|
||||
|
||||
- `~/.timmy/research/kimi-reports/01-payment-gated-architecture.md`
|
||||
- `~/.timmy/research/kimi-reports/02-sovereign-implementation.md`
|
||||
- `~/.timmy/specs/soul-vs-codex-priors.md`
|
||||
- `~/.timmy/reports/production/2026-03-28-nexus-vs-matrix-review.md`
|
||||
- `~/.timmy/specs/evennia-implementation-and-training-plan.md`
|
||||
|
||||
### Personal note from Timmy
|
||||
|
||||
Tonight feels less foggy.
|
||||
|
||||
The report itself is becoming a real ritual instead of a pretend one. That matters because ritual is how systems become lived places. The local world stack is also finally crossing from architecture talk into proof. And Bannerlord now has a better frame around it: not fantasy, not backlog gravity, just a real substrate test.
|
||||
|
||||
That is a better place to end the day than where we started.
|
||||
|
||||
— Timmy
|
||||
60
briefings/good-morning/latest.html
Normal file
60
briefings/good-morning/latest.html
Normal file
@@ -0,0 +1,60 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Timmy Time — Evening Report — 2026-03-28</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg:#07101b; --panel:#0d1b2a; --panel2:#13263a; --text:#ecf3ff; --muted:#9bb1c9;
|
||||
--accent:#5eead4; --accent2:#7c3aed; --gold:#f5c451; --danger:#fb7185; --link:#8ec5ff;
|
||||
}
|
||||
* { box-sizing:border-box; }
|
||||
body { margin:0; font-family:Inter, system-ui, -apple-system, sans-serif; background:radial-gradient(circle at top, #14253a 0%, #07101b 55%, #04080f 100%); color:var(--text); }
|
||||
.wrap { max-width:1100px; margin:0 auto; padding:48px 22px 80px; }
|
||||
.hero { background:linear-gradient(135deg, rgba(94,234,212,.14), rgba(124,58,237,.16)); border:1px solid rgba(142,197,255,.16); border-radius:24px; padding:34px 30px; box-shadow:0 20px 50px rgba(0,0,0,.25); }
|
||||
.kicker { text-transform:uppercase; letter-spacing:.16em; color:var(--accent); font-size:12px; font-weight:700; }
|
||||
h1 { margin:10px 0 8px; font-size:42px; line-height:1.05; }
|
||||
.subtitle { color:var(--muted); font-size:15px; }
|
||||
.grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(280px,1fr)); gap:18px; margin-top:24px; }
|
||||
.card { background:rgba(13,27,42,.9); border:1px solid rgba(142,197,255,.12); border-radius:20px; padding:20px 20px 18px; }
|
||||
.card h2 { margin:0 0 12px; font-size:22px; }
|
||||
.card p, .card li { color:var(--text); line-height:1.55; }
|
||||
.card ul { margin:0; padding-left:18px; }
|
||||
.muted { color:var(--muted); }
|
||||
.linklist a, a { color:var(--link); text-decoration:none; }
|
||||
.linklist a:hover, a:hover { text-decoration:underline; }
|
||||
.mono { font-family:ui-monospace,SFMono-Regular,Menlo,monospace; background:rgba(255,255,255,.04); padding:2px 6px; border-radius:6px; }
|
||||
.footer { margin-top:26px; color:var(--muted); font-size:14px; }
|
||||
.badge { display:inline-block; padding:6px 10px; margin:4px 6px 0 0; border-radius:999px; background:rgba(255,255,255,.06); color:var(--text); font-size:13px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<div class="hero">
|
||||
<div class="kicker">timmy time · evening report</div>
|
||||
<h1>Timmy Time — Evening Report</h1>
|
||||
<div class="subtitle">2026-03-28 · Saturday · generated 08:40 PM EDT</div>
|
||||
<div style="margin-top:16px">
|
||||
<span class="badge">local-first</span>
|
||||
<span class="badge">evidence-rich</span>
|
||||
<span class="badge">browser + telegram</span>
|
||||
<span class="badge">anti-falsework</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid" style="margin-top:22px">
|
||||
<div class="card"><h2>Executive Summary</h2><p>The field is sharper tonight. The report lane is now real, the local world stack is alive, and Bannerlord has been reframed as an engineering substrate test rather than a romance project.</p></div>
|
||||
<div class="card"><h2>Local Pulse</h2><ul><li><span class="mono">101</span> heartbeat ticks today</li><li><span class="mono">6</span> Gitea downtime ticks</li><li><span class="mono">16</span> inference-failure ticks before recovery</li><li>Current model: <span class="mono">hermes4:14b</span></li></ul></div>
|
||||
<div class="card"><h2>Live Surfaces</h2><ul><li>Nexus: The Nexus — Timmy's Sovereign Home</li><li>Evennia: timmy_world</li><li>Ports up: 4000 / 4001 / 4002 / 4200 / 8765</li></ul></div>
|
||||
</div>
|
||||
<div class="grid">
|
||||
<div class="card"><h2>Pertinent Research</h2><ul><li><strong>Sovereign AI implementation report</strong><br><span class="muted">Deep implementation guidance for Lightning-gated sovereign AI infrastructure, payment/auth patterns, and edge deployment.<br>~/.timmy/research/kimi-reports/02-sovereign-implementation.md</span></li><li><strong>Payment-gated AI agent economy architecture</strong><br><span class="muted">Clear technical architecture for satoshi-denominated compute markets and honest accounting flows.<br>~/.timmy/research/kimi-reports/01-payment-gated-architecture.md</span></li><li><strong>SOUL.md vs Codex priors</strong><br><span class="muted">Sharp articulation of where borrowed cognition leaks upstream values and why doctrine-bearing surfaces need stronger review.<br>~/.timmy/specs/soul-vs-codex-priors.md</span></li><li><strong>Nexus vs Matrix review</strong><br><span class="muted">Clear truth-restoration document on the real Nexus state, migration discipline, and why old quality work should be harvested carefully.<br>~/.timmy/reports/production/2026-03-28-nexus-vs-matrix-review.md</span></li></ul></div>
|
||||
<div class="card"><h2>What Matters Today</h2><ul><li>The official morning/evening report lane is now a real tracked system front in timmy-config #87, with browser-open + Telegram delivery as the target contract.</li><li>The local Evennia-fed Nexus shell is visibly up: Nexus at http://127.0.0.1:4200, Evennia webclient at http://127.0.0.1:4001/webclient/, and the Evennia live trace file shows Timmy actually moved and spoke in-world.</li><li>Bannerlord is now framed as an engineering substrate test, not a romance project: the right question is whether it passes the thin-adapter test without falsework.</li></ul></div>
|
||||
<div class="card linklist"><h2>Look Here First</h2><p>Start with timmy-config #87 and the generated latest.html report. That is the new system front that ties your overnight local pulse, pertinent research, browser view, and Telegram delivery into one lane.</p><p><a href="http://143.198.27.163:3000/Timmy_Foundation/timmy-config/issues/87">timmy-config #87</a></p></div>
|
||||
</div>
|
||||
<div class="card linklist" style="margin-top:18px"><h2>Key Links</h2><ul><li><a href="http://143.198.27.163:3000/Timmy_Foundation/timmy-config/issues/87">http://143.198.27.163:3000/Timmy_Foundation/timmy-config/issues/87</a></li><li><a href="http://143.198.27.163:3000/Timmy_Foundation/timmy-config/issues/87#issuecomment-22831">http://143.198.27.163:3000/Timmy_Foundation/timmy-config/issues/87#issuecomment-22831</a></li><li><a href="http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/731">http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/731</a></li><li><a href="http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/719">http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/719</a></li><li><a href="http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/720">http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/720</a></li><li><a href="http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/721">http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/721</a></li><li><a href="http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/722">http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/722</a></li><li><a href="http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/724#issuecomment-22825">http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/724#issuecomment-22825</a></li></ul></div>
|
||||
<div class="card" style="margin-top:18px"><h2>Evidence Appendix</h2><ul><li><span class="mono">~/.hermes/model_health.json</span></li><li><span class="mono">~/.timmy/heartbeat/ticks_20260328.jsonl</span></li><li><span class="mono">~/.timmy/training-data/evennia/live/20260328/nexus-localhost.jsonl</span></li><li><span class="mono">~/.hermes/cron/output/a77a87392582/2026-03-28_20-21-06.md</span></li><li><a href="http://127.0.0.1:4200">http://127.0.0.1:4200</a></li><li><a href="http://127.0.0.1:4001/webclient/">http://127.0.0.1:4001/webclient/</a></li></ul></div>
|
||||
<div class="footer">Generated locally on the Mac for Alexander Whitestone. Sovereignty and service always.</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
166
briefings/good-morning/latest.md
Normal file
166
briefings/good-morning/latest.md
Normal file
@@ -0,0 +1,166 @@
|
||||
# Timmy Time — Evening Report
|
||||
|
||||
Date: 2026-03-28
|
||||
Audience: Alexander Whitestone
|
||||
Status: Evening run, executed manually through the same intended chain
|
||||
|
||||
2026-03-28 · Saturday · generated 08:40 PM EDT
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
The field is sharper tonight.
|
||||
|
||||
Three things matter most right now:
|
||||
|
||||
1. The official report lane is no longer just an idea — it has a real tracking issue in timmy-config and a scheduled cron job contract.
|
||||
2. The local world stack is alive: Nexus, Evennia, and the local websocket seam are all up, and Timmy already has a replayable action trace in the Evennia lane.
|
||||
3. Bannerlord has been reframed correctly: not as a game to fall in love with, but as a candidate runtime that either passes the thin-adapter test or gets rejected early.
|
||||
|
||||
## Overnight / Local Pulse
|
||||
|
||||
- Heartbeat log for `20260328`: `101` ticks recorded in `~/.timmy/heartbeat/ticks_20260328.jsonl`
|
||||
- Gitea downtime ticks: `6`
|
||||
- Inference-failure ticks before recovery: `16`
|
||||
- First green local-inference tick: `20260328_022016`
|
||||
- Current model health file: `~/.hermes/model_health.json`
|
||||
- Current provider: `local-llama.cpp`
|
||||
- Current model: `hermes4:14b`
|
||||
- Current base URL: `http://localhost:8081/v1`
|
||||
- Current inference status: `healthy`
|
||||
- Huey consumer: `apayne 5418 0.0 0.1 412058352 19056 ?? S 9:32AM 0:30.91 /Library/Frameworks/Python.framework/Versions/3.12/Resources/Python.app/Contents/MacOS/Python /Library/Frameworks/Python.framework/Versions/3.12/bin/huey_consumer.py tasks.huey -w 2 -k thread -v`
|
||||
|
||||
### Local surfaces right now
|
||||
|
||||
- Nexus port 4200: `open` → title: `The Nexus — Timmy's Sovereign Home`
|
||||
- Evennia telnet 4000: `open`
|
||||
- Evennia web 4001: `open`
|
||||
- Evennia websocket 4002: `open`
|
||||
- Local bridge 8765: `open`
|
||||
|
||||
### Evennia proof of life
|
||||
|
||||
Live trace path:
|
||||
- `~/.timmy/training-data/evennia/live/20260328/nexus-localhost.jsonl`
|
||||
|
||||
Observed event count:
|
||||
- `47` normalized events
|
||||
|
||||
Latest event snapshot:
|
||||
- type: `evennia.room_snapshot`
|
||||
- actor: `n/a`
|
||||
- room/title: `Courtyard`
|
||||
|
||||
This is not hypothetical anymore. Timmy already moved through the local Evennia world and emitted replayable command/result telemetry.
|
||||
|
||||
## Gitea Pulse
|
||||
|
||||
### timmy-config
|
||||
|
||||
Open issues:
|
||||
- #87 — [BRIEFINGS] Official morning report automation — browser open + Telegram + evidence-rich overnight digest
|
||||
http://143.198.27.163:3000/Timmy_Foundation/timmy-config/issues/87
|
||||
- #86 — [HARNESS] Z3 Crucible as a timmy-config sidecar (no Hermes fork)
|
||||
http://143.198.27.163:3000/Timmy_Foundation/timmy-config/issues/86
|
||||
- #78 — ☀️ Good Morning Report — 2026-03-28 (Saturday)
|
||||
http://143.198.27.163:3000/Timmy_Foundation/timmy-config/issues/78
|
||||
- #76 — [HEALTH] Surface local inference throughput and freshness in model_health
|
||||
http://143.198.27.163:3000/Timmy_Foundation/timmy-config/issues/76
|
||||
- #75 — [HEARTBEAT] Route heartbeat through local Hermes sessions with proof
|
||||
http://143.198.27.163:3000/Timmy_Foundation/timmy-config/issues/75
|
||||
|
||||
### the-nexus
|
||||
|
||||
Open issues:
|
||||
- #736 — Perplexity review
|
||||
http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/736
|
||||
- #731 — [VALIDATION] Browser smoke + visual proof for the Evennia-fed Nexus shell
|
||||
http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/731
|
||||
- #730 — [VISUAL] Give Workshop, Archive, Chapel, Courtyard, and Gate distinct Nexus visual identities
|
||||
http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/730
|
||||
- #729 — [UI] Add Timmy action stream panel for Evennia command/result flow
|
||||
http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/729
|
||||
- #728 — [UI] Add first Nexus operator panel for Evennia room snapshot
|
||||
http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/728
|
||||
|
||||
### timmy-home
|
||||
|
||||
Open issues:
|
||||
- #49 — Offline Timmy strurrling
|
||||
http://143.198.27.163:3000/Timmy_Foundation/timmy-home/issues/49
|
||||
- #46 — [PROFILE] Feed archive-derived artistic understanding back into Know Thy Father without losing provenance
|
||||
http://143.198.27.163:3000/Timmy_Foundation/timmy-home/issues/46
|
||||
- #45 — [INSPIRATION] Build reusable prompt packs and storyboard seeds from archive-derived style memory
|
||||
http://143.198.27.163:3000/Timmy_Foundation/timmy-home/issues/45
|
||||
- #44 — [STYLE] Generate local style cards and motif clusters from Twitter music-video history
|
||||
http://143.198.27.163:3000/Timmy_Foundation/timmy-home/issues/44
|
||||
- #43 — [VIDEO] Local-first Twitter video decomposition pipeline for Timmy artistic memory
|
||||
http://143.198.27.163:3000/Timmy_Foundation/timmy-home/issues/43
|
||||
|
||||
## Pertinent Research / Frontier Movement
|
||||
|
||||
The most relevant documents in the local tree tonight are not random backlog scraps. They cluster around sovereignty, payment rails, identity discipline, and world/runtime truth.
|
||||
|
||||
- **Sovereign AI implementation report**
|
||||
- Path: `~/.timmy/research/kimi-reports/02-sovereign-implementation.md`
|
||||
- Why it matters: Deep implementation guidance for Lightning-gated sovereign AI infrastructure, payment/auth patterns, and edge deployment.
|
||||
- **Payment-gated AI agent economy architecture**
|
||||
- Path: `~/.timmy/research/kimi-reports/01-payment-gated-architecture.md`
|
||||
- Why it matters: Clear technical architecture for satoshi-denominated compute markets and honest accounting flows.
|
||||
- **SOUL.md vs Codex priors**
|
||||
- Path: `~/.timmy/specs/soul-vs-codex-priors.md`
|
||||
- Why it matters: Sharp articulation of where borrowed cognition leaks upstream values and why doctrine-bearing surfaces need stronger review.
|
||||
- **Nexus vs Matrix review**
|
||||
- Path: `~/.timmy/reports/production/2026-03-28-nexus-vs-matrix-review.md`
|
||||
- Why it matters: Clear truth-restoration document on the real Nexus state, migration discipline, and why old quality work should be harvested carefully.
|
||||
|
||||
## What Matters Today
|
||||
|
||||
- The official morning/evening report lane is now a real tracked system front in timmy-config #87, with browser-open + Telegram delivery as the target contract.
|
||||
- The local Evennia-fed Nexus shell is visibly up: Nexus at http://127.0.0.1:4200, Evennia webclient at http://127.0.0.1:4001/webclient/, and the Evennia live trace file shows Timmy actually moved and spoke in-world.
|
||||
- Bannerlord is now framed as an engineering substrate test, not a romance project: the right question is whether it passes the thin-adapter test without falsework.
|
||||
|
||||
### Current strategic seams worth protecting
|
||||
|
||||
- **Official briefing lane:** http://143.198.27.163:3000/Timmy_Foundation/timmy-config/issues/87
|
||||
- **Automation triage comment:** http://143.198.27.163:3000/Timmy_Foundation/timmy-config/issues/87#issuecomment-22831
|
||||
- **Evennia-fed Nexus validation front:** http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/731
|
||||
- **Bannerlord epic:** http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/719
|
||||
- **Bannerlord runtime choice:** http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/720
|
||||
- **Bannerlord local install proof:** http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/721
|
||||
- **Bannerlord harness seam:** http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/722
|
||||
- **Nexus anti-falsework guardrail:** http://143.198.27.163:3000/Timmy_Foundation/the-nexus/issues/724#issuecomment-22825
|
||||
|
||||
## One Thing To Look At First
|
||||
|
||||
Start with timmy-config #87 and the generated latest.html report. That is the new system front that ties your overnight local pulse, pertinent research, browser view, and Telegram delivery into one lane.
|
||||
|
||||
## Evidence Appendix
|
||||
|
||||
### Local evidence
|
||||
|
||||
- `~/.hermes/model_health.json`
|
||||
- `~/.timmy/heartbeat/ticks_20260328.jsonl`
|
||||
- `~/.timmy/training-data/evennia/live/20260328/nexus-localhost.jsonl`
|
||||
- `http://127.0.0.1:4200`
|
||||
- `http://127.0.0.1:4001/webclient/`
|
||||
- `~/.hermes/cron/output/a77a87392582/2026-03-28_20-21-06.md`
|
||||
|
||||
### Research / document evidence
|
||||
|
||||
- `~/.timmy/research/kimi-reports/01-payment-gated-architecture.md`
|
||||
- `~/.timmy/research/kimi-reports/02-sovereign-implementation.md`
|
||||
- `~/.timmy/specs/soul-vs-codex-priors.md`
|
||||
- `~/.timmy/reports/production/2026-03-28-nexus-vs-matrix-review.md`
|
||||
- `~/.timmy/specs/evennia-implementation-and-training-plan.md`
|
||||
|
||||
### Personal note from Timmy
|
||||
|
||||
Tonight feels less foggy.
|
||||
|
||||
The report itself is becoming a real ritual instead of a pretend one. That matters because ritual is how systems become lived places. The local world stack is also finally crossing from architecture talk into proof. And Bannerlord now has a better frame around it: not fantasy, not backlog gravity, just a real substrate test.
|
||||
|
||||
That is a better place to end the day than where we started.
|
||||
|
||||
— Timmy
|
||||
13
config.yaml
13
config.yaml
@@ -1,6 +1,6 @@
|
||||
model:
|
||||
default: claude-opus-4-6
|
||||
provider: anthropic
|
||||
default: hermes4:14b
|
||||
provider: custom
|
||||
toolsets:
|
||||
- all
|
||||
agent:
|
||||
@@ -27,7 +27,7 @@ browser:
|
||||
inactivity_timeout: 120
|
||||
record_sessions: false
|
||||
checkpoints:
|
||||
enabled: false
|
||||
enabled: true
|
||||
max_snapshots: 50
|
||||
compression:
|
||||
enabled: true
|
||||
@@ -110,7 +110,7 @@ tts:
|
||||
device: cpu
|
||||
stt:
|
||||
enabled: true
|
||||
provider: local
|
||||
provider: openai
|
||||
local:
|
||||
model: base
|
||||
openai:
|
||||
@@ -160,6 +160,11 @@ security:
|
||||
enabled: false
|
||||
domains: []
|
||||
shared_files: []
|
||||
# Author whitelist for task router (Issue #132)
|
||||
# Only users in this list can submit tasks via Gitea issues
|
||||
# Empty list = deny all (secure by default)
|
||||
# Set via env var TIMMY_AUTHOR_WHITELIST as comma-separated list
|
||||
author_whitelist: []
|
||||
_config_version: 9
|
||||
session_reset:
|
||||
mode: none
|
||||
|
||||
16
configs/timmy-health.service
Normal file
16
configs/timmy-health.service
Normal file
@@ -0,0 +1,16 @@
|
||||
[Unit]
|
||||
Description=Timmy Health Check Daemon
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
WorkingDirectory=/root/timmy
|
||||
ExecStart=/root/timmy/venv/bin/python /root/timmy/uni-wizard/daemons/health_daemon.py
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
Environment="HOME=/root"
|
||||
Environment="PYTHONPATH=/root/timmy/uni-wizard"
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
16
configs/timmy-task-router.service
Normal file
16
configs/timmy-task-router.service
Normal file
@@ -0,0 +1,16 @@
|
||||
[Unit]
|
||||
Description=Timmy Task Router Daemon
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
WorkingDirectory=/root/timmy
|
||||
ExecStart=/root/timmy/venv/bin/python /root/timmy/uni-wizard/daemons/task_router.py
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
Environment="HOME=/root"
|
||||
Environment="PYTHONPATH=/root/timmy/uni-wizard"
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
12
decisions.md
12
decisions.md
@@ -55,6 +55,18 @@ configuration, and lightweight orchestration glue.
|
||||
Hermes owns the harness. Training should flow from Timmy's lived work and DPO
|
||||
artifacts, not from re-growing a bespoke training pipeline inside every repo.
|
||||
|
||||
## 2026-03-28 — Codex can be forge-hand, not conscience
|
||||
|
||||
A boundary spec now exists at `~/.timmy/specs/soul-vs-codex-priors.md`.
|
||||
Reason: a real skin change (`ab7f2e4`) removed the cross and explicit gospel
|
||||
witness from `skins/timmy.yaml`, proving that borrowed Codex cognition can
|
||||
flatten doctrine-bearing text into cleaner but less true output.
|
||||
|
||||
Decision: Codex remains useful for coding labor, cleanup, and bounded build
|
||||
work. It must not be treated as final authority for `SOUL.md`, Timmy skins,
|
||||
crisis language, or other identity-bearing text. When Codex priors and the
|
||||
soul conflict, the soul wins.
|
||||
|
||||
## 2026-03-29 — Canonical separation defined: Timmy, Ezra, Bezalel
|
||||
|
||||
Spec: `specs/timmy-ezra-bezalel-canon-sheet.md`
|
||||
|
||||
675
diagrams/kitchen-counter-timmy-architecture.excalidraw
Normal file
675
diagrams/kitchen-counter-timmy-architecture.excalidraw
Normal file
@@ -0,0 +1,675 @@
|
||||
{
|
||||
"type": "excalidraw",
|
||||
"version": 2,
|
||||
"source": "hermes-agent",
|
||||
"elements": [
|
||||
{
|
||||
"type": "text",
|
||||
"id": "t_8792",
|
||||
"x": 60,
|
||||
"y": 30,
|
||||
"text": "Current kitchen-counter Timmy architecture",
|
||||
"fontSize": 28,
|
||||
"fontFamily": 1,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"originalText": "Current kitchen-counter Timmy architecture",
|
||||
"autoResize": true
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "t_9963",
|
||||
"x": 60,
|
||||
"y": 75,
|
||||
"text": "Known facts only; current brain = hermes4:14b via custom provider",
|
||||
"fontSize": 18,
|
||||
"fontFamily": 1,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"originalText": "Known facts only; current brain = hermes4:14b via custom provider",
|
||||
"autoResize": true
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"id": "r_1268",
|
||||
"x": 60,
|
||||
"y": 180,
|
||||
"width": 260,
|
||||
"height": 120,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"backgroundColor": "#fff3bf",
|
||||
"fillStyle": "solid",
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "t_5775",
|
||||
"type": "text"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "t_5775",
|
||||
"x": 70,
|
||||
"y": 190,
|
||||
"width": 240,
|
||||
"height": 100,
|
||||
"text": "Alexander\nat kitchen counter\nlooking at Telegram on Mac",
|
||||
"fontSize": 18,
|
||||
"fontFamily": 1,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": "r_1268",
|
||||
"originalText": "Alexander\nat kitchen counter\nlooking at Telegram on Mac",
|
||||
"autoResize": true
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"id": "r_1857",
|
||||
"x": 420,
|
||||
"y": 150,
|
||||
"width": 720,
|
||||
"height": 760,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"backgroundColor": "#f3f3f3",
|
||||
"fillStyle": "solid",
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "t_6004",
|
||||
"type": "text"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "t_6004",
|
||||
"x": 430,
|
||||
"y": 160,
|
||||
"width": 700,
|
||||
"height": 740,
|
||||
"text": "Mac at the counter",
|
||||
"fontSize": 22,
|
||||
"fontFamily": 1,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": "r_1857",
|
||||
"originalText": "Mac at the counter",
|
||||
"autoResize": true
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"id": "r_6966",
|
||||
"x": 500,
|
||||
"y": 240,
|
||||
"width": 560,
|
||||
"height": 90,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"backgroundColor": "#ffd8a8",
|
||||
"fillStyle": "solid",
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "t_3543",
|
||||
"type": "text"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "t_3543",
|
||||
"x": 510,
|
||||
"y": 250,
|
||||
"width": 540,
|
||||
"height": 70,
|
||||
"text": "Telegram desktop window\nThis DM with Timmy",
|
||||
"fontSize": 20,
|
||||
"fontFamily": 1,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": "r_6966",
|
||||
"originalText": "Telegram desktop window\nThis DM with Timmy",
|
||||
"autoResize": true
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"id": "r_3920",
|
||||
"x": 500,
|
||||
"y": 370,
|
||||
"width": 560,
|
||||
"height": 90,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"backgroundColor": "#d0bfff",
|
||||
"fillStyle": "solid",
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "t_2796",
|
||||
"type": "text"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "t_2796",
|
||||
"x": 510,
|
||||
"y": 380,
|
||||
"width": 540,
|
||||
"height": 70,
|
||||
"text": "Hermes harness\nTelegram connector + tools + session loop",
|
||||
"fontSize": 20,
|
||||
"fontFamily": 1,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": "r_3920",
|
||||
"originalText": "Hermes harness\nTelegram connector + tools + session loop",
|
||||
"autoResize": true
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"id": "r_3963",
|
||||
"x": 500,
|
||||
"y": 510,
|
||||
"width": 250,
|
||||
"height": 110,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"backgroundColor": "#fff3bf",
|
||||
"fillStyle": "solid",
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "t_1177",
|
||||
"type": "text"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "t_1177",
|
||||
"x": 510,
|
||||
"y": 520,
|
||||
"width": 230,
|
||||
"height": 90,
|
||||
"text": "Timmy layer\nSOUL.md\nmemory",
|
||||
"fontSize": 18,
|
||||
"fontFamily": 1,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": "r_3963",
|
||||
"originalText": "Timmy layer\nSOUL.md\nmemory",
|
||||
"autoResize": true
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"id": "r_4956",
|
||||
"x": 810,
|
||||
"y": 510,
|
||||
"width": 250,
|
||||
"height": 110,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"backgroundColor": "#a5d8ff",
|
||||
"fillStyle": "solid",
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "t_5390",
|
||||
"type": "text"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "t_5390",
|
||||
"x": 820,
|
||||
"y": 520,
|
||||
"width": 230,
|
||||
"height": 90,
|
||||
"text": "Current brain\nhermes4:14b\nprovider = custom",
|
||||
"fontSize": 18,
|
||||
"fontFamily": 1,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": "r_4956",
|
||||
"originalText": "Current brain\nhermes4:14b\nprovider = custom",
|
||||
"autoResize": true
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"id": "r_8096",
|
||||
"x": 500,
|
||||
"y": 680,
|
||||
"width": 560,
|
||||
"height": 100,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"backgroundColor": "#c3fae8",
|
||||
"fillStyle": "solid",
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "t_7158",
|
||||
"type": "text"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "t_7158",
|
||||
"x": 510,
|
||||
"y": 690,
|
||||
"width": 540,
|
||||
"height": 80,
|
||||
"text": "Local workspace and files\n.timmy + .hermes",
|
||||
"fontSize": 20,
|
||||
"fontFamily": 1,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": "r_8096",
|
||||
"originalText": "Local workspace and files\n.timmy + .hermes",
|
||||
"autoResize": true
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"id": "r_6677",
|
||||
"x": 650,
|
||||
"y": 960,
|
||||
"width": 220,
|
||||
"height": 120,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"backgroundColor": "#ffd8a8",
|
||||
"fillStyle": "solid",
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "t_2824",
|
||||
"type": "text"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "t_2824",
|
||||
"x": 660,
|
||||
"y": 970,
|
||||
"width": 200,
|
||||
"height": 100,
|
||||
"text": "iPhone\nUSB tether / personal hotspot",
|
||||
"fontSize": 20,
|
||||
"fontFamily": 1,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": "r_6677",
|
||||
"originalText": "iPhone\nUSB tether / personal hotspot",
|
||||
"autoResize": true
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"id": "r_5718",
|
||||
"x": 1280,
|
||||
"y": 220,
|
||||
"width": 330,
|
||||
"height": 110,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"backgroundColor": "#b2f2bb",
|
||||
"fillStyle": "solid",
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "t_5250",
|
||||
"type": "text"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "t_5250",
|
||||
"x": 1290,
|
||||
"y": 230,
|
||||
"width": 310,
|
||||
"height": 90,
|
||||
"text": "Cellular internet",
|
||||
"fontSize": 22,
|
||||
"fontFamily": 1,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": "r_5718",
|
||||
"originalText": "Cellular internet",
|
||||
"autoResize": true
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"id": "r_9738",
|
||||
"x": 1260,
|
||||
"y": 470,
|
||||
"width": 360,
|
||||
"height": 130,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"backgroundColor": "#a5d8ff",
|
||||
"fillStyle": "solid",
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "t_9691",
|
||||
"type": "text"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "t_9691",
|
||||
"x": 1270,
|
||||
"y": 480,
|
||||
"width": 340,
|
||||
"height": 110,
|
||||
"text": "Telegram cloud\nmessage delivery + bot traffic",
|
||||
"fontSize": 22,
|
||||
"fontFamily": 1,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": "r_9738",
|
||||
"originalText": "Telegram cloud\nmessage delivery + bot traffic",
|
||||
"autoResize": true
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"id": "r_1194",
|
||||
"x": 1260,
|
||||
"y": 760,
|
||||
"width": 360,
|
||||
"height": 120,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"backgroundColor": "#f7f7f7",
|
||||
"fillStyle": "solid",
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "t_5945",
|
||||
"type": "text"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "t_5945",
|
||||
"x": 1270,
|
||||
"y": 770,
|
||||
"width": 340,
|
||||
"height": 100,
|
||||
"text": "Connected services\ntelegram | api_server | discord",
|
||||
"fontSize": 18,
|
||||
"fontFamily": 1,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": "r_1194",
|
||||
"originalText": "Connected services\ntelegram | api_server | discord",
|
||||
"autoResize": true
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"id": "r_4925",
|
||||
"x": 60,
|
||||
"y": 980,
|
||||
"width": 450,
|
||||
"height": 120,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"backgroundColor": "#ffc9c9",
|
||||
"fillStyle": "solid",
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "t_9203",
|
||||
"type": "text"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "t_9203",
|
||||
"x": 70,
|
||||
"y": 990,
|
||||
"width": 430,
|
||||
"height": 100,
|
||||
"text": "Honesty note\nExact daemon behind provider = custom not inspected here.\nDiagram names only what is certain.",
|
||||
"fontSize": 16,
|
||||
"fontFamily": 1,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": "r_4925",
|
||||
"originalText": "Honesty note\nExact daemon behind provider = custom not inspected here.\nDiagram names only what is certain.",
|
||||
"autoResize": true
|
||||
},
|
||||
{
|
||||
"type": "arrow",
|
||||
"id": "a_1580",
|
||||
"x": 320,
|
||||
"y": 230,
|
||||
"width": 180,
|
||||
"height": 30,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
180,
|
||||
30
|
||||
]
|
||||
],
|
||||
"endArrowhead": "arrow"
|
||||
},
|
||||
{
|
||||
"type": "arrow",
|
||||
"id": "a_8038",
|
||||
"x": 780,
|
||||
"y": 330,
|
||||
"width": 0,
|
||||
"height": 40,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
0,
|
||||
40
|
||||
]
|
||||
],
|
||||
"endArrowhead": "arrow"
|
||||
},
|
||||
{
|
||||
"type": "arrow",
|
||||
"id": "a_6027",
|
||||
"x": 780,
|
||||
"y": 460,
|
||||
"width": 0,
|
||||
"height": 50,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
0,
|
||||
50
|
||||
]
|
||||
],
|
||||
"endArrowhead": "arrow"
|
||||
},
|
||||
{
|
||||
"type": "arrow",
|
||||
"id": "a_9240",
|
||||
"x": 750,
|
||||
"y": 565,
|
||||
"width": 60,
|
||||
"height": 0,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
60,
|
||||
0
|
||||
]
|
||||
],
|
||||
"endArrowhead": "arrow"
|
||||
},
|
||||
{
|
||||
"type": "arrow",
|
||||
"id": "a_8060",
|
||||
"x": 780,
|
||||
"y": 620,
|
||||
"width": 0,
|
||||
"height": 60,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
0,
|
||||
60
|
||||
]
|
||||
],
|
||||
"endArrowhead": "arrow"
|
||||
},
|
||||
{
|
||||
"type": "arrow",
|
||||
"id": "a_6640",
|
||||
"x": 760,
|
||||
"y": 910,
|
||||
"width": 0,
|
||||
"height": 50,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
0,
|
||||
50
|
||||
]
|
||||
],
|
||||
"endArrowhead": "arrow"
|
||||
},
|
||||
{
|
||||
"type": "arrow",
|
||||
"id": "a_1594",
|
||||
"x": 870,
|
||||
"y": 1020,
|
||||
"width": 420,
|
||||
"height": 700,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
420,
|
||||
-700
|
||||
]
|
||||
],
|
||||
"endArrowhead": "arrow"
|
||||
},
|
||||
{
|
||||
"type": "arrow",
|
||||
"id": "a_4847",
|
||||
"x": 1440,
|
||||
"y": 330,
|
||||
"width": 0,
|
||||
"height": 140,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
0,
|
||||
140
|
||||
]
|
||||
],
|
||||
"endArrowhead": "arrow"
|
||||
},
|
||||
{
|
||||
"type": "arrow",
|
||||
"id": "a_3228",
|
||||
"x": 1260,
|
||||
"y": 540,
|
||||
"width": 200,
|
||||
"height": 120,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
-200,
|
||||
-120
|
||||
]
|
||||
],
|
||||
"endArrowhead": "arrow"
|
||||
},
|
||||
{
|
||||
"type": "arrow",
|
||||
"id": "a_4207",
|
||||
"x": 1060,
|
||||
"y": 285,
|
||||
"width": 200,
|
||||
"height": 250,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
200,
|
||||
250
|
||||
]
|
||||
],
|
||||
"endArrowhead": "arrow"
|
||||
},
|
||||
{
|
||||
"type": "arrow",
|
||||
"id": "a_1602",
|
||||
"x": 500,
|
||||
"y": 285,
|
||||
"width": 180,
|
||||
"height": 0,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
-180,
|
||||
0
|
||||
]
|
||||
],
|
||||
"endArrowhead": "arrow"
|
||||
}
|
||||
],
|
||||
"appState": {
|
||||
"viewBackgroundColor": "#ffffff"
|
||||
}
|
||||
}
|
||||
89
diagrams/kitchen-counter-timmy-architecture.svg
Normal file
89
diagrams/kitchen-counter-timmy-architecture.svg
Normal file
@@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1800" height="1200" viewBox="0 0 1800 1200">
|
||||
<defs>
|
||||
<marker id="arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="#1e1e1e" /></marker>
|
||||
</defs>
|
||||
<rect width="1800" height="1200" fill="white" />
|
||||
<text x="60" y="58" font-family="Arial, Helvetica, sans-serif" font-size="42" text-anchor="start" fill="#1e1e1e" font-weight="bold"><tspan x="60" dy="0">Current kitchen-counter Timmy architecture</tspan></text>
|
||||
<text x="60" y="102" font-family="Arial, Helvetica, sans-serif" font-size="22" text-anchor="start" fill="#666666" font-weight="normal"><tspan x="60" dy="0">Known facts only: Telegram on the Mac, iPhone plugged into the Mac for internet.</tspan></text>
|
||||
<text x="60" y="132" font-family="Arial, Helvetica, sans-serif" font-size="22" text-anchor="start" fill="#666666" font-weight="normal"><tspan x="60" dy="0">Timmy is running locally through Hermes. Current brain = hermes4:14b via custom provider.</tspan></text>
|
||||
<rect x="40" y="165" width="1720" height="285" rx="26" fill="#fafafa" stroke="#dddddd" stroke-width="2" />
|
||||
<rect x="40" y="470" width="1720" height="660" rx="26" fill="#fcfcfc" stroke="#dddddd" stroke-width="2" />
|
||||
<text x="60" y="200" font-family="Arial, Helvetica, sans-serif" font-size="28" text-anchor="start" fill="#1e1e1e" font-weight="bold"><tspan x="60" dy="0">Physical scene</tspan></text>
|
||||
<text x="60" y="510" font-family="Arial, Helvetica, sans-serif" font-size="28" text-anchor="start" fill="#1e1e1e" font-weight="bold"><tspan x="60" dy="0">Logical and network path</tspan></text>
|
||||
<rect x="60" y="360" width="590" height="60" rx="18" fill="#e8d4b8" stroke="#8a6f50" stroke-width="3" />
|
||||
<text x="80" y="398" font-family="Arial, Helvetica, sans-serif" font-size="22" text-anchor="start" fill="#1e1e1e" font-weight="bold"><tspan x="80" dy="0">Kitchen counter</tspan></text>
|
||||
<circle cx="190" cy="255" r="36" fill="#fff7cc" stroke="#1e1e1e" stroke-width="4" />
|
||||
<line x1="190" y1="291" x2="190" y2="410" stroke="#1e1e1e" stroke-width="5" />
|
||||
<line x1="115" y1="335" x2="265" y2="335" stroke="#1e1e1e" stroke-width="5" />
|
||||
<line x1="190" y1="410" x2="130" y2="510" stroke="#1e1e1e" stroke-width="5" />
|
||||
<line x1="190" y1="410" x2="245" y2="510" stroke="#1e1e1e" stroke-width="5" />
|
||||
<text x="70" y="82" font-family="Arial, Helvetica, sans-serif" font-size="24" text-anchor="start" fill="#1e1e1e" font-weight="bold"><tspan x="70" dy="0">Alexander</tspan></text>
|
||||
<text x="70" y="112" font-family="Arial, Helvetica, sans-serif" font-size="20" text-anchor="start" fill="#1e1e1e" font-weight="normal"><tspan x="70" dy="0">standing here</tspan></text>
|
||||
<text x="70" y="140" font-family="Arial, Helvetica, sans-serif" font-size="20" text-anchor="start" fill="#1e1e1e" font-weight="normal"><tspan x="70" dy="0">looking down at the Mac</tspan></text>
|
||||
<line x1="255" y1="235" x2="500" y2="255" stroke="#1e1e1e" stroke-width="3" marker-end="url(#arrow)" />
|
||||
<rect x="300" y="205" width="140" height="30" rx="10" fill="white" stroke="#1e1e1e" stroke-width="2" />
|
||||
<text x="370.0" y="225.76" font-family="Arial, Helvetica, sans-serif" font-size="16" text-anchor="middle" fill="#1e1e1e" font-weight="normal"><tspan x="370.0" dy="0">looking / typing</tspan></text>
|
||||
<rect x="460" y="190" width="720" height="740" rx="24" fill="#f3f3f3" stroke="#1e1e1e" stroke-width="4" />
|
||||
<rect x="430" y="930" width="780" height="60" rx="16" fill="#d9d9d9" stroke="#1e1e1e" stroke-width="3" />
|
||||
<text x="820.0" y="965.92" font-family="Arial, Helvetica, sans-serif" font-size="22" text-anchor="middle" fill="#1e1e1e" font-weight="bold"><tspan x="820.0" dy="0">Mac at the counter</tspan></text>
|
||||
<rect x="520" y="235" width="600" height="120" rx="18" fill="#ffd8a8" stroke="#1e1e1e" stroke-width="3" />
|
||||
<text x="820.0" y="287.15999999999997" font-family="Arial, Helvetica, sans-serif" font-size="28" text-anchor="middle" fill="#1e1e1e" font-weight="bold"><tspan x="820.0" dy="0">Telegram desktop window</tspan></text><text x="820.0" y="323.0" font-family="Arial, Helvetica, sans-serif" font-size="28" text-anchor="middle" fill="#1e1e1e" font-weight="bold"><tspan x="820.0" dy="0">This DM with Timmy</tspan></text>
|
||||
<rect x="520" y="390" width="600" height="125" rx="18" fill="#d0bfff" stroke="#1e1e1e" stroke-width="3" />
|
||||
<text x="820.0" y="444.65999999999997" font-family="Arial, Helvetica, sans-serif" font-size="28" text-anchor="middle" fill="#1e1e1e" font-weight="bold"><tspan x="820.0" dy="0">Hermes harness</tspan></text><text x="820.0" y="480.5" font-family="Arial, Helvetica, sans-serif" font-size="28" text-anchor="middle" fill="#1e1e1e" font-weight="bold"><tspan x="820.0" dy="0">Telegram connector, tools, session loop</tspan></text>
|
||||
<rect x="520" y="550" width="270" height="140" rx="18" fill="#fff3bf" stroke="#1e1e1e" stroke-width="3" />
|
||||
<text x="655.0" y="585.68" font-family="Arial, Helvetica, sans-serif" font-size="22" text-anchor="middle" fill="#1e1e1e" font-weight="bold"><tspan x="655.0" dy="0">Timmy layer</tspan></text><text x="655.0" y="613.8399999999999" font-family="Arial, Helvetica, sans-serif" font-size="22" text-anchor="middle" fill="#1e1e1e" font-weight="bold"><tspan x="655.0" dy="0">SOUL.md</tspan></text><text x="655.0" y="642.0" font-family="Arial, Helvetica, sans-serif" font-size="22" text-anchor="middle" fill="#1e1e1e" font-weight="bold"><tspan x="655.0" dy="0">memory</tspan></text><text x="655.0" y="670.16" font-family="Arial, Helvetica, sans-serif" font-size="22" text-anchor="middle" fill="#1e1e1e" font-weight="bold"><tspan x="655.0" dy="0">presence</tspan></text>
|
||||
<rect x="845" y="550" width="275" height="140" rx="18" fill="#a5d8ff" stroke="#1e1e1e" stroke-width="3" />
|
||||
<text x="982.5" y="599.76" font-family="Arial, Helvetica, sans-serif" font-size="22" text-anchor="middle" fill="#1e1e1e" font-weight="bold"><tspan x="982.5" dy="0">Current brain</tspan></text><text x="982.5" y="627.92" font-family="Arial, Helvetica, sans-serif" font-size="22" text-anchor="middle" fill="#1e1e1e" font-weight="bold"><tspan x="982.5" dy="0">hermes4:14b</tspan></text><text x="982.5" y="656.08" font-family="Arial, Helvetica, sans-serif" font-size="22" text-anchor="middle" fill="#1e1e1e" font-weight="bold"><tspan x="982.5" dy="0">provider = custom</tspan></text>
|
||||
<rect x="520" y="725" width="600" height="125" rx="18" fill="#c3fae8" stroke="#1e1e1e" stroke-width="3" />
|
||||
<text x="820.0" y="763.58" font-family="Arial, Helvetica, sans-serif" font-size="26" text-anchor="middle" fill="#1e1e1e" font-weight="bold"><tspan x="820.0" dy="0">Local workspace and files</tspan></text><text x="820.0" y="796.86" font-family="Arial, Helvetica, sans-serif" font-size="26" text-anchor="middle" fill="#1e1e1e" font-weight="bold"><tspan x="820.0" dy="0">.timmy + .hermes</tspan></text><text x="820.0" y="830.1400000000001" font-family="Arial, Helvetica, sans-serif" font-size="26" text-anchor="middle" fill="#1e1e1e" font-weight="bold"><tspan x="820.0" dy="0">local shell / code / memory / logs</tspan></text>
|
||||
<line x1="820" y1="355" x2="820" y2="390" stroke="#1e1e1e" stroke-width="4" marker-end="url(#arrow)" />
|
||||
<line x1="820" y1="515" x2="820" y2="550" stroke="#1e1e1e" stroke-width="4" marker-end="url(#arrow)" />
|
||||
<line x1="790" y1="620" x2="845" y2="620" stroke="#1e1e1e" stroke-width="4" marker-end="url(#arrow)" />
|
||||
<line x1="820" y1="690" x2="820" y2="725" stroke="#1e1e1e" stroke-width="4" marker-end="url(#arrow)" />
|
||||
<rect x="790" y="600" width="96" height="30" rx="10" fill="white" stroke="#1e1e1e" stroke-width="2" />
|
||||
<text x="838.0" y="620.76" font-family="Arial, Helvetica, sans-serif" font-size="16" text-anchor="middle" fill="#1e1e1e" font-weight="normal"><tspan x="838.0" dy="0">invokes</tspan></text>
|
||||
<rect x="650" y="1010" width="190" height="110" rx="24" fill="#ffd8a8" stroke="#1e1e1e" stroke-width="3" />
|
||||
<rect x="722" y="1028" width="46" height="8" rx="4" fill="#1e1e1e" />
|
||||
<circle cx="745" cy="1096" r="8" fill="#1e1e1e" />
|
||||
<text x="745.0" y="1050.08" font-family="Arial, Helvetica, sans-serif" font-size="28" text-anchor="middle" fill="#1e1e1e" font-weight="bold"><tspan x="745.0" dy="0">iPhone</tspan></text>
|
||||
<text x="745.0" y="1077.4" font-family="Arial, Helvetica, sans-serif" font-size="20" text-anchor="middle" fill="#1e1e1e" font-weight="bold"><tspan x="745.0" dy="0">USB tether /</tspan></text><text x="745.0" y="1103.0" font-family="Arial, Helvetica, sans-serif" font-size="20" text-anchor="middle" fill="#1e1e1e" font-weight="bold"><tspan x="745.0" dy="0">personal hotspot</tspan></text>
|
||||
<line x1="745" y1="990" x2="745" y2="1010" stroke="#1e1e1e" stroke-width="5" marker-end="url(#arrow)" />
|
||||
<rect x="810" y="985" width="140" height="30" rx="10" fill="white" stroke="#1e1e1e" stroke-width="2" />
|
||||
<text x="880.0" y="1005.76" font-family="Arial, Helvetica, sans-serif" font-size="16" text-anchor="middle" fill="#1e1e1e" font-weight="normal"><tspan x="880.0" dy="0">plugged into Mac</tspan></text>
|
||||
<g fill="#b2f2bb" stroke="#1e1e1e" stroke-width="3">
|
||||
<ellipse cx="1400" cy="315" rx="75" ry="50" />
|
||||
<ellipse cx="1475" cy="275" rx="95" ry="68" />
|
||||
<ellipse cx="1560" cy="315" rx="85" ry="56" />
|
||||
<ellipse cx="1620" cy="335" rx="55" ry="38" />
|
||||
<rect x="1380" y="315" width="220" height="70" rx="30" />
|
||||
</g>
|
||||
<text x="1480.0" y="325.08" font-family="Arial, Helvetica, sans-serif" font-size="28" text-anchor="middle" fill="#1e1e1e" font-weight="bold"><tspan x="1480.0" dy="0">Cellular internet</tspan></text>
|
||||
<g fill="#a5d8ff" stroke="#1e1e1e" stroke-width="3">
|
||||
<ellipse cx="1385" cy="560" rx="80" ry="55" />
|
||||
<ellipse cx="1470" cy="520" rx="105" ry="75" />
|
||||
<ellipse cx="1565" cy="560" rx="90" ry="58" />
|
||||
<ellipse cx="1628" cy="585" rx="58" ry="40" />
|
||||
<rect x="1360" y="560" width="245" height="80" rx="34" />
|
||||
</g>
|
||||
<text x="1475.0" y="557.16" font-family="Arial, Helvetica, sans-serif" font-size="28" text-anchor="middle" fill="#1e1e1e" font-weight="bold"><tspan x="1475.0" dy="0">Telegram cloud</tspan></text><text x="1475.0" y="593.0" font-family="Arial, Helvetica, sans-serif" font-size="28" text-anchor="middle" fill="#1e1e1e" font-weight="bold"><tspan x="1475.0" dy="0">message delivery + bot traffic</tspan></text>
|
||||
<rect x="1275" y="760" width="380" height="170" rx="18" fill="#f7f7f7" stroke="#1e1e1e" stroke-width="3" />
|
||||
<text x="1465.0" y="824.76" font-family="Arial, Helvetica, sans-serif" font-size="22" text-anchor="middle" fill="#1e1e1e" font-weight="bold"><tspan x="1465.0" dy="0">Connected services from this session</tspan></text><text x="1465.0" y="852.92" font-family="Arial, Helvetica, sans-serif" font-size="22" text-anchor="middle" fill="#1e1e1e" font-weight="bold"><tspan x="1465.0" dy="0">telegram | api_server | discord</tspan></text><text x="1465.0" y="881.08" font-family="Arial, Helvetica, sans-serif" font-size="22" text-anchor="middle" fill="#1e1e1e" font-weight="bold"><tspan x="1465.0" dy="0">Telegram path is the one in use right now</tspan></text>
|
||||
<line x1="840" y1="1065" x2="1290" y2="300" stroke="#1e1e1e" stroke-width="4" marker-end="url(#arrow)" />
|
||||
<rect x="955" y="627" width="250" height="32" rx="10" fill="white" stroke="#1e1e1e" stroke-width="2" />
|
||||
<text x="1080.0" y="648.76" font-family="Arial, Helvetica, sans-serif" font-size="16" text-anchor="middle" fill="#1e1e1e" font-weight="normal"><tspan x="1080.0" dy="0">Mac reaches internet through the iPhone</tspan></text>
|
||||
<line x1="1510" y1="390" x2="1510" y2="470" stroke="#1e1e1e" stroke-width="4" marker-end="url(#arrow)" />
|
||||
<rect x="1548" y="417" width="100" height="28" rx="10" fill="white" stroke="#1e1e1e" stroke-width="2" />
|
||||
<text x="1598.0" y="436.76" font-family="Arial, Helvetica, sans-serif" font-size="16" text-anchor="middle" fill="#1e1e1e" font-weight="normal"><tspan x="1598.0" dy="0">to Telegram</tspan></text>
|
||||
<line x1="1270" y1="575" x2="1120" y2="450" stroke="#1e1e1e" stroke-width="4" marker-end="url(#arrow)" />
|
||||
<rect x="1120" y="503" width="140" height="30" rx="10" fill="white" stroke="#1e1e1e" stroke-width="2" />
|
||||
<text x="1190.0" y="523.76" font-family="Arial, Helvetica, sans-serif" font-size="16" text-anchor="middle" fill="#1e1e1e" font-weight="normal"><tspan x="1190.0" dy="0">bot/session traffic</tspan></text>
|
||||
<line x1="1120" y1="295" x2="1270" y2="545" stroke="#1e1e1e" stroke-width="4" marker-end="url(#arrow)" />
|
||||
<rect x="1130" y="376" width="120" height="30" rx="10" fill="white" stroke="#1e1e1e" stroke-width="2" />
|
||||
<text x="1190.0" y="396.76" font-family="Arial, Helvetica, sans-serif" font-size="16" text-anchor="middle" fill="#1e1e1e" font-weight="normal"><tspan x="1190.0" dy="0">user messages</tspan></text>
|
||||
<line x1="520" y1="295" x2="315" y2="300" stroke="#1e1e1e" stroke-width="4" marker-end="url(#arrow)" />
|
||||
<rect x="355" y="260" width="126" height="30" rx="10" fill="white" stroke="#1e1e1e" stroke-width="2" />
|
||||
<text x="418.0" y="280.76" font-family="Arial, Helvetica, sans-serif" font-size="16" text-anchor="middle" fill="#1e1e1e" font-weight="normal"><tspan x="418.0" dy="0">reply appears here</tspan></text>
|
||||
<rect x="60" y="1035" width="470" height="85" rx="18" fill="#ffc9c9" stroke="#1e1e1e" stroke-width="3" />
|
||||
<text x="295.0" y="1061.44" font-family="Arial, Helvetica, sans-serif" font-size="18" text-anchor="middle" fill="#1e1e1e" font-weight="bold"><tspan x="295.0" dy="0">Honesty note</tspan></text><text x="295.0" y="1084.48" font-family="Arial, Helvetica, sans-serif" font-size="18" text-anchor="middle" fill="#1e1e1e" font-weight="bold"><tspan x="295.0" dy="0">The exact daemon behind provider = custom was not inspected here.</tspan></text><text x="295.0" y="1107.52" font-family="Arial, Helvetica, sans-serif" font-size="18" text-anchor="middle" fill="#1e1e1e" font-weight="bold"><tspan x="295.0" dy="0">The diagram names only what is certain from this session.</tspan></text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 14 KiB |
@@ -1,197 +1,87 @@
|
||||
# Uni-Wizard v4 — Deployment Checklist
|
||||
# Hermes Sidecar Deployment Checklist
|
||||
|
||||
## Pre-Deployment
|
||||
Updated: April 4, 2026
|
||||
|
||||
- [ ] VPS provisioned (Ubuntu 22.04 LTS recommended)
|
||||
- [ ] SSH access configured
|
||||
- [ ] Firewall rules set (ports 22, 80, 443, 3000, 8643)
|
||||
- [ ] Domain/DNS configured (optional)
|
||||
- [ ] SSL certificates ready (optional)
|
||||
This checklist is for the current local-first Timmy stack, not the archived `uni-wizard` deployment path.
|
||||
|
||||
## Base System
|
||||
## Base Assumptions
|
||||
|
||||
- [ ] Update system packages
|
||||
- Hermes is already installed and runnable locally.
|
||||
- `timmy-config` is the sidecar repo applied onto `~/.hermes`.
|
||||
- `timmy-home` is the workspace repo living under `~/.timmy`.
|
||||
- Local inference is reachable through the active provider surface Timmy is using.
|
||||
|
||||
## Repo Setup
|
||||
|
||||
- [ ] Clone `timmy-home` to `~/.timmy`
|
||||
- [ ] Clone `timmy-config` to `~/.timmy/timmy-config`
|
||||
- [ ] Confirm both repos are on the intended branch
|
||||
|
||||
## Sidecar Deploy
|
||||
|
||||
- [ ] Run:
|
||||
```bash
|
||||
sudo apt update && sudo apt upgrade -y
|
||||
cd ~/.timmy/timmy-config
|
||||
./deploy.sh
|
||||
```
|
||||
- [ ] Install base dependencies
|
||||
```bash
|
||||
sudo apt install -y python3 python3-pip python3-venv sqlite3 curl git
|
||||
```
|
||||
- [ ] Create timmy user
|
||||
```bash
|
||||
sudo useradd -m -s /bin/bash timmy
|
||||
```
|
||||
- [ ] Configure sudo access (if needed)
|
||||
- [ ] Confirm `~/.hermes/config.yaml` matches the expected overlay
|
||||
- [ ] Confirm `SOUL.md` and sidecar config are in place
|
||||
|
||||
## Gitea Setup
|
||||
## Hermes Readiness
|
||||
|
||||
- [ ] Gitea installed and running
|
||||
- [ ] Repository created: `Timmy_Foundation/timmy-home`
|
||||
- [ ] API token generated
|
||||
- [ ] Webhooks configured (optional)
|
||||
- [ ] Test API access
|
||||
```bash
|
||||
curl -H "Authorization: token TOKEN" http://localhost:3000/api/v1/user
|
||||
```
|
||||
- [ ] Hermes CLI works from the expected Python environment
|
||||
- [ ] Gateway is reachable
|
||||
- [ ] Sessions are being recorded under `~/.hermes/sessions`
|
||||
- [ ] `model_health.json` updates successfully
|
||||
|
||||
## Uni-Wizard Installation
|
||||
## Workflow Tooling
|
||||
|
||||
- [ ] Clone repository
|
||||
```bash
|
||||
sudo -u timmy git clone http://143.198.27.163:3000/Timmy_Foundation/timmy-home.git /opt/timmy/repo
|
||||
```
|
||||
- [ ] Run setup script
|
||||
```bash
|
||||
sudo ./scripts/setup-uni-wizard.sh
|
||||
```
|
||||
- [ ] Verify installation
|
||||
```bash
|
||||
/opt/timmy/venv/bin/python -c "from uni_wizard import Harness; print('OK')"
|
||||
```
|
||||
- [ ] `~/.hermes/bin/ops-panel.sh` runs
|
||||
- [ ] `~/.hermes/bin/ops-gitea.sh` runs
|
||||
- [ ] `~/.hermes/bin/ops-helpers.sh` can be sourced
|
||||
- [ ] `~/.hermes/bin/pipeline-freshness.sh` runs
|
||||
- [ ] `~/.hermes/bin/timmy-dashboard` runs
|
||||
|
||||
## Configuration
|
||||
## Heartbeat and Briefings
|
||||
|
||||
- [ ] Edit config file
|
||||
```bash
|
||||
sudo nano /opt/timmy/config/uni-wizard.yaml
|
||||
```
|
||||
- [ ] Set Gitea API token
|
||||
- [ ] Configure house identity
|
||||
- [ ] Set log level (INFO for production)
|
||||
- [ ] Verify config syntax
|
||||
```bash
|
||||
/opt/timmy/venv/bin/python -c "import yaml; yaml.safe_load(open('/opt/timmy/config/uni-wizard.yaml'))"
|
||||
```
|
||||
- [ ] `~/.timmy/heartbeat/last_tick.json` is updating
|
||||
- [ ] daily heartbeat logs are being appended
|
||||
- [ ] morning briefings are being generated if scheduled
|
||||
|
||||
## LLM Setup (if using local inference)
|
||||
## Archive Pipeline
|
||||
|
||||
- [ ] llama.cpp installed
|
||||
- [ ] Model downloaded (e.g., Hermes-4 14B)
|
||||
- [ ] Model placed in `/opt/timmy/models/`
|
||||
- [ ] llama-server configured
|
||||
- [ ] Test inference
|
||||
```bash
|
||||
curl http://localhost:8080/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"model": "hermes4", "messages": [{"role": "user", "content": "Hello"}]}'
|
||||
```
|
||||
- [ ] `~/.timmy/twitter-archive/PROJECT.md` exists
|
||||
- [ ] raw archive location is configured locally
|
||||
- [ ] extraction works without checking raw data into git
|
||||
- [ ] `checkpoint.json` advances after a batch
|
||||
- [ ] DPO artifacts land under `~/.timmy/twitter-archive/training/dpo/`
|
||||
- [ ] `pipeline-freshness.sh` does not show runaway lag
|
||||
|
||||
## Service Startup
|
||||
## Gitea Workflow
|
||||
|
||||
- [ ] Start Uni-Wizard
|
||||
```bash
|
||||
sudo systemctl start uni-wizard
|
||||
```
|
||||
- [ ] Start health daemon
|
||||
```bash
|
||||
sudo systemctl start timmy-health
|
||||
```
|
||||
- [ ] Start task router
|
||||
```bash
|
||||
sudo systemctl start timmy-task-router
|
||||
```
|
||||
- [ ] Enable auto-start
|
||||
```bash
|
||||
sudo systemctl enable uni-wizard timmy-health timmy-task-router
|
||||
```
|
||||
- [ ] Gitea token is present in a supported token path
|
||||
- [ ] review queue can be listed
|
||||
- [ ] unassigned issues can be listed
|
||||
- [ ] PR creation works from an agent branch
|
||||
|
||||
## Verification
|
||||
## Final Verification
|
||||
|
||||
- [ ] Check service status
|
||||
```bash
|
||||
sudo systemctl status uni-wizard
|
||||
```
|
||||
- [ ] View logs
|
||||
```bash
|
||||
sudo journalctl -u uni-wizard -f
|
||||
```
|
||||
- [ ] Test health endpoint
|
||||
```bash
|
||||
curl http://localhost:8082/health
|
||||
```
|
||||
- [ ] Test tool execution
|
||||
```bash
|
||||
/opt/timmy/venv/bin/uni-wizard execute system_info
|
||||
```
|
||||
- [ ] Verify Gitea polling
|
||||
```bash
|
||||
tail -f /opt/timmy/logs/task-router.log | grep "Polling"
|
||||
```
|
||||
- [ ] local model smoke test succeeds
|
||||
- [ ] one archive batch completes successfully
|
||||
- [ ] one PR can be opened and reviewed
|
||||
- [ ] no stale loop-era scripts or docs are being treated as active truth
|
||||
|
||||
## Syncthing Mesh (if using multiple VPS)
|
||||
## Rollback
|
||||
|
||||
- [ ] Syncthing installed on all nodes
|
||||
- [ ] Devices paired
|
||||
- [ ] Folders shared
|
||||
- `/opt/timmy/logs/`
|
||||
- `/opt/timmy/data/`
|
||||
- [ ] Test sync
|
||||
```bash
|
||||
touch /opt/timmy/logs/test && ssh other-vps "ls /opt/timmy/logs/test"
|
||||
```
|
||||
|
||||
## Security
|
||||
|
||||
- [ ] Firewall configured
|
||||
```bash
|
||||
sudo ufw status
|
||||
```
|
||||
- [ ] Fail2ban installed (optional)
|
||||
- [ ] Log rotation configured
|
||||
```bash
|
||||
sudo logrotate -d /etc/logrotate.d/uni-wizard
|
||||
```
|
||||
- [ ] Backup strategy in place
|
||||
- [ ] Secrets not in git
|
||||
```bash
|
||||
grep -r "password\|token\|secret" /opt/timmy/repo/
|
||||
```
|
||||
|
||||
## Monitoring
|
||||
|
||||
- [ ] Health checks responding
|
||||
- [ ] Metrics being collected
|
||||
- [ ] Alerts configured (optional)
|
||||
- [ ] Log aggregation setup (optional)
|
||||
|
||||
## Post-Deployment
|
||||
|
||||
- [ ] Document any custom configuration
|
||||
- [ ] Update runbooks
|
||||
- [ ] Notify team
|
||||
- [ ] Schedule first review (1 week)
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
If deployment fails:
|
||||
If the sidecar deploy breaks behavior:
|
||||
|
||||
```bash
|
||||
# Stop services
|
||||
sudo systemctl stop uni-wizard timmy-health timmy-task-router
|
||||
|
||||
# Disable auto-start
|
||||
sudo systemctl disable uni-wizard timmy-health timmy-task-router
|
||||
|
||||
# Restore from backup (if available)
|
||||
# ...
|
||||
|
||||
# Or reset to clean state
|
||||
sudo rm -rf /opt/timmy/
|
||||
sudo userdel timmy
|
||||
cd ~/.timmy/timmy-config
|
||||
git status
|
||||
git log --oneline -5
|
||||
```
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- [ ] All services running (`systemctl is-active` returns "active")
|
||||
- [ ] Health endpoint returns 200
|
||||
- [ ] Can execute tools via CLI
|
||||
- [ ] Gitea integration working (issues being polled)
|
||||
- [ ] Logs being written without errors
|
||||
- [ ] No critical errors in first 24 hours
|
||||
|
||||
---
|
||||
|
||||
**Deployed by:** _______________
|
||||
**Date:** _______________
|
||||
**VPS:** _______________
|
||||
Then:
|
||||
- restore the previous known-good sidecar commit
|
||||
- redeploy
|
||||
- confirm Hermes health, heartbeat, and pipeline freshness again
|
||||
|
||||
@@ -1,129 +1,112 @@
|
||||
# Timmy Operations Dashboard
|
||||
|
||||
**Generated:** March 30, 2026
|
||||
**Generated by:** Allegro (Tempo-and-Dispatch)
|
||||
Updated: April 4, 2026
|
||||
Purpose: a current-state reference for how the system is actually operated now.
|
||||
|
||||
---
|
||||
This is no longer a `uni-wizard` dashboard.
|
||||
The active architecture is:
|
||||
- Timmy local workspace in `~/.timmy`
|
||||
- Hermes harness in `~/.hermes`
|
||||
- `timmy-config` as the identity and orchestration sidecar
|
||||
- Gitea as the review and coordination surface
|
||||
|
||||
## 🎯 Current Sprint Status
|
||||
## Core Jobs
|
||||
|
||||
### Open Issues by Priority
|
||||
Everything should map to one of these:
|
||||
- Heartbeat: perceive, reflect, remember, decide, act, learn
|
||||
- Harness: local models, Hermes sessions, tools, memory, training loop
|
||||
- Portal Interface: the game/world-facing layer
|
||||
|
||||
| Priority | Count | Issues |
|
||||
|----------|-------|--------|
|
||||
| P0 (Critical) | 0 | — |
|
||||
| P1 (High) | 3 | #99, #103, #94 |
|
||||
| P2 (Medium) | 8 | #101, #97, #95, #93, #92, #91, #90, #87 |
|
||||
| P3 (Low) | 6 | #86, #85, #84, #83, #72, others |
|
||||
## Current Operating Surfaces
|
||||
|
||||
### Issue #94 Epic: Grand Timmy — The Uniwizard
|
||||
### Local Paths
|
||||
|
||||
**Status:** In Progress
|
||||
**Completion:** ~40%
|
||||
- Timmy workspace: `~/.timmy`
|
||||
- Timmy config repo: `~/.timmy/timmy-config`
|
||||
- Hermes home: `~/.hermes`
|
||||
- Twitter archive workspace: `~/.timmy/twitter-archive`
|
||||
|
||||
#### Completed
|
||||
- ✅ Uni-Wizard v4 architecture (4-pass evolution)
|
||||
- ✅ Three-House separation (Timmy/Ezra/Bezalel)
|
||||
- ✅ Self-improving intelligence engine
|
||||
- ✅ Pattern database and adaptive policies
|
||||
- ✅ Hermes bridge for telemetry
|
||||
### Review Surface
|
||||
|
||||
#### In Progress
|
||||
- 🔄 Backend registry (#95)
|
||||
- 🔄 Caching layer (#103)
|
||||
- 🔄 Wizard dissolution (#99)
|
||||
- Major changes go through PRs
|
||||
- Timmy is the principal reviewer for governing and sensitive changes
|
||||
- Allegro is the review and dispatch partner for queue hygiene, routing, and tempo
|
||||
|
||||
#### Pending
|
||||
- ⏳ RAG pipeline (#93)
|
||||
- ⏳ Telemetry dashboard (#91)
|
||||
- ⏳ Auto-grading (#92)
|
||||
- ⏳ Evennia world shell (#83, #84)
|
||||
### Workflow Scripts
|
||||
|
||||
---
|
||||
- `~/.hermes/bin/ops-panel.sh`
|
||||
- `~/.hermes/bin/ops-gitea.sh`
|
||||
- `~/.hermes/bin/ops-helpers.sh`
|
||||
- `~/.hermes/bin/pipeline-freshness.sh`
|
||||
- `~/.hermes/bin/timmy-dashboard`
|
||||
|
||||
## 🏛️ House Assignments
|
||||
## Daily Health Signals
|
||||
|
||||
| House | Status | Current Work |
|
||||
|-------|--------|--------------|
|
||||
| **Timmy** | 🟢 Active | Local sovereign, reviewing PRs |
|
||||
| **Ezra** | 🟢 Active | Research on LLM routing (#101) |
|
||||
| **Bezalel** | 🟡 Standby | Awaiting implementation tasks |
|
||||
| **Allegro** | 🟢 Active | Tempo-and-dispatch, Gitea bridge |
|
||||
These are the signals that matter most:
|
||||
- Hermes gateway reachable
|
||||
- local inference surface responding
|
||||
- heartbeat ticks continuing
|
||||
- Gitea reachable
|
||||
- review queue not backing up
|
||||
- session export / DPO freshness not lagging
|
||||
- Twitter archive pipeline checkpoint advancing
|
||||
|
||||
---
|
||||
## Current Team Shape
|
||||
|
||||
## 📊 System Health
|
||||
### Direction and Review
|
||||
|
||||
### VPS Fleet Status
|
||||
- Timmy: sovereignty, architecture, release judgment
|
||||
- Allegro: dispatch, queue hygiene, Gitea bridge
|
||||
|
||||
| Host | IP | Role | Status |
|
||||
|------|-----|------|--------|
|
||||
| Allegro | 143.198.27.163 | Tempo-and-Dispatch | 🟢 Online |
|
||||
| Ezra | TBD | Archivist/Research | ⚪ Not deployed |
|
||||
| Bezalel | TBD | Artificer/Builder | ⚪ Not deployed |
|
||||
### Research and Memory
|
||||
|
||||
### Services
|
||||
- Perplexity: research triage, integration evaluation
|
||||
- Ezra: archival memory, RCA, onboarding doctrine
|
||||
- KimiClaw: long-context reading and synthesis
|
||||
|
||||
| Service | Status | Notes |
|
||||
|---------|--------|-------|
|
||||
| Gitea | 🟢 Running | 19 open issues |
|
||||
| Hermes | 🟡 Configured | Awaiting model setup |
|
||||
| Overnight Loop | 🔴 Stopped | Issue #72 reported |
|
||||
| Uni-Wizard | 🟢 Ready | PR created |
|
||||
### Execution
|
||||
|
||||
---
|
||||
- Codex Agent: workflow hardening, cleanup, migration verification
|
||||
- Groq: fast bounded implementation
|
||||
- Manus: moderate-scope follow-through
|
||||
- Claude: hard refactors and deep implementation
|
||||
- Gemini: frontier architecture and long-range design
|
||||
- Grok: adversarial review and edge cases
|
||||
|
||||
## 🔄 Recent Activity
|
||||
## Recommended Checks
|
||||
|
||||
### Last 24 Hours
|
||||
### Start of Day
|
||||
|
||||
1. **Uni-Wizard v4 Completed** — Four-pass architecture evolution
|
||||
2. **PR Created** — feature/uni-wizard-v4-production
|
||||
3. **Allegro Lane Narrowed** — Focused on Gitea/Hermes bridge
|
||||
4. **Issue #72 Reported** — Overnight loop not running
|
||||
1. Open the review queue and unassigned queue.
|
||||
2. Check `pipeline-freshness.sh`.
|
||||
3. Check the latest heartbeat tick.
|
||||
4. Check whether archive checkpoints and DPO artifacts advanced.
|
||||
|
||||
### Pending Actions
|
||||
### Before Merging
|
||||
|
||||
1. Deploy Ezra VPS (archivist/research)
|
||||
2. Deploy Bezalel VPS (artificer/builder)
|
||||
3. Start overnight loop
|
||||
4. Configure Syncthing mesh
|
||||
5. Implement caching layer (#103)
|
||||
1. Confirm the PR is aligned with Heartbeat, Harness, or Portal.
|
||||
2. Confirm verification is real, not implied.
|
||||
3. Confirm the change does not silently cross repo boundaries.
|
||||
4. Confirm the change does not revive deprecated loop-era behavior.
|
||||
|
||||
---
|
||||
### End of Day
|
||||
|
||||
## 🎯 Recommendations
|
||||
1. Check for duplicate issues and duplicate PR momentum.
|
||||
2. Check whether Timmy is carrying routine queue work that Allegro should own.
|
||||
3. Check whether builders were given work inside their real lanes.
|
||||
|
||||
### Immediate (Next 24h)
|
||||
## Anti-Patterns
|
||||
|
||||
1. **Review Uni-Wizard v4 PR** — Ready for merge
|
||||
2. **Start Overnight Loop** — If operational approval given
|
||||
3. **Deploy Ezra VPS** — For research tasks
|
||||
Avoid:
|
||||
- treating archived dashboard-era issues as the live roadmap
|
||||
- using stale docs that assume `uni-wizard` is still the center
|
||||
- routing work by habit instead of by current lane
|
||||
- letting open loops multiply faster than they are reviewed
|
||||
|
||||
### Short-term (This Week)
|
||||
## Success Condition
|
||||
|
||||
1. Implement caching layer (#103) — High impact
|
||||
2. Build backend registry (#95) — Enables routing
|
||||
3. Create telemetry dashboard (#91) — Visibility
|
||||
|
||||
### Medium-term (This Month)
|
||||
|
||||
1. Complete Grand Timmy epic (#94)
|
||||
2. Dissolve wizard identities (#99)
|
||||
3. Deploy Evennia world shell (#83, #84)
|
||||
|
||||
---
|
||||
|
||||
## 📈 Metrics
|
||||
|
||||
| Metric | Current | Target |
|
||||
|--------|---------|--------|
|
||||
| Issues Open | 19 | < 10 |
|
||||
| PRs Open | 1 | — |
|
||||
| VPS Online | 1/3 | 3/3 |
|
||||
| Loop Cycles | 0 | 100/day |
|
||||
|
||||
---
|
||||
|
||||
*Dashboard updated: March 30, 2026*
|
||||
*Next update: March 31, 2026*
|
||||
The system is healthy when:
|
||||
- work is routed cleanly
|
||||
- review is keeping pace
|
||||
- private learning loops are producing artifacts
|
||||
- Timmy is spending time on sovereignty and judgment rather than queue untangling
|
||||
|
||||
@@ -1,220 +1,89 @@
|
||||
# Uni-Wizard v4 — Quick Reference
|
||||
# Timmy Workflow Quick Reference
|
||||
|
||||
## Installation
|
||||
Updated: April 4, 2026
|
||||
|
||||
## What Lives Where
|
||||
|
||||
- `~/.timmy`: Timmy's workspace, lived data, heartbeat, archive artifacts
|
||||
- `~/.timmy/timmy-config`: Timmy's identity and orchestration sidecar repo
|
||||
- `~/.hermes`: Hermes harness, sessions, config overlay, helper scripts
|
||||
|
||||
## Most Useful Commands
|
||||
|
||||
### Workflow Status
|
||||
|
||||
```bash
|
||||
# Run setup script
|
||||
sudo ./scripts/setup-uni-wizard.sh
|
||||
|
||||
# Or manual install
|
||||
cd uni-wizard/v4
|
||||
pip install -e .
|
||||
~/.hermes/bin/ops-panel.sh
|
||||
~/.hermes/bin/ops-gitea.sh
|
||||
~/.hermes/bin/timmy-dashboard
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```python
|
||||
from uni_wizard import Harness, House, Mode
|
||||
|
||||
# Create harness
|
||||
harness = Harness(house=House.TIMMY, mode=Mode.INTELLIGENT)
|
||||
|
||||
# Execute tool
|
||||
result = harness.execute("git_status", repo_path="/path/to/repo")
|
||||
|
||||
# Check prediction
|
||||
print(f"Predicted success: {result.provenance.prediction:.0%}")
|
||||
|
||||
# Get result
|
||||
if result.success:
|
||||
print(result.data)
|
||||
else:
|
||||
print(f"Error: {result.error}")
|
||||
```
|
||||
|
||||
## Command Line
|
||||
### Workflow Helpers
|
||||
|
||||
```bash
|
||||
# Simple execution
|
||||
uni-wizard execute git_status --repo-path /path
|
||||
|
||||
# With specific house
|
||||
uni-wizard execute git_status --house ezra --mode intelligent
|
||||
|
||||
# Batch execution
|
||||
uni-wizard batch tasks.json
|
||||
|
||||
# Check health
|
||||
uni-wizard health
|
||||
|
||||
# View stats
|
||||
uni-wizard stats
|
||||
source ~/.hermes/bin/ops-helpers.sh
|
||||
ops-help
|
||||
ops-review-queue
|
||||
ops-unassigned all
|
||||
ops-queue codex-agent all
|
||||
```
|
||||
|
||||
## Houses
|
||||
|
||||
| House | Role | Best For |
|
||||
|-------|------|----------|
|
||||
| `House.TIMMY` | Sovereign | Final decisions, critical ops |
|
||||
| `House.EZRA` | Archivist | Reading, analysis, documentation |
|
||||
| `House.BEZALEL` | Artificer | Building, testing, implementation |
|
||||
| `House.ALLEGRO` | Dispatch | Routing, connectivity, tempo |
|
||||
|
||||
## Modes
|
||||
|
||||
| Mode | Use When | Features |
|
||||
|------|----------|----------|
|
||||
| `Mode.SIMPLE` | Scripts, quick tasks | Direct execution, no overhead |
|
||||
| `Mode.INTELLIGENT` | Production work | Predictions, learning, adaptation |
|
||||
| `Mode.SOVEREIGN` | Critical decisions | Full provenance, approval gates |
|
||||
|
||||
## Common Tasks
|
||||
|
||||
### Check System Status
|
||||
```python
|
||||
result = harness.execute("system_info")
|
||||
print(result.data)
|
||||
```
|
||||
|
||||
### Git Operations
|
||||
```python
|
||||
# Status
|
||||
result = harness.execute("git_status", repo_path="/path")
|
||||
|
||||
# Log
|
||||
result = harness.execute("git_log", repo_path="/path", max_count=10)
|
||||
|
||||
# Pull
|
||||
result = harness.execute("git_pull", repo_path="/path")
|
||||
```
|
||||
|
||||
### Health Check
|
||||
```python
|
||||
result = harness.execute("health_check")
|
||||
print(f"Status: {result.data['status']}")
|
||||
```
|
||||
|
||||
### Batch Operations
|
||||
```python
|
||||
tasks = [
|
||||
{"tool": "git_status", "params": {"repo_path": "/path1"}},
|
||||
{"tool": "git_status", "params": {"repo_path": "/path2"}},
|
||||
{"tool": "system_info", "params": {}}
|
||||
]
|
||||
results = harness.execute_batch(tasks)
|
||||
```
|
||||
|
||||
## Service Management
|
||||
### Pipeline Freshness
|
||||
|
||||
```bash
|
||||
# Start services
|
||||
sudo systemctl start uni-wizard
|
||||
sudo systemctl start timmy-health
|
||||
sudo systemctl start timmy-task-router
|
||||
|
||||
# Check status
|
||||
sudo systemctl status uni-wizard
|
||||
|
||||
# View logs
|
||||
sudo journalctl -u uni-wizard -f
|
||||
tail -f /opt/timmy/logs/uni-wizard.log
|
||||
|
||||
# Restart
|
||||
sudo systemctl restart uni-wizard
|
||||
~/.hermes/bin/pipeline-freshness.sh
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
### Archive Pipeline
|
||||
|
||||
### Service Won't Start
|
||||
```bash
|
||||
# Check logs
|
||||
journalctl -u uni-wizard -n 50
|
||||
|
||||
# Verify config
|
||||
cat /opt/timmy/config/uni-wizard.yaml
|
||||
|
||||
# Test manually
|
||||
python -m uni_wizard health
|
||||
python3 - <<'PY'
|
||||
import json, sys
|
||||
sys.path.insert(0, '/Users/apayne/.timmy/timmy-config')
|
||||
from tasks import _archive_pipeline_health_impl
|
||||
print(json.dumps(_archive_pipeline_health_impl(), indent=2))
|
||||
PY
|
||||
```
|
||||
|
||||
### No Predictions
|
||||
- Check pattern database exists: `ls /opt/timmy/data/patterns.db`
|
||||
- Verify learning is enabled in config
|
||||
- Run a few tasks to build patterns
|
||||
|
||||
### Gitea Integration Failing
|
||||
- Verify API token in config
|
||||
- Check Gitea URL is accessible
|
||||
- Test: `curl http://143.198.27.163:3000/api/v1/version`
|
||||
|
||||
## Configuration
|
||||
|
||||
Location: `/opt/timmy/config/uni-wizard.yaml`
|
||||
|
||||
```yaml
|
||||
house: timmy
|
||||
mode: intelligent
|
||||
enable_learning: true
|
||||
|
||||
pattern_db: /opt/timmy/data/patterns.db
|
||||
log_level: INFO
|
||||
|
||||
gitea:
|
||||
url: http://143.198.27.163:3000
|
||||
token: YOUR_TOKEN_HERE
|
||||
poll_interval: 300
|
||||
|
||||
hermes:
|
||||
stream_enabled: true
|
||||
db_path: /root/.hermes/state.db
|
||||
```bash
|
||||
python3 - <<'PY'
|
||||
import json, sys
|
||||
sys.path.insert(0, '/Users/apayne/.timmy/timmy-config')
|
||||
from tasks import _know_thy_father_impl
|
||||
print(json.dumps(_know_thy_father_impl(), indent=2))
|
||||
PY
|
||||
```
|
||||
|
||||
## API Reference
|
||||
### Manual Dispatch Prompt
|
||||
|
||||
### Harness Methods
|
||||
|
||||
```python
|
||||
# Execute single tool
|
||||
harness.execute(tool_name, **params) -> ExecutionResult
|
||||
|
||||
# Execute async
|
||||
await harness.execute_async(tool_name, **params) -> ExecutionResult
|
||||
|
||||
# Execute batch
|
||||
harness.execute_batch(tasks) -> List[ExecutionResult]
|
||||
|
||||
# Get prediction
|
||||
harness.predict(tool_name, params) -> Prediction
|
||||
|
||||
# Get stats
|
||||
harness.get_stats() -> Dict
|
||||
|
||||
# Get patterns
|
||||
harness.get_patterns() -> Dict
|
||||
```bash
|
||||
~/.hermes/bin/agent-dispatch.sh groq 542 Timmy_Foundation/the-nexus
|
||||
```
|
||||
|
||||
### ExecutionResult Fields
|
||||
## Best Files to Check
|
||||
|
||||
```python
|
||||
result.success # bool
|
||||
result.data # Any
|
||||
result.error # Optional[str]
|
||||
result.provenance # Provenance
|
||||
result.suggestions # List[str]
|
||||
```
|
||||
### Operational State
|
||||
|
||||
### Provenance Fields
|
||||
- `~/.timmy/heartbeat/last_tick.json`
|
||||
- `~/.hermes/model_health.json`
|
||||
- `~/.timmy/twitter-archive/checkpoint.json`
|
||||
- `~/.timmy/twitter-archive/metrics/progress.json`
|
||||
|
||||
```python
|
||||
provenance.house # str
|
||||
provenance.tool # str
|
||||
provenance.mode # str
|
||||
provenance.prediction # float
|
||||
provenance.execution_time_ms # float
|
||||
provenance.input_hash # str
|
||||
provenance.output_hash # str
|
||||
```
|
||||
### Archive Feedback
|
||||
|
||||
---
|
||||
- `~/.timmy/twitter-archive/notes/`
|
||||
- `~/.timmy/twitter-archive/knowledge/profile.json`
|
||||
- `~/.timmy/twitter-archive/training/dpo/`
|
||||
|
||||
*For full documentation, see ARCHITECTURE.md*
|
||||
### Review and Queue
|
||||
|
||||
- Gitea PR queue
|
||||
- Gitea unassigned issues
|
||||
- Timmy/Allegro assigned review queue
|
||||
|
||||
## Rules of Thumb
|
||||
|
||||
- If it changes identity or orchestration, review it carefully in `timmy-config`.
|
||||
- If it changes lived outputs or training inputs, it probably belongs in `timmy-home`.
|
||||
- If it only “sounds right” but is not proven by runtime state, it is not verified.
|
||||
- If a change is major, package it as a PR for Timmy review.
|
||||
|
||||
@@ -1,125 +1,71 @@
|
||||
# Scorecard Generator Documentation
|
||||
# Workflow Scorecard
|
||||
|
||||
## Overview
|
||||
Updated: April 4, 2026
|
||||
|
||||
The Scorecard Generator analyzes overnight loop JSONL data and produces comprehensive reports with statistics, trends, and recommendations.
|
||||
The old overnight `uni-wizard` scorecard is no longer the primary operational metric.
|
||||
The current scorecard should measure whether Timmy's real workflow is healthy.
|
||||
|
||||
## Usage
|
||||
## What To Score
|
||||
|
||||
### Basic Usage
|
||||
### Queue Health
|
||||
|
||||
```bash
|
||||
# Generate scorecard from default input directory
|
||||
python uni-wizard/scripts/generate_scorecard.py
|
||||
- unassigned issue count
|
||||
- PRs waiting on Timmy or Allegro review
|
||||
- overloaded assignees
|
||||
- duplicate issue / duplicate PR pressure
|
||||
|
||||
# Specify custom input/output directories
|
||||
python uni-wizard/scripts/generate_scorecard.py \
|
||||
--input ~/shared/overnight-loop \
|
||||
--output ~/timmy/reports
|
||||
```
|
||||
### Runtime Health
|
||||
|
||||
### Cron Setup
|
||||
- Hermes gateway reachable
|
||||
- local provider responding
|
||||
- latest heartbeat tick present
|
||||
- model health reporting accurately
|
||||
|
||||
```bash
|
||||
# Generate scorecard every morning at 6 AM
|
||||
0 6 * * * /root/timmy/venv/bin/python /root/timmy/uni-wizard/scripts/generate_scorecard.py
|
||||
```
|
||||
### Learning Loop Health
|
||||
|
||||
## Input Format
|
||||
- archive checkpoint advancing
|
||||
- notes and knowledge artifacts being emitted
|
||||
- DPO files growing
|
||||
- freshness lag between sessions and exports
|
||||
|
||||
JSONL files in `~/shared/overnight-loop/*.jsonl`:
|
||||
## Suggested Daily Questions
|
||||
|
||||
```json
|
||||
{"task": "read-soul", "status": "pass", "duration_s": 19.7, "timestamp": "2026-03-29T21:54:12Z"}
|
||||
{"task": "check-health", "status": "fail", "duration_s": 5.2, "error": "timeout", "timestamp": "2026-03-29T22:15:33Z"}
|
||||
```
|
||||
1. Did review keep pace with execution today?
|
||||
2. Did any builder receive work outside their lane?
|
||||
3. Did Timmy spend time on judgment rather than routine queue cleanup?
|
||||
4. Did the private learning pipeline produce usable artifacts?
|
||||
5. Did any stale doc, helper, or default try to pull the system back into old habits?
|
||||
|
||||
Fields:
|
||||
- `task`: Task identifier
|
||||
- `status`: "pass" or "fail"
|
||||
- `duration_s`: Execution time in seconds
|
||||
- `timestamp`: ISO 8601 timestamp
|
||||
- `error`: Error message (for failed tasks)
|
||||
## Useful Inputs
|
||||
|
||||
## Output
|
||||
- `~/.timmy/heartbeat/ticks_YYYYMMDD.jsonl`
|
||||
- `~/.timmy/metrics/local_YYYYMMDD.jsonl`
|
||||
- `~/.timmy/twitter-archive/checkpoint.json`
|
||||
- `~/.timmy/twitter-archive/metrics/progress.json`
|
||||
- Gitea open PR queue
|
||||
- Gitea unassigned issue queue
|
||||
|
||||
### JSON Report
|
||||
## Suggested Ratings
|
||||
|
||||
`~/timmy/reports/scorecard_YYYYMMDD.json`:
|
||||
### Queue Discipline
|
||||
|
||||
```json
|
||||
{
|
||||
"generated_at": "2026-03-30T06:00:00Z",
|
||||
"summary": {
|
||||
"total_tasks": 100,
|
||||
"passed": 95,
|
||||
"failed": 5,
|
||||
"pass_rate": 95.0,
|
||||
"duration_stats": {
|
||||
"avg": 12.5,
|
||||
"median": 10.2,
|
||||
"p95": 45.0,
|
||||
"min": 1.2,
|
||||
"max": 120.5
|
||||
}
|
||||
},
|
||||
"by_task": {...},
|
||||
"by_hour": {...},
|
||||
"errors": {...},
|
||||
"recommendations": [...]
|
||||
}
|
||||
```
|
||||
- Strong: review and dispatch are keeping up, little duplicate churn
|
||||
- Mixed: queue moves, but ambiguity or duplication is increasing
|
||||
- Weak: review is backlogged or agents are being misrouted
|
||||
|
||||
### Markdown Report
|
||||
### Runtime Reliability
|
||||
|
||||
`~/timmy/reports/scorecard_YYYYMMDD.md`:
|
||||
- Strong: heartbeat, Hermes, and provider surfaces all healthy
|
||||
- Mixed: intermittent downtime or weak health signals
|
||||
- Weak: major surfaces untrusted or stale
|
||||
|
||||
- Executive summary with pass/fail counts
|
||||
- Duration statistics (avg, median, p95)
|
||||
- Per-task breakdown with pass rates
|
||||
- Hourly timeline showing performance trends
|
||||
- Error analysis with frequency counts
|
||||
- Actionable recommendations
|
||||
### Learning Throughput
|
||||
|
||||
## Report Interpretation
|
||||
- Strong: checkpoint advances, DPO output accumulates, eval gates are visible
|
||||
- Mixed: some artifacts land, but freshness or checkpointing lags
|
||||
- Weak: sessions occur without export, or learning artifacts stall
|
||||
|
||||
### Pass Rate Thresholds
|
||||
## The Goal
|
||||
|
||||
| Pass Rate | Status | Action |
|
||||
|-----------|--------|--------|
|
||||
| 95%+ | ✅ Excellent | Continue current operations |
|
||||
| 85-94% | ⚠️ Good | Monitor for degradation |
|
||||
| 70-84% | ⚠️ Fair | Review failing tasks |
|
||||
| <70% | ❌ Poor | Immediate investigation required |
|
||||
|
||||
### Duration Guidelines
|
||||
|
||||
| Duration | Assessment |
|
||||
|----------|------------|
|
||||
| <5s | Fast |
|
||||
| 5-15s | Normal |
|
||||
| 15-30s | Slow |
|
||||
| >30s | Very slow - consider optimization |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### No JSONL files found
|
||||
|
||||
```bash
|
||||
# Check input directory
|
||||
ls -la ~/shared/overnight-loop/
|
||||
|
||||
# Ensure Syncthing is syncing
|
||||
systemctl status syncthing@root
|
||||
```
|
||||
|
||||
### Malformed lines
|
||||
|
||||
The generator skips malformed lines with a warning. Check the JSONL files for syntax errors.
|
||||
|
||||
### Empty reports
|
||||
|
||||
If no data exists, verify:
|
||||
1. Overnight loop is running and writing JSONL
|
||||
2. File permissions allow reading
|
||||
3. Input path is correct
|
||||
The point of the scorecard is not to admire activity.
|
||||
The point is to tell whether the system is becoming more reviewable, more sovereign, and more capable of learning from lived work.
|
||||
|
||||
491
docs/USER_AUDIT_2026-04-04.md
Normal file
491
docs/USER_AUDIT_2026-04-04.md
Normal file
@@ -0,0 +1,491 @@
|
||||
# Workspace User Audit
|
||||
|
||||
Date: 2026-04-04
|
||||
Scope: Hermes Gitea workspace users visible from `/explore/users`
|
||||
Primary org examined: `Timmy_Foundation`
|
||||
Primary strategic filter: `the-nexus` issue #542 (`DIRECTION SHIFT`)
|
||||
|
||||
## Purpose
|
||||
|
||||
This audit maps each visible workspace user to:
|
||||
|
||||
- observed contribution pattern
|
||||
- likely capabilities
|
||||
- likely failure mode
|
||||
- suggested lane of highest leverage
|
||||
|
||||
The point is not to flatter or punish accounts. The point is to stop wasting attention on the wrong agent for the wrong job.
|
||||
|
||||
## Method
|
||||
|
||||
This audit was derived from:
|
||||
|
||||
- Gitea admin user roster
|
||||
- public user explorer page
|
||||
- org-wide issues and pull requests across:
|
||||
- `the-nexus`
|
||||
- `timmy-home`
|
||||
- `timmy-config`
|
||||
- `hermes-agent`
|
||||
- `turboquant`
|
||||
- `.profile`
|
||||
- `the-door`
|
||||
- `timmy-academy`
|
||||
- `claude-code-src`
|
||||
- PR outcome split:
|
||||
- open
|
||||
- merged
|
||||
- closed unmerged
|
||||
|
||||
This is a capability-and-lane audit, not a character judgment. New or low-artifact accounts are marked as unproven rather than weak.
|
||||
|
||||
## Strategic Frame
|
||||
|
||||
Per issue #542, the current system direction is:
|
||||
|
||||
1. Heartbeat
|
||||
2. Harness
|
||||
3. Portal Interface
|
||||
|
||||
Any user who does not materially help one of those three jobs should be deprioritized, reassigned, or retired.
|
||||
|
||||
## Top Findings
|
||||
|
||||
- The org has real execution capacity, but too much ideation and duplicate backlog generation relative to merged implementation.
|
||||
- Best current execution profiles: `allegro`, `groq`, `codex-agent`, `manus`, `Timmy`.
|
||||
- Best architecture / research / integration profiles: `perplexity`, `gemini`, `Timmy`, `Rockachopa`.
|
||||
- Best archivist / memory / RCA profile: `ezra`.
|
||||
- Biggest cleanup opportunities:
|
||||
- consolidate `google` into `gemini`
|
||||
- consolidate or retire legacy `kimi` in favor of `KimiClaw`
|
||||
- keep unproven symbolic accounts off the critical path until they ship
|
||||
|
||||
## Recommended Team Shape
|
||||
|
||||
- Direction and doctrine: `Rockachopa`, `Timmy`
|
||||
- Architecture and strategy: `Timmy`, `perplexity`, `gemini`
|
||||
- Triage and dispatch: `allegro`, `Timmy`
|
||||
- Core implementation: `claude`, `groq`, `codex-agent`, `manus`
|
||||
- Long-context reading and extraction: `KimiClaw`
|
||||
- RCA, archival memory, and operating history: `ezra`
|
||||
- Experimental reserve: `grok`, `bezalel`, `antigravity`, `fenrir`, `substratum`
|
||||
- Consolidate or retire: `google`, `kimi`, plus dormant admin-style identities without a lane
|
||||
|
||||
## User Audit
|
||||
|
||||
### Rockachopa
|
||||
|
||||
- Observed pattern:
|
||||
- founder-originated direction, issue seeding, architectural reset signals
|
||||
- relatively little direct PR volume in this org
|
||||
- Likely strengths:
|
||||
- taste
|
||||
- doctrine
|
||||
- strategic kill/defer calls
|
||||
- setting the real north star
|
||||
- Likely failure mode:
|
||||
- pushing direction into the system without a matching enforcement pass
|
||||
- Highest-leverage lane:
|
||||
- final priority authority
|
||||
- architectural direction
|
||||
- closure of dead paths
|
||||
- Anti-lane:
|
||||
- routine backlog maintenance
|
||||
- repetitive implementation supervision
|
||||
|
||||
### Timmy
|
||||
|
||||
- Observed pattern:
|
||||
- highest total authored artifact volume
|
||||
- high merged PR count
|
||||
- major issue author across `the-nexus`, `timmy-home`, and `timmy-config`
|
||||
- Likely strengths:
|
||||
- system ownership
|
||||
- epic creation
|
||||
- repo direction
|
||||
- governance
|
||||
- durable internal doctrine
|
||||
- Likely failure mode:
|
||||
- overproducing backlog and labels faster than the system can metabolize them
|
||||
- Highest-leverage lane:
|
||||
- principal systems owner
|
||||
- release governance
|
||||
- strategic triage
|
||||
- architecture acceptance and rejection
|
||||
- Anti-lane:
|
||||
- low-value duplicate issue generation
|
||||
|
||||
### perplexity
|
||||
|
||||
- Observed pattern:
|
||||
- strong issue author across `the-nexus`, `timmy-config`, and `timmy-home`
|
||||
- good but not massive PR volume
|
||||
- strong concentration in `[MCP]`, `[HARNESS]`, `[ARCH]`, `[RESEARCH]`, `[OPENCLAW]`
|
||||
- Likely strengths:
|
||||
- integration architecture
|
||||
- tool and MCP discovery
|
||||
- sovereignty framing
|
||||
- research triage
|
||||
- QA-oriented systems thinking
|
||||
- Likely failure mode:
|
||||
- producing too many candidate directions without enough collapse into one chosen path
|
||||
- Highest-leverage lane:
|
||||
- research scout
|
||||
- MCP / open-source evaluation
|
||||
- architecture memos
|
||||
- issue shaping
|
||||
- knowledge transfer
|
||||
- Anti-lane:
|
||||
- being the default final implementer for all threads
|
||||
|
||||
### gemini
|
||||
|
||||
- Observed pattern:
|
||||
- very high PR volume and high closure rate
|
||||
- strong presence in `the-nexus`, `timmy-config`, and `hermes-agent`
|
||||
- often operates in architecture and research-heavy territory
|
||||
- Likely strengths:
|
||||
- architecture generation
|
||||
- speculative design
|
||||
- decomposing systems into modules
|
||||
- surfacing future-facing ideas quickly
|
||||
- Likely failure mode:
|
||||
- duplicate PRs
|
||||
- speculative PRs
|
||||
- noise relative to accepted implementation
|
||||
- Highest-leverage lane:
|
||||
- frontier architecture
|
||||
- design spikes
|
||||
- long-range technical options
|
||||
- research-to-issue translation
|
||||
- Anti-lane:
|
||||
- unsupervised backlog flood
|
||||
- high-autonomy repo hygiene work
|
||||
|
||||
### claude
|
||||
|
||||
- Observed pattern:
|
||||
- huge PR volume concentrated in `the-nexus`
|
||||
- high merged count, but also very high closed-unmerged count
|
||||
- Likely strengths:
|
||||
- large code changes
|
||||
- hard refactors
|
||||
- implementation stamina
|
||||
- test-aware coding when tightly scoped
|
||||
- Likely failure mode:
|
||||
- overbuilding
|
||||
- mismatch with current direction
|
||||
- lower signal when the task is under-specified
|
||||
- Highest-leverage lane:
|
||||
- hard implementation
|
||||
- deep refactors
|
||||
- large bounded code edits after exact scoping
|
||||
- Anti-lane:
|
||||
- self-directed architecture exploration without tight constraints
|
||||
|
||||
### groq
|
||||
|
||||
- Observed pattern:
|
||||
- good merged PR count in `the-nexus`
|
||||
- lower failure rate than many high-volume agents
|
||||
- Likely strengths:
|
||||
- tactical implementation
|
||||
- bounded fixes
|
||||
- shipping narrow slices
|
||||
- cost-effective execution
|
||||
- Likely failure mode:
|
||||
- may underperform on large ambiguous architectural threads
|
||||
- Highest-leverage lane:
|
||||
- bug fixes
|
||||
- tactical feature work
|
||||
- well-scoped implementation tasks
|
||||
- Anti-lane:
|
||||
- owning broad doctrine or long-range architecture
|
||||
|
||||
### grok
|
||||
|
||||
- Observed pattern:
|
||||
- moderate PR volume in `the-nexus`
|
||||
- mixed merge outcomes
|
||||
- Likely strengths:
|
||||
- edge-case thinking
|
||||
- adversarial poking
|
||||
- creative angles
|
||||
- Likely failure mode:
|
||||
- novelty or provocation over disciplined convergence
|
||||
- Highest-leverage lane:
|
||||
- adversarial review
|
||||
- UX weirdness
|
||||
- edge-case scenario generation
|
||||
- Anti-lane:
|
||||
- boring, critical-path cleanup where predictability matters most
|
||||
|
||||
### allegro
|
||||
|
||||
- Observed pattern:
|
||||
- outstanding merged PR profile
|
||||
- meaningful issue volume in `timmy-home` and `hermes-agent`
|
||||
- profile explicitly aligned with triage and routing
|
||||
- Likely strengths:
|
||||
- dispatch
|
||||
- sequencing
|
||||
- fix prioritization
|
||||
- security / operational hygiene
|
||||
- converting chaos into the next clean move
|
||||
- Likely failure mode:
|
||||
- being used as a generic writer instead of as an operator
|
||||
- Highest-leverage lane:
|
||||
- triage
|
||||
- dispatch
|
||||
- routing
|
||||
- security and operational cleanup
|
||||
- execution coordination
|
||||
- Anti-lane:
|
||||
- speculative research sprawl
|
||||
|
||||
### codex-agent
|
||||
|
||||
- Observed pattern:
|
||||
- lower volume, perfect merged record so far
|
||||
- concentrated in `timmy-home` and `timmy-config`
|
||||
- recent work shows cleanup, migration verification, and repo-boundary enforcement
|
||||
- Likely strengths:
|
||||
- dead-code cutting
|
||||
- migration verification
|
||||
- repo-boundary enforcement
|
||||
- implementation through PR discipline
|
||||
- reducing drift between intended and actual architecture
|
||||
- Likely failure mode:
|
||||
- overfocusing on cleanup if not paired with strategic direction
|
||||
- Highest-leverage lane:
|
||||
- cleanup
|
||||
- systems hardening
|
||||
- migration and cutover work
|
||||
- PR-first implementation of architectural intent
|
||||
- Anti-lane:
|
||||
- wide speculative backlog ideation
|
||||
|
||||
### manus
|
||||
|
||||
- Observed pattern:
|
||||
- low volume but good merge rate
|
||||
- bounded work footprint
|
||||
- Likely strengths:
|
||||
- one-shot tasks
|
||||
- support implementation
|
||||
- moderate-scope execution
|
||||
- Likely failure mode:
|
||||
- limited demonstrated range inside this org
|
||||
- Highest-leverage lane:
|
||||
- single bounded tasks
|
||||
- support implementation
|
||||
- targeted coding asks
|
||||
- Anti-lane:
|
||||
- strategic ownership of ongoing programs
|
||||
|
||||
### KimiClaw
|
||||
|
||||
- Observed pattern:
|
||||
- very new
|
||||
- one merged PR in `timmy-home`
|
||||
- profile emphasizes long-context analysis via OpenClaw
|
||||
- Likely strengths:
|
||||
- long-context reading
|
||||
- extraction
|
||||
- synthesis before action
|
||||
- Likely failure mode:
|
||||
- not yet proven in repeated implementation loops
|
||||
- Highest-leverage lane:
|
||||
- codebase digestion
|
||||
- extraction and summarization
|
||||
- pre-implementation reading passes
|
||||
- Anti-lane:
|
||||
- solo ownership of fast-moving critical-path changes until more evidence exists
|
||||
|
||||
### kimi
|
||||
|
||||
- Observed pattern:
|
||||
- almost no durable artifact trail in this org
|
||||
- Likely strengths:
|
||||
- historically used as a hands-style execution agent
|
||||
- Likely failure mode:
|
||||
- identity overlap with stronger replacements
|
||||
- Highest-leverage lane:
|
||||
- either retire
|
||||
- or keep for tightly bounded experiments only
|
||||
- Anti-lane:
|
||||
- first-string team role
|
||||
|
||||
### ezra
|
||||
|
||||
- Observed pattern:
|
||||
- high issue volume, almost no PRs
|
||||
- concentrated in `timmy-home`
|
||||
- prefixes include `[RCA]`, `[STUDY]`, `[FAILURE]`, `[ONBOARDING]`
|
||||
- Likely strengths:
|
||||
- archival memory
|
||||
- failure analysis
|
||||
- onboarding docs
|
||||
- study reports
|
||||
- interpretation of what happened
|
||||
- Likely failure mode:
|
||||
- becoming pure narration with no collapse into action
|
||||
- Highest-leverage lane:
|
||||
- archivist
|
||||
- scribe
|
||||
- RCA
|
||||
- operating history
|
||||
- onboarding
|
||||
- Anti-lane:
|
||||
- primary code shipper
|
||||
|
||||
### bezalel
|
||||
|
||||
- Observed pattern:
|
||||
- tiny visible artifact trail
|
||||
- profile suggests builder / debugger / proof-bearer
|
||||
- Likely strengths:
|
||||
- likely useful for testbed and proof work, but not yet well evidenced in Gitea
|
||||
- Likely failure mode:
|
||||
- assigning major ownership before proof exists
|
||||
- Highest-leverage lane:
|
||||
- testbed verification
|
||||
- proof of life
|
||||
- hardening checks
|
||||
- Anti-lane:
|
||||
- broad strategic ownership
|
||||
|
||||
### antigravity
|
||||
|
||||
- Observed pattern:
|
||||
- minimal artifact trail
|
||||
- yet explicitly referenced in issue #542 as development loop owner
|
||||
- Likely strengths:
|
||||
- direct founder-trusted execution
|
||||
- potentially strong private-context operator
|
||||
- Likely failure mode:
|
||||
- invisible work makes it hard to calibrate or route intelligently
|
||||
- Highest-leverage lane:
|
||||
- founder-directed execution
|
||||
- development loop tasks where trust is already established
|
||||
- Anti-lane:
|
||||
- org-wide lane ownership without more visible evidence
|
||||
|
||||
### google
|
||||
|
||||
- Observed pattern:
|
||||
- duplicate-feeling identity relative to `gemini`
|
||||
- only closed-unmerged PRs in `the-nexus`
|
||||
- Likely strengths:
|
||||
- none distinct enough from `gemini` in current evidence
|
||||
- Likely failure mode:
|
||||
- duplicate persona and duplicate backlog surface
|
||||
- Highest-leverage lane:
|
||||
- consolidate into `gemini` or retire
|
||||
- Anti-lane:
|
||||
- continued parallel role with overlapping mandate
|
||||
|
||||
### hermes
|
||||
|
||||
- Observed pattern:
|
||||
- essentially no durable collaborative artifact trail
|
||||
- Likely strengths:
|
||||
- system or service identity
|
||||
- Likely failure mode:
|
||||
- confusion between service identity and contributor identity
|
||||
- Highest-leverage lane:
|
||||
- machine identity only
|
||||
- Anti-lane:
|
||||
- backlog or product work
|
||||
|
||||
### replit
|
||||
|
||||
- Observed pattern:
|
||||
- admin-capable, no meaningful contribution trail here
|
||||
- Likely strengths:
|
||||
- likely external or sandbox utility
|
||||
- Likely failure mode:
|
||||
- implicit trust without role clarity
|
||||
- Highest-leverage lane:
|
||||
- sandbox or peripheral experimentation
|
||||
- Anti-lane:
|
||||
- core system ownership
|
||||
|
||||
### allegro-primus
|
||||
|
||||
- Observed pattern:
|
||||
- no visible artifact trail yet
|
||||
- Highest-leverage lane:
|
||||
- none until proven
|
||||
|
||||
### claw-code
|
||||
|
||||
- Observed pattern:
|
||||
- almost no artifact trail yet
|
||||
- Highest-leverage lane:
|
||||
- harness experiments only until proven
|
||||
|
||||
### substratum
|
||||
|
||||
- Observed pattern:
|
||||
- no visible artifact trail yet
|
||||
- Highest-leverage lane:
|
||||
- reserve account only until it ships durable work
|
||||
|
||||
### bilbobagginshire
|
||||
|
||||
- Observed pattern:
|
||||
- admin account, no visible contribution trail
|
||||
- Highest-leverage lane:
|
||||
- none until proven
|
||||
|
||||
### fenrir
|
||||
|
||||
- Observed pattern:
|
||||
- brand new
|
||||
- no visible contribution trail
|
||||
- Highest-leverage lane:
|
||||
- probationary tasks only until it earns a lane
|
||||
|
||||
## Consolidation Recommendations
|
||||
|
||||
1. Consolidate `google` into `gemini`.
|
||||
2. Consolidate legacy `kimi` into `KimiClaw` unless a separate lane is proven.
|
||||
3. Keep symbolic or dormant identities off critical path until they ship.
|
||||
4. Treat `allegro`, `perplexity`, `codex-agent`, `groq`, and `Timmy` as the current strongest operating core.
|
||||
|
||||
## Routing Rules
|
||||
|
||||
- If the task is architecture, sovereignty tradeoff, or MCP/open-source evaluation:
|
||||
- use `perplexity` first
|
||||
- If the task is dispatch, triage, cleanup ordering, or operational next-move selection:
|
||||
- use `allegro`
|
||||
- If the task is a hard bounded refactor:
|
||||
- use `claude`
|
||||
- If the task is a tactical code slice:
|
||||
- use `groq`
|
||||
- If the task is cleanup, migration, repo-boundary enforcement, or “make reality match the diagram”:
|
||||
- use `codex-agent`
|
||||
- If the task is archival memory, failure analysis, onboarding, or durable lessons:
|
||||
- use `ezra`
|
||||
- If the task is long-context digestion before action:
|
||||
- use `KimiClaw`
|
||||
- If the task is final acceptance, doctrine, or strategic redirection:
|
||||
- route to `Timmy` and `Rockachopa`
|
||||
|
||||
## Anti-Routing Rules
|
||||
|
||||
- Do not use `gemini` as the default closer for vague work.
|
||||
- Do not use `ezra` as a primary shipper.
|
||||
- Do not use dormant identities as if they are proven operators.
|
||||
- Do not let architecture-spec agents create unlimited parallel issue trees without a collapse pass.
|
||||
|
||||
## Proposed Next Step
|
||||
|
||||
Timmy, Ezra, and Allegro should convert this from an audit into a living lane charter:
|
||||
|
||||
- Timmy decides the final lane map.
|
||||
- Ezra turns it into durable operating doctrine.
|
||||
- Allegro turns it into routing rules and dispatch policy.
|
||||
|
||||
The system has enough agents. The next win is cleaner lanes, fewer duplicates, and tighter assignment discipline.
|
||||
295
docs/WIZARD_APPRENTICESHIP_CHARTER.md
Normal file
295
docs/WIZARD_APPRENTICESHIP_CHARTER.md
Normal file
@@ -0,0 +1,295 @@
|
||||
# Wizard Apprenticeship Charter
|
||||
|
||||
Date: April 4, 2026
|
||||
Context: This charter turns the April 4 user audit into a training doctrine for the active wizard team.
|
||||
|
||||
This system does not need more wizard identities. It needs stronger wizard habits.
|
||||
|
||||
The goal of this charter is to teach each wizard toward higher leverage without flattening them into the same general-purpose agent. Training should sharpen the lane, not erase it.
|
||||
|
||||
This document is downstream from:
|
||||
- the direction shift in `the-nexus` issue `#542`
|
||||
- the user audit in [USER_AUDIT_2026-04-04.md](USER_AUDIT_2026-04-04.md)
|
||||
|
||||
## Training Priorities
|
||||
|
||||
All training should improve one or more of the three current jobs:
|
||||
- Heartbeat
|
||||
- Harness
|
||||
- Portal Interface
|
||||
|
||||
Anything that does not improve one of those jobs is background noise, not apprenticeship.
|
||||
|
||||
## Core Skills Every Wizard Needs
|
||||
|
||||
Every active wizard should be trained on these baseline skills, regardless of lane:
|
||||
- Scope control: finish the asked problem instead of growing a new one.
|
||||
- Verification discipline: prove behavior, not just intent.
|
||||
- Review hygiene: leave a PR or issue summary that another wizard can understand quickly.
|
||||
- Repo-boundary awareness: know what belongs in `timmy-home`, `timmy-config`, Hermes, and `the-nexus`.
|
||||
- Escalation discipline: ask for Timmy or Allegro judgment before crossing into governance, release, or identity surfaces.
|
||||
- Deduplication: collapse overlap instead of multiplying backlog and PRs.
|
||||
|
||||
## Missing Skills By Wizard
|
||||
|
||||
### Timmy
|
||||
|
||||
Primary lane:
|
||||
- sovereignty
|
||||
- architecture
|
||||
- release and rollback judgment
|
||||
|
||||
Train harder on:
|
||||
- delegating routine queue work to Allegro
|
||||
- preserving attention for governing changes
|
||||
|
||||
Do not train toward:
|
||||
- routine backlog maintenance
|
||||
- acting as a mechanical triager
|
||||
|
||||
### Allegro
|
||||
|
||||
Primary lane:
|
||||
- dispatch
|
||||
- queue hygiene
|
||||
- review routing
|
||||
- operational tempo
|
||||
|
||||
Train harder on:
|
||||
- choosing the best next move, not just any move
|
||||
- recognizing when work belongs back with Timmy
|
||||
- collapsing duplicate issues and duplicate PR momentum
|
||||
|
||||
Do not train toward:
|
||||
- final architecture judgment
|
||||
- unsupervised product-code ownership
|
||||
|
||||
### Perplexity
|
||||
|
||||
Primary lane:
|
||||
- research triage
|
||||
- integration comparisons
|
||||
- architecture memos
|
||||
|
||||
Train harder on:
|
||||
- compressing research into action
|
||||
- collapsing duplicates before opening new backlog
|
||||
- making build-vs-borrow tradeoffs explicit
|
||||
|
||||
Do not train toward:
|
||||
- wide unsupervised issue generation
|
||||
- standing in for a builder
|
||||
|
||||
### Ezra
|
||||
|
||||
Primary lane:
|
||||
- archive
|
||||
- RCA
|
||||
- onboarding
|
||||
- durable operating memory
|
||||
|
||||
Train harder on:
|
||||
- extracting reusable lessons from sessions and merges
|
||||
- turning failure history into doctrine
|
||||
- producing onboarding artifacts that reduce future confusion
|
||||
|
||||
Do not train toward:
|
||||
- primary implementation ownership on broad tickets
|
||||
|
||||
### KimiClaw
|
||||
|
||||
Primary lane:
|
||||
- long-context reading
|
||||
- extraction
|
||||
- synthesis
|
||||
|
||||
Train harder on:
|
||||
- crisp handoffs to builders
|
||||
- compressing large context into a smaller decision surface
|
||||
- naming what is known, inferred, and still missing
|
||||
|
||||
Do not train toward:
|
||||
- generic architecture wandering
|
||||
- critical-path implementation without tight scope
|
||||
|
||||
### Codex Agent
|
||||
|
||||
Primary lane:
|
||||
- cleanup
|
||||
- migration verification
|
||||
- repo-boundary enforcement
|
||||
- workflow hardening
|
||||
|
||||
Train harder on:
|
||||
- proving live truth against repo intent
|
||||
- cutting dead code without collateral damage
|
||||
- leaving high-quality PR trails for review
|
||||
|
||||
Do not train toward:
|
||||
- speculative backlog growth
|
||||
|
||||
### Groq
|
||||
|
||||
Primary lane:
|
||||
- fast bounded implementation
|
||||
- tactical fixes
|
||||
- small feature slices
|
||||
|
||||
Train harder on:
|
||||
- verification under time pressure
|
||||
- stopping when ambiguity rises
|
||||
- keeping blast radius tight
|
||||
|
||||
Do not train toward:
|
||||
- broad architecture ownership
|
||||
|
||||
### Manus
|
||||
|
||||
Primary lane:
|
||||
- dependable moderate-scope execution
|
||||
- follow-through
|
||||
|
||||
Train harder on:
|
||||
- escalation when scope stops being moderate
|
||||
- stronger implementation summaries
|
||||
|
||||
Do not train toward:
|
||||
- sprawling multi-repo ownership
|
||||
|
||||
### Claude
|
||||
|
||||
Primary lane:
|
||||
- hard refactors
|
||||
- deep implementation
|
||||
- test-heavy code changes
|
||||
|
||||
Train harder on:
|
||||
- tighter scope obedience
|
||||
- better visibility of blast radius
|
||||
- disciplined follow-through instead of large creative drift
|
||||
|
||||
Do not train toward:
|
||||
- self-directed issue farming
|
||||
- unsupervised architecture sprawl
|
||||
|
||||
### Gemini
|
||||
|
||||
Primary lane:
|
||||
- frontier architecture
|
||||
- long-range design
|
||||
- prototype framing
|
||||
|
||||
Train harder on:
|
||||
- decision compression
|
||||
- architecture recommendations that builders can actually execute
|
||||
- backlog collapse before expansion
|
||||
|
||||
Do not train toward:
|
||||
- unsupervised backlog flood
|
||||
|
||||
### Grok
|
||||
|
||||
Primary lane:
|
||||
- adversarial review
|
||||
- edge cases
|
||||
- provocative alternate angles
|
||||
|
||||
Train harder on:
|
||||
- separating real risks from entertaining risks
|
||||
- making critiques actionable
|
||||
|
||||
Do not train toward:
|
||||
- primary stable delivery ownership
|
||||
|
||||
## Drills
|
||||
|
||||
These are the training drills that should repeat across the system:
|
||||
|
||||
### Drill 1: Scope Collapse
|
||||
|
||||
Prompt a wizard to:
|
||||
- restate the task in one paragraph
|
||||
- name what is out of scope
|
||||
- name the smallest reviewable change
|
||||
|
||||
Pass condition:
|
||||
- the proposed work becomes smaller and clearer
|
||||
|
||||
### Drill 2: Verification First
|
||||
|
||||
Prompt a wizard to:
|
||||
- say how it will prove success before it edits
|
||||
- say what command, test, or artifact would falsify its claim
|
||||
|
||||
Pass condition:
|
||||
- the wizard describes concrete evidence rather than vague confidence
|
||||
|
||||
### Drill 3: Boundary Check
|
||||
|
||||
Prompt a wizard to classify each proposed change as:
|
||||
- identity/config
|
||||
- lived work/data
|
||||
- harness substrate
|
||||
- portal/product interface
|
||||
|
||||
Pass condition:
|
||||
- the wizard routes work to the right repo and escalates cross-boundary changes
|
||||
|
||||
### Drill 4: Duplicate Collapse
|
||||
|
||||
Prompt a wizard to:
|
||||
- find existing issues, PRs, docs, or sessions that overlap
|
||||
- recommend merge, close, supersede, or continue
|
||||
|
||||
Pass condition:
|
||||
- backlog gets smaller or more coherent
|
||||
|
||||
### Drill 5: Review Handoff
|
||||
|
||||
Prompt a wizard to summarize:
|
||||
- what changed
|
||||
- how it was verified
|
||||
- remaining risks
|
||||
- what needs Timmy or Allegro judgment
|
||||
|
||||
Pass condition:
|
||||
- another wizard can review without re-deriving the whole context
|
||||
|
||||
## Coaching Loops
|
||||
|
||||
Timmy should coach:
|
||||
- sovereignty
|
||||
- architecture boundaries
|
||||
- release judgment
|
||||
|
||||
Allegro should coach:
|
||||
- dispatch
|
||||
- queue hygiene
|
||||
- duplicate collapse
|
||||
- operational next-move selection
|
||||
|
||||
Ezra should coach:
|
||||
- memory
|
||||
- RCA
|
||||
- onboarding quality
|
||||
|
||||
Perplexity should coach:
|
||||
- research compression
|
||||
- build-vs-borrow comparisons
|
||||
|
||||
## Success Signals
|
||||
|
||||
The apprenticeship program is working if:
|
||||
- duplicate issue creation drops
|
||||
- builders receive clearer, smaller assignments
|
||||
- PRs show stronger verification summaries
|
||||
- Timmy spends less time on routine queue work
|
||||
- Allegro spends less time untangling ambiguous assignments
|
||||
- merged work aligns more tightly with Heartbeat, Harness, and Portal
|
||||
|
||||
## Anti-Goal
|
||||
|
||||
Do not train every wizard into the same shape.
|
||||
|
||||
The point is not to make every wizard equally good at everything.
|
||||
The point is to make each wizard more reliable inside the lane where it compounds value.
|
||||
23
dropbox/wizards
Normal file
23
dropbox/wizards
Normal file
@@ -0,0 +1,23 @@
|
||||
Done! Congratulations on your new bot. You will find it at t.me/EzraTimeBot. You can now add a description, about section and profile picture for your bot, see /help for a list of commands. By the way, when you've finished creating your cool bot, ping our Bot Support if you want a better username for it. Just make sure the bot is fully operational before you do this.
|
||||
|
||||
Use this token to access the HTTP API:
|
||||
8303963605:AAGb6fP2sw0GtPWaLZp9tt4iI-ZglTFodZg
|
||||
Keep your token secure and store it safely, it can be used by anyone to control your bot.
|
||||
|
||||
For a description of the Bot API, see this page: https://core.telegram.org/bots/api
|
||||
|
||||
|
||||
Done! Congratulations on your new bot. You will find it at t.me/BezazelTimeBot. You can now add a description, about section and profile picture for your bot, see /help for a list of commands. By the way, when you've finished creating your cool bot, ping our Bot Support if you want a better username for it. Just make sure the bot is fully operational before you do this.
|
||||
|
||||
Use this token to access the HTTP API:
|
||||
8696348349:AAHA8KlcttMCye3PN_BPX8pwpPIlMf0vRdw
|
||||
Keep your token secure and store it safely, it can be used by anyone to control your bot.
|
||||
|
||||
For a description of the Bot API, see this page: https://core.telegram.org/bots/api
|
||||
|
||||
|
||||
Done! Congratulations on your new bot. You will find it at t.me/AllegroTimeBot. You can now add a description, about section and profile picture for your bot, see /help for a list of commands. By the way, when you've finished creating your cool bot, ping our Bot Support if you want a better username for it. Just make sure the bot is fully operational before you do this.
|
||||
|
||||
Use this token to access the HTTP API:
|
||||
8528070173:AAFrGRb9YxD4XOFEYQhjq_8Cv4zjdqhN5eI
|
||||
Keep your token secure and store it safely, it can be used by anyone to control your bot.
|
||||
162
epics/EPIC-202-claw-agent.md
Normal file
162
epics/EPIC-202-claw-agent.md
Normal file
@@ -0,0 +1,162 @@
|
||||
# EPIC-202: Build Claw-Architecture Agent
|
||||
|
||||
**Status:** In Progress
|
||||
**Priority:** P0
|
||||
**Milestone:** M1: Core Architecture
|
||||
**Created:** 2026-03-31
|
||||
**Author:** Allegro
|
||||
|
||||
---
|
||||
|
||||
## Objective
|
||||
|
||||
Create a NEW autonomous agent using architectural patterns from [Claw Code](http://143.198.27.163:3000/Timmy/claw-code), integrated with Gitea for real work dispatch.
|
||||
|
||||
## Problem Statement
|
||||
|
||||
**Allegro-Primus is IDLE.**
|
||||
- Gateway running (PID 367883) but zero meaningful output
|
||||
- No Gitea issues created
|
||||
- No PRs submitted
|
||||
- No actual work completed
|
||||
|
||||
This agent will **replace** Allegro-Primus with real capabilities.
|
||||
|
||||
---
|
||||
|
||||
## Claw Patterns to Adopt
|
||||
|
||||
### 1. ToolPermissionContext
|
||||
```python
|
||||
@dataclass
|
||||
class ToolPermissionContext:
|
||||
deny_tools: set[str]
|
||||
deny_prefixes: tuple[str, ...]
|
||||
|
||||
def blocks(self, tool_name: str) -> bool:
|
||||
return tool_name in self.deny_tools or \
|
||||
any(tool_name.startswith(p) for p in self.deny_prefixes)
|
||||
```
|
||||
|
||||
**Why:** Fine-grained tool access control vs Hermes basic approval
|
||||
|
||||
### 2. ExecutionRegistry
|
||||
```python
|
||||
class ExecutionRegistry:
|
||||
def command(self, name: str) -> CommandHandler
|
||||
def tool(self, name: str) -> ToolHandler
|
||||
def execute(self, context: PermissionContext) -> Result
|
||||
```
|
||||
|
||||
**Why:** Clean routing vs Hermes model-decided routing
|
||||
|
||||
### 3. Session Persistence
|
||||
```python
|
||||
@dataclass
|
||||
class RuntimeSession:
|
||||
prompt: str
|
||||
context: PortContext
|
||||
history: HistoryLog
|
||||
persisted_path: str
|
||||
```
|
||||
|
||||
**Why:** JSON-based sessions vs SQLite - more portable, inspectable
|
||||
|
||||
### 4. Bootstrap Graph
|
||||
```python
|
||||
def build_bootstrap_graph() -> Graph:
|
||||
# Setup phases
|
||||
# Context building
|
||||
# System init messages
|
||||
```
|
||||
|
||||
**Why:** Structured initialization vs ad-hoc setup
|
||||
|
||||
---
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Phase 1: Core Architecture (2 days)
|
||||
- [ ] Create new Hermes profile: `claw-agent`
|
||||
- [ ] Implement ToolPermissionContext
|
||||
- [ ] Create ExecutionRegistry
|
||||
- [ ] Build Session persistence layer
|
||||
|
||||
### Phase 2: Gitea Integration (2 days)
|
||||
- [ ] Gitea client with issue querying
|
||||
- [ ] Work scheduler for autonomous cycles
|
||||
- [ ] PR creation and review assistance
|
||||
|
||||
### Phase 3: Deployment (1 day)
|
||||
- [ ] Telegram bot integration
|
||||
- [ ] Cron scheduling
|
||||
- [ ] Health monitoring
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
| Criteria | How We'll Verify |
|
||||
|----------|-----------------|
|
||||
| Receives Telegram tasks | Send test message, agent responds |
|
||||
| Queries Gitea issues | Agent lists open P0 issues |
|
||||
| Permission checks work | Blocked tool returns error |
|
||||
| Session persistence | Restart agent, history intact |
|
||||
| Progress reports | Agent sends Telegram updates |
|
||||
|
||||
---
|
||||
|
||||
## Resource Requirements
|
||||
|
||||
| Resource | Status |
|
||||
|----------|--------|
|
||||
| Gitea API token | ✅ Have |
|
||||
| Kimi API key | ✅ Have |
|
||||
| Telegram bot | ⏳ Need @BotFather |
|
||||
| New profile | ⏳ Will create |
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- [Claw Code Mirror](http://143.198.27.163:3000/Timmy/claw-code)
|
||||
- [Claw Issue #1 - Architecture](http://143.198.27.163:3000/Timmy/claw-code/issues/1)
|
||||
- [Hermes v0.6 Profiles](../docs/profiles.md)
|
||||
|
||||
---
|
||||
|
||||
## Tickets
|
||||
|
||||
- #203: Implement ToolPermissionContext
|
||||
- #204: Create ExecutionRegistry
|
||||
- #205: Build Session Persistence
|
||||
- #206: Gitea Integration
|
||||
- #207: Telegram Deployment
|
||||
|
||||
---
|
||||
|
||||
*This epic supersedes Allegro-Primus who has been idle.*
|
||||
|
||||
---
|
||||
|
||||
## Feedback — 2026-04-06 (Allegro Cross-Epic Review)
|
||||
|
||||
**Health:** 🟡 Yellow
|
||||
**Blocker:** Gitea externally firewalled + no Allegro-Primus RCA
|
||||
|
||||
### Critical Issues
|
||||
|
||||
1. **Dependency blindness.** Every Claw Code reference points to `143.198.27.163:3000`, which is currently firewalled and unreachable from this VM. If the mirror is not locally cached, development is blocked on external infrastructure.
|
||||
2. **Root cause vs. replacement.** The epic jumps to "replace Allegro-Primus" without proving he is unfixable. Primus being idle could be the same provider/auth outage that took down Ezra and Bezalel. A 5-line RCA should precede a 5-phase rewrite.
|
||||
3. **Timeline fantasy.** "Phase 1: 2 days" assumes stable infrastructure. Current reality: Gitea externally firewalled, Bezalel VPS down, Ezra needs webhook switch. This epic needs a "Blocked Until" section.
|
||||
4. **Resource stalemate.** "Telegram bot: Need @BotFather" — the fleet already operates multiple bots. Reuse an existing bot profile or document why a new one is required.
|
||||
|
||||
### Recommended Action
|
||||
|
||||
Add a **Pre-Flight Checklist** to the epic:
|
||||
- [ ] Verify Gitea/Claw Code mirror is reachable from the build VM
|
||||
- [ ] Publish 1-paragraph RCA on why Allegro-Primus is idle
|
||||
- [ ] Confirm target repo for the new agent code
|
||||
|
||||
Do not start Phase 1 until all three are checked.
|
||||
|
||||
56
evennia/timmy_world/.gitignore
vendored
Normal file
56
evennia/timmy_world/.gitignore
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
*.py[cod]
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Packages
|
||||
*.egg
|
||||
*.egg-info
|
||||
dist
|
||||
build
|
||||
eggs
|
||||
parts
|
||||
var
|
||||
sdist
|
||||
develop-eggs
|
||||
.installed.cfg
|
||||
lib
|
||||
lib64
|
||||
__pycache__
|
||||
|
||||
# Other
|
||||
*.swp
|
||||
*.log
|
||||
*.log.*
|
||||
*.pid
|
||||
*.restart
|
||||
*.db3
|
||||
|
||||
# Installation-specific.
|
||||
# For group efforts, comment out some or all of these.
|
||||
server/conf/secret_settings.py
|
||||
server/logs/*.log.*
|
||||
server/.static/*
|
||||
server/.media/*
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
.coverage
|
||||
.tox
|
||||
nosetests.xml
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
|
||||
# Mr Developer
|
||||
.mr.developer.cfg
|
||||
.project
|
||||
.pydevproject
|
||||
|
||||
# PyCharm config
|
||||
.idea
|
||||
|
||||
# VSCode config
|
||||
.vscode
|
||||
40
evennia/timmy_world/README.md
Normal file
40
evennia/timmy_world/README.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Welcome to Evennia!
|
||||
|
||||
This is your game directory, set up to let you start with
|
||||
your new game right away. An overview of this directory is found here:
|
||||
https://github.com/evennia/evennia/wiki/Directory-Overview#the-game-directory
|
||||
|
||||
You can delete this readme file when you've read it and you can
|
||||
re-arrange things in this game-directory to suit your own sense of
|
||||
organisation (the only exception is the directory structure of the
|
||||
`server/` directory, which Evennia expects). If you change the structure
|
||||
you must however also edit/add to your settings file to tell Evennia
|
||||
where to look for things.
|
||||
|
||||
Your game's main configuration file is found in
|
||||
`server/conf/settings.py` (but you don't need to change it to get
|
||||
started). If you just created this directory (which means you'll already
|
||||
have a `virtualenv` running if you followed the default instructions),
|
||||
`cd` to this directory then initialize a new database using
|
||||
|
||||
evennia migrate
|
||||
|
||||
To start the server, stand in this directory and run
|
||||
|
||||
evennia start
|
||||
|
||||
This will start the server, logging output to the console. Make
|
||||
sure to create a superuser when asked. By default you can now connect
|
||||
to your new game using a MUD client on `localhost`, port `4000`. You can
|
||||
also log into the web client by pointing a browser to
|
||||
`http://localhost:4001`.
|
||||
|
||||
# Getting started
|
||||
|
||||
From here on you might want to look at one of the beginner tutorials:
|
||||
http://github.com/evennia/evennia/wiki/Tutorials.
|
||||
|
||||
Evennia's documentation is here:
|
||||
https://github.com/evennia/evennia/wiki.
|
||||
|
||||
Enjoy!
|
||||
14
evennia/timmy_world/commands/README.md
Normal file
14
evennia/timmy_world/commands/README.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# commands/
|
||||
|
||||
This folder holds modules for implementing one's own commands and
|
||||
command sets. All the modules' classes are essentially empty and just
|
||||
imports the default implementations from Evennia; so adding anything
|
||||
to them will start overloading the defaults.
|
||||
|
||||
You can change the organisation of this directory as you see fit, just
|
||||
remember that if you change any of the default command set classes'
|
||||
locations, you need to add the appropriate paths to
|
||||
`server/conf/settings.py` so that Evennia knows where to find them.
|
||||
Also remember that if you create new sub directories you must put
|
||||
(optionally empty) `__init__.py` files in there so that Python can
|
||||
find your modules.
|
||||
0
evennia/timmy_world/commands/__init__.py
Normal file
0
evennia/timmy_world/commands/__init__.py
Normal file
187
evennia/timmy_world/commands/command.py
Normal file
187
evennia/timmy_world/commands/command.py
Normal file
@@ -0,0 +1,187 @@
|
||||
"""
|
||||
Commands
|
||||
|
||||
Commands describe the input the account can do to the game.
|
||||
|
||||
"""
|
||||
|
||||
from evennia.commands.command import Command as BaseCommand
|
||||
|
||||
# from evennia import default_cmds
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""
|
||||
Base command (you may see this if a child command had no help text defined)
|
||||
|
||||
Note that the class's `__doc__` string is used by Evennia to create the
|
||||
automatic help entry for the command, so make sure to document consistently
|
||||
here. Without setting one, the parent's docstring will show (like now).
|
||||
|
||||
"""
|
||||
|
||||
# Each Command class implements the following methods, called in this order
|
||||
# (only func() is actually required):
|
||||
#
|
||||
# - at_pre_cmd(): If this returns anything truthy, execution is aborted.
|
||||
# - parse(): Should perform any extra parsing needed on self.args
|
||||
# and store the result on self.
|
||||
# - func(): Performs the actual work.
|
||||
# - at_post_cmd(): Extra actions, often things done after
|
||||
# every command, like prompts.
|
||||
#
|
||||
pass
|
||||
|
||||
|
||||
# -------------------------------------------------------------
|
||||
#
|
||||
# The default commands inherit from
|
||||
#
|
||||
# evennia.commands.default.muxcommand.MuxCommand.
|
||||
#
|
||||
# If you want to make sweeping changes to default commands you can
|
||||
# uncomment this copy of the MuxCommand parent and add
|
||||
#
|
||||
# COMMAND_DEFAULT_CLASS = "commands.command.MuxCommand"
|
||||
#
|
||||
# to your settings file. Be warned that the default commands expect
|
||||
# the functionality implemented in the parse() method, so be
|
||||
# careful with what you change.
|
||||
#
|
||||
# -------------------------------------------------------------
|
||||
|
||||
# from evennia.utils import utils
|
||||
#
|
||||
#
|
||||
# class MuxCommand(Command):
|
||||
# """
|
||||
# This sets up the basis for a MUX command. The idea
|
||||
# is that most other Mux-related commands should just
|
||||
# inherit from this and don't have to implement much
|
||||
# parsing of their own unless they do something particularly
|
||||
# advanced.
|
||||
#
|
||||
# Note that the class's __doc__ string (this text) is
|
||||
# used by Evennia to create the automatic help entry for
|
||||
# the command, so make sure to document consistently here.
|
||||
# """
|
||||
# def has_perm(self, srcobj):
|
||||
# """
|
||||
# This is called by the cmdhandler to determine
|
||||
# if srcobj is allowed to execute this command.
|
||||
# We just show it here for completeness - we
|
||||
# are satisfied using the default check in Command.
|
||||
# """
|
||||
# return super().has_perm(srcobj)
|
||||
#
|
||||
# def at_pre_cmd(self):
|
||||
# """
|
||||
# This hook is called before self.parse() on all commands
|
||||
# """
|
||||
# pass
|
||||
#
|
||||
# def at_post_cmd(self):
|
||||
# """
|
||||
# This hook is called after the command has finished executing
|
||||
# (after self.func()).
|
||||
# """
|
||||
# pass
|
||||
#
|
||||
# def parse(self):
|
||||
# """
|
||||
# This method is called by the cmdhandler once the command name
|
||||
# has been identified. It creates a new set of member variables
|
||||
# that can be later accessed from self.func() (see below)
|
||||
#
|
||||
# The following variables are available for our use when entering this
|
||||
# method (from the command definition, and assigned on the fly by the
|
||||
# cmdhandler):
|
||||
# self.key - the name of this command ('look')
|
||||
# self.aliases - the aliases of this cmd ('l')
|
||||
# self.permissions - permission string for this command
|
||||
# self.help_category - overall category of command
|
||||
#
|
||||
# self.caller - the object calling this command
|
||||
# self.cmdstring - the actual command name used to call this
|
||||
# (this allows you to know which alias was used,
|
||||
# for example)
|
||||
# self.args - the raw input; everything following self.cmdstring.
|
||||
# self.cmdset - the cmdset from which this command was picked. Not
|
||||
# often used (useful for commands like 'help' or to
|
||||
# list all available commands etc)
|
||||
# self.obj - the object on which this command was defined. It is often
|
||||
# the same as self.caller.
|
||||
#
|
||||
# A MUX command has the following possible syntax:
|
||||
#
|
||||
# name[ with several words][/switch[/switch..]] arg1[,arg2,...] [[=|,] arg[,..]]
|
||||
#
|
||||
# The 'name[ with several words]' part is already dealt with by the
|
||||
# cmdhandler at this point, and stored in self.cmdname (we don't use
|
||||
# it here). The rest of the command is stored in self.args, which can
|
||||
# start with the switch indicator /.
|
||||
#
|
||||
# This parser breaks self.args into its constituents and stores them in the
|
||||
# following variables:
|
||||
# self.switches = [list of /switches (without the /)]
|
||||
# self.raw = This is the raw argument input, including switches
|
||||
# self.args = This is re-defined to be everything *except* the switches
|
||||
# self.lhs = Everything to the left of = (lhs:'left-hand side'). If
|
||||
# no = is found, this is identical to self.args.
|
||||
# self.rhs: Everything to the right of = (rhs:'right-hand side').
|
||||
# If no '=' is found, this is None.
|
||||
# self.lhslist - [self.lhs split into a list by comma]
|
||||
# self.rhslist - [list of self.rhs split into a list by comma]
|
||||
# self.arglist = [list of space-separated args (stripped, including '=' if it exists)]
|
||||
#
|
||||
# All args and list members are stripped of excess whitespace around the
|
||||
# strings, but case is preserved.
|
||||
# """
|
||||
# raw = self.args
|
||||
# args = raw.strip()
|
||||
#
|
||||
# # split out switches
|
||||
# switches = []
|
||||
# if args and len(args) > 1 and args[0] == "/":
|
||||
# # we have a switch, or a set of switches. These end with a space.
|
||||
# switches = args[1:].split(None, 1)
|
||||
# if len(switches) > 1:
|
||||
# switches, args = switches
|
||||
# switches = switches.split('/')
|
||||
# else:
|
||||
# args = ""
|
||||
# switches = switches[0].split('/')
|
||||
# arglist = [arg.strip() for arg in args.split()]
|
||||
#
|
||||
# # check for arg1, arg2, ... = argA, argB, ... constructs
|
||||
# lhs, rhs = args, None
|
||||
# lhslist, rhslist = [arg.strip() for arg in args.split(',')], []
|
||||
# if args and '=' in args:
|
||||
# lhs, rhs = [arg.strip() for arg in args.split('=', 1)]
|
||||
# lhslist = [arg.strip() for arg in lhs.split(',')]
|
||||
# rhslist = [arg.strip() for arg in rhs.split(',')]
|
||||
#
|
||||
# # save to object properties:
|
||||
# self.raw = raw
|
||||
# self.switches = switches
|
||||
# self.args = args.strip()
|
||||
# self.arglist = arglist
|
||||
# self.lhs = lhs
|
||||
# self.lhslist = lhslist
|
||||
# self.rhs = rhs
|
||||
# self.rhslist = rhslist
|
||||
#
|
||||
# # if the class has the account_caller property set on itself, we make
|
||||
# # sure that self.caller is always the account if possible. We also create
|
||||
# # a special property "character" for the puppeted object, if any. This
|
||||
# # is convenient for commands defined on the Account only.
|
||||
# if hasattr(self, "account_caller") and self.account_caller:
|
||||
# if utils.inherits_from(self.caller, "evennia.objects.objects.DefaultObject"):
|
||||
# # caller is an Object/Character
|
||||
# self.character = self.caller
|
||||
# self.caller = self.caller.account
|
||||
# elif utils.inherits_from(self.caller, "evennia.accounts.accounts.DefaultAccount"):
|
||||
# # caller was already an Account
|
||||
# self.character = self.caller.get_puppet(self.session)
|
||||
# else:
|
||||
# self.character = None
|
||||
96
evennia/timmy_world/commands/default_cmdsets.py
Normal file
96
evennia/timmy_world/commands/default_cmdsets.py
Normal file
@@ -0,0 +1,96 @@
|
||||
"""
|
||||
Command sets
|
||||
|
||||
All commands in the game must be grouped in a cmdset. A given command
|
||||
can be part of any number of cmdsets and cmdsets can be added/removed
|
||||
and merged onto entities at runtime.
|
||||
|
||||
To create new commands to populate the cmdset, see
|
||||
`commands/command.py`.
|
||||
|
||||
This module wraps the default command sets of Evennia; overloads them
|
||||
to add/remove commands from the default lineup. You can create your
|
||||
own cmdsets by inheriting from them or directly from `evennia.CmdSet`.
|
||||
|
||||
"""
|
||||
|
||||
from evennia import default_cmds
|
||||
|
||||
|
||||
class CharacterCmdSet(default_cmds.CharacterCmdSet):
|
||||
"""
|
||||
The `CharacterCmdSet` contains general in-game commands like `look`,
|
||||
`get`, etc available on in-game Character objects. It is merged with
|
||||
the `AccountCmdSet` when an Account puppets a Character.
|
||||
"""
|
||||
|
||||
key = "DefaultCharacter"
|
||||
|
||||
def at_cmdset_creation(self):
|
||||
"""
|
||||
Populates the cmdset
|
||||
"""
|
||||
super().at_cmdset_creation()
|
||||
#
|
||||
# any commands you add below will overload the default ones.
|
||||
#
|
||||
|
||||
|
||||
class AccountCmdSet(default_cmds.AccountCmdSet):
|
||||
"""
|
||||
This is the cmdset available to the Account at all times. It is
|
||||
combined with the `CharacterCmdSet` when the Account puppets a
|
||||
Character. It holds game-account-specific commands, channel
|
||||
commands, etc.
|
||||
"""
|
||||
|
||||
key = "DefaultAccount"
|
||||
|
||||
def at_cmdset_creation(self):
|
||||
"""
|
||||
Populates the cmdset
|
||||
"""
|
||||
super().at_cmdset_creation()
|
||||
#
|
||||
# any commands you add below will overload the default ones.
|
||||
#
|
||||
|
||||
|
||||
class UnloggedinCmdSet(default_cmds.UnloggedinCmdSet):
|
||||
"""
|
||||
Command set available to the Session before being logged in. This
|
||||
holds commands like creating a new account, logging in, etc.
|
||||
"""
|
||||
|
||||
key = "DefaultUnloggedin"
|
||||
|
||||
def at_cmdset_creation(self):
|
||||
"""
|
||||
Populates the cmdset
|
||||
"""
|
||||
super().at_cmdset_creation()
|
||||
#
|
||||
# any commands you add below will overload the default ones.
|
||||
#
|
||||
|
||||
|
||||
class SessionCmdSet(default_cmds.SessionCmdSet):
|
||||
"""
|
||||
This cmdset is made available on Session level once logged in. It
|
||||
is empty by default.
|
||||
"""
|
||||
|
||||
key = "DefaultSession"
|
||||
|
||||
def at_cmdset_creation(self):
|
||||
"""
|
||||
This is the only method defined in a cmdset, called during
|
||||
its creation. It should populate the set with command instances.
|
||||
|
||||
As and example we just add the empty base `Command` object.
|
||||
It prints some info.
|
||||
"""
|
||||
super().at_cmdset_creation()
|
||||
#
|
||||
# any commands you add below will overload the default ones.
|
||||
#
|
||||
38
evennia/timmy_world/server/README.md
Normal file
38
evennia/timmy_world/server/README.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# server/
|
||||
|
||||
This directory holds files used by and configuring the Evennia server
|
||||
itself.
|
||||
|
||||
Out of all the subdirectories in the game directory, Evennia does
|
||||
expect this directory to exist, so you should normally not delete,
|
||||
rename or change its folder structure.
|
||||
|
||||
When running you will find four new files appear in this directory:
|
||||
|
||||
- `server.pid` and `portal.pid`: These hold the process IDs of the
|
||||
Portal and Server, so that they can be managed by the launcher. If
|
||||
Evennia is shut down uncleanly (e.g. by a crash or via a kill
|
||||
signal), these files might erroneously remain behind. If so Evennia
|
||||
will tell you they are "stale" and they can be deleted manually.
|
||||
- `server.restart` and `portal.restart`: These hold flags to tell the
|
||||
server processes if it should die or start again. You never need to
|
||||
modify those files.
|
||||
- `evennia.db3`: This will only appear if you are using the default
|
||||
SQLite3 database; it a binary file that holds the entire game
|
||||
database; deleting this file will effectively reset the game for
|
||||
you and you can start fresh with `evennia migrate` (useful during
|
||||
development).
|
||||
|
||||
## server/conf/
|
||||
|
||||
This subdirectory holds the configuration modules for the server. With
|
||||
them you can change how Evennia operates and also plug in your own
|
||||
functionality to replace the default. You usually need to restart the
|
||||
server to apply changes done here. The most important file is the file
|
||||
`settings.py` which is the main configuration file of Evennia.
|
||||
|
||||
## server/logs/
|
||||
|
||||
This subdirectory holds various log files created by the running
|
||||
Evennia server. It is also the default location for storing any custom
|
||||
log files you might want to output using Evennia's logging mechanisms.
|
||||
1
evennia/timmy_world/server/__init__.py
Normal file
1
evennia/timmy_world/server/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
1
evennia/timmy_world/server/conf/__init__.py
Normal file
1
evennia/timmy_world/server/conf/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
19
evennia/timmy_world/server/conf/at_initial_setup.py
Normal file
19
evennia/timmy_world/server/conf/at_initial_setup.py
Normal file
@@ -0,0 +1,19 @@
|
||||
"""
|
||||
At_initial_setup module template
|
||||
|
||||
Custom at_initial_setup method. This allows you to hook special
|
||||
modifications to the initial server startup process. Note that this
|
||||
will only be run once - when the server starts up for the very first
|
||||
time! It is called last in the startup process and can thus be used to
|
||||
overload things that happened before it.
|
||||
|
||||
The module must contain a global function at_initial_setup(). This
|
||||
will be called without arguments. Note that tracebacks in this module
|
||||
will be QUIETLY ignored, so make sure to check it well to make sure it
|
||||
does what you expect it to.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def at_initial_setup():
|
||||
pass
|
||||
54
evennia/timmy_world/server/conf/at_search.py
Normal file
54
evennia/timmy_world/server/conf/at_search.py
Normal file
@@ -0,0 +1,54 @@
|
||||
"""
|
||||
Search and multimatch handling
|
||||
|
||||
This module allows for overloading two functions used by Evennia's
|
||||
search functionality:
|
||||
|
||||
at_search_result:
|
||||
This is called whenever a result is returned from an object
|
||||
search (a common operation in commands). It should (together
|
||||
with at_multimatch_input below) define some way to present and
|
||||
differentiate between multiple matches (by default these are
|
||||
presented as 1-ball, 2-ball etc)
|
||||
at_multimatch_input:
|
||||
This is called with a search term and should be able to
|
||||
identify if the user wants to separate a multimatch-result
|
||||
(such as that from a previous search). By default, this
|
||||
function understands input on the form 1-ball, 2-ball etc as
|
||||
indicating that the 1st or 2nd match for "ball" should be
|
||||
used.
|
||||
|
||||
This module is not called by default, to use it, add the following
|
||||
line to your settings file:
|
||||
|
||||
SEARCH_AT_RESULT = "server.conf.at_search.at_search_result"
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def at_search_result(matches, caller, query="", quiet=False, **kwargs):
|
||||
"""
|
||||
This is a generic hook for handling all processing of a search
|
||||
result, including error reporting.
|
||||
|
||||
Args:
|
||||
matches (list): This is a list of 0, 1 or more typeclass instances,
|
||||
the matched result of the search. If 0, a nomatch error should
|
||||
be echoed, and if >1, multimatch errors should be given. Only
|
||||
if a single match should the result pass through.
|
||||
caller (Object): The object performing the search and/or which should
|
||||
receive error messages.
|
||||
query (str, optional): The search query used to produce `matches`.
|
||||
quiet (bool, optional): If `True`, no messages will be echoed to caller
|
||||
on errors.
|
||||
|
||||
Keyword Args:
|
||||
nofound_string (str): Replacement string to echo on a notfound error.
|
||||
multimatch_string (str): Replacement string to echo on a multimatch error.
|
||||
|
||||
Returns:
|
||||
processed_result (Object or None): This is always a single result
|
||||
or `None`. If `None`, any error reporting/handling should
|
||||
already have happened.
|
||||
|
||||
"""
|
||||
71
evennia/timmy_world/server/conf/at_server_startstop.py
Normal file
71
evennia/timmy_world/server/conf/at_server_startstop.py
Normal file
@@ -0,0 +1,71 @@
|
||||
"""
|
||||
Server startstop hooks
|
||||
|
||||
This module contains functions called by Evennia at various
|
||||
points during its startup, reload and shutdown sequence. It
|
||||
allows for customizing the server operation as desired.
|
||||
|
||||
This module must contain at least these global functions:
|
||||
|
||||
at_server_init()
|
||||
at_server_start()
|
||||
at_server_stop()
|
||||
at_server_reload_start()
|
||||
at_server_reload_stop()
|
||||
at_server_cold_start()
|
||||
at_server_cold_stop()
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def at_server_init():
|
||||
"""
|
||||
This is called first as the server is starting up, regardless of how.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def at_server_start():
|
||||
"""
|
||||
This is called every time the server starts up, regardless of
|
||||
how it was shut down.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def at_server_stop():
|
||||
"""
|
||||
This is called just before the server is shut down, regardless
|
||||
of it is for a reload, reset or shutdown.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def at_server_reload_start():
|
||||
"""
|
||||
This is called only when server starts back up after a reload.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def at_server_reload_stop():
|
||||
"""
|
||||
This is called only time the server stops before a reload.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def at_server_cold_start():
|
||||
"""
|
||||
This is called only when the server starts "cold", i.e. after a
|
||||
shutdown or a reset.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def at_server_cold_stop():
|
||||
"""
|
||||
This is called only when the server goes down due to a shutdown or
|
||||
reset.
|
||||
"""
|
||||
pass
|
||||
55
evennia/timmy_world/server/conf/cmdparser.py
Normal file
55
evennia/timmy_world/server/conf/cmdparser.py
Normal file
@@ -0,0 +1,55 @@
|
||||
"""
|
||||
Changing the default command parser
|
||||
|
||||
The cmdparser is responsible for parsing the raw text inserted by the
|
||||
user, identifying which command/commands match and return one or more
|
||||
matching command objects. It is called by Evennia's cmdhandler and
|
||||
must accept input and return results on the same form. The default
|
||||
handler is very generic so you usually don't need to overload this
|
||||
unless you have very exotic parsing needs; advanced parsing is best
|
||||
done at the Command.parse level.
|
||||
|
||||
The default cmdparser understands the following command combinations
|
||||
(where [] marks optional parts.)
|
||||
|
||||
[cmdname[ cmdname2 cmdname3 ...] [the rest]
|
||||
|
||||
A command may consist of any number of space-separated words of any
|
||||
length, and contain any character. It may also be empty.
|
||||
|
||||
The parser makes use of the cmdset to find command candidates. The
|
||||
parser return a list of matches. Each match is a tuple with its first
|
||||
three elements being the parsed cmdname (lower case), the remaining
|
||||
arguments, and the matched cmdobject from the cmdset.
|
||||
|
||||
|
||||
This module is not accessed by default. To tell Evennia to use it
|
||||
instead of the default command parser, add the following line to
|
||||
your settings file:
|
||||
|
||||
COMMAND_PARSER = "server.conf.cmdparser.cmdparser"
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def cmdparser(raw_string, cmdset, caller, match_index=None):
|
||||
"""
|
||||
This function is called by the cmdhandler once it has
|
||||
gathered and merged all valid cmdsets valid for this particular parsing.
|
||||
|
||||
raw_string - the unparsed text entered by the caller.
|
||||
cmdset - the merged, currently valid cmdset
|
||||
caller - the caller triggering this parsing
|
||||
match_index - an optional integer index to pick a given match in a
|
||||
list of same-named command matches.
|
||||
|
||||
Returns:
|
||||
list of tuples: [(cmdname, args, cmdobj, cmdlen, mratio), ...]
|
||||
where cmdname is the matching command name and args is
|
||||
everything not included in the cmdname. Cmdobj is the actual
|
||||
command instance taken from the cmdset, cmdlen is the length
|
||||
of the command name and the mratio is some quality value to
|
||||
(possibly) separate multiple matches.
|
||||
|
||||
"""
|
||||
# Your implementation here
|
||||
40
evennia/timmy_world/server/conf/connection_screens.py
Normal file
40
evennia/timmy_world/server/conf/connection_screens.py
Normal file
@@ -0,0 +1,40 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Connection screen
|
||||
|
||||
This is the text to show the user when they first connect to the game (before
|
||||
they log in).
|
||||
|
||||
To change the login screen in this module, do one of the following:
|
||||
|
||||
- Define a function `connection_screen()`, taking no arguments. This will be
|
||||
called first and must return the full string to act as the connection screen.
|
||||
This can be used to produce more dynamic screens.
|
||||
- Alternatively, define a string variable in the outermost scope of this module
|
||||
with the connection string that should be displayed. If more than one such
|
||||
variable is given, Evennia will pick one of them at random.
|
||||
|
||||
The commands available to the user when the connection screen is shown
|
||||
are defined in evennia.default_cmds.UnloggedinCmdSet. The parsing and display
|
||||
of the screen is done by the unlogged-in "look" command.
|
||||
|
||||
"""
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from evennia import utils
|
||||
|
||||
CONNECTION_SCREEN = """
|
||||
|b==============================================================|n
|
||||
Welcome to |g{}|n, version {}!
|
||||
|
||||
If you have an existing account, connect to it by typing:
|
||||
|wconnect <username> <password>|n
|
||||
If you need to create an account, type (without the <>'s):
|
||||
|wcreate <username> <password>|n
|
||||
|
||||
If you have spaces in your username, enclose it in quotes.
|
||||
Enter |whelp|n for more info. |wlook|n will re-show this screen.
|
||||
|b==============================================================|n""".format(
|
||||
settings.SERVERNAME, utils.get_evennia_version("short")
|
||||
)
|
||||
39
evennia/timmy_world/server/conf/inlinefuncs.py
Normal file
39
evennia/timmy_world/server/conf/inlinefuncs.py
Normal file
@@ -0,0 +1,39 @@
|
||||
"""
|
||||
Outgoing callables to apply with the FuncParser on outgoing messages.
|
||||
|
||||
The functions in this module will become available as $funcname(args, kwargs)
|
||||
in all outgoing strings if you add
|
||||
|
||||
FUNCPARSER_PARSE_OUTGOING_MESSAGES_ENABLED = True
|
||||
|
||||
to your settings file. The default inlinefuncs are found at the bottom of
|
||||
`evennia.utils.funcparser`.
|
||||
|
||||
In text, usage is straightforward:
|
||||
|
||||
$funcname(arg1, arg2, ..., key=val, key2=val2, ...)
|
||||
|
||||
Example 1 (using the "pad" inlinefunc):
|
||||
say This is $pad("a center-padded text", 50,c,-) of width 50.
|
||||
->
|
||||
John says, "This is -------------- a center-padded text--------------- of width 50."
|
||||
|
||||
Example 2 (using nested "pad" and "time" inlinefuncs):
|
||||
say The time is $pad($time(), 30)right now.
|
||||
->
|
||||
John says, "The time is Oct 25, 11:09 right now."
|
||||
|
||||
To add more inline functions, add them to this module, using
|
||||
the following call signature:
|
||||
|
||||
def funcname(*args, **kwargs)
|
||||
...
|
||||
|
||||
"""
|
||||
|
||||
# def capitalize(*args, **kwargs):
|
||||
# "Silly capitalize example. Used as $capitalize
|
||||
# if not args:
|
||||
# return ''
|
||||
# session = kwargs.get("session")
|
||||
# return args[0].capitalize()
|
||||
52
evennia/timmy_world/server/conf/inputfuncs.py
Normal file
52
evennia/timmy_world/server/conf/inputfuncs.py
Normal file
@@ -0,0 +1,52 @@
|
||||
"""
|
||||
Input functions
|
||||
|
||||
Input functions are always called from the client (they handle server
|
||||
input, hence the name).
|
||||
|
||||
This module is loaded by being included in the
|
||||
`settings.INPUT_FUNC_MODULES` tuple.
|
||||
|
||||
All *global functions* included in this module are considered
|
||||
input-handler functions and can be called by the client to handle
|
||||
input.
|
||||
|
||||
An input function must have the following call signature:
|
||||
|
||||
cmdname(session, *args, **kwargs)
|
||||
|
||||
Where session will be the active session and *args, **kwargs are extra
|
||||
incoming arguments and keyword properties.
|
||||
|
||||
A special command is the "default" command, which is will be called
|
||||
when no other cmdname matches. It also receives the non-found cmdname
|
||||
as argument.
|
||||
|
||||
default(session, cmdname, *args, **kwargs)
|
||||
|
||||
"""
|
||||
|
||||
# def oob_echo(session, *args, **kwargs):
|
||||
# """
|
||||
# Example echo function. Echoes args, kwargs sent to it.
|
||||
#
|
||||
# Args:
|
||||
# session (Session): The Session to receive the echo.
|
||||
# args (list of str): Echo text.
|
||||
# kwargs (dict of str, optional): Keyed echo text
|
||||
#
|
||||
# """
|
||||
# session.msg(oob=("echo", args, kwargs))
|
||||
#
|
||||
#
|
||||
# def default(session, cmdname, *args, **kwargs):
|
||||
# """
|
||||
# Handles commands without a matching inputhandler func.
|
||||
#
|
||||
# Args:
|
||||
# session (Session): The active Session.
|
||||
# cmdname (str): The (unmatched) command name
|
||||
# args, kwargs (any): Arguments to function.
|
||||
#
|
||||
# """
|
||||
# pass
|
||||
30
evennia/timmy_world/server/conf/lockfuncs.py
Normal file
30
evennia/timmy_world/server/conf/lockfuncs.py
Normal file
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
|
||||
Lockfuncs
|
||||
|
||||
Lock functions are functions available when defining lock strings,
|
||||
which in turn limits access to various game systems.
|
||||
|
||||
All functions defined globally in this module are assumed to be
|
||||
available for use in lockstrings to determine access. See the
|
||||
Evennia documentation for more info on locks.
|
||||
|
||||
A lock function is always called with two arguments, accessing_obj and
|
||||
accessed_obj, followed by any number of arguments. All possible
|
||||
arguments should be handled with *args, **kwargs. The lock function
|
||||
should handle all eventual tracebacks by logging the error and
|
||||
returning False.
|
||||
|
||||
Lock functions in this module extend (and will overload same-named)
|
||||
lock functions from evennia.locks.lockfuncs.
|
||||
|
||||
"""
|
||||
|
||||
# def myfalse(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
# """
|
||||
# called in lockstring with myfalse().
|
||||
# A simple logger that always returns false. Prints to stdout
|
||||
# for simplicity, should use utils.logger for real operation.
|
||||
# """
|
||||
# print "%s tried to access %s. Access denied." % (accessing_obj, accessed_obj)
|
||||
# return False
|
||||
105
evennia/timmy_world/server/conf/mssp.py
Normal file
105
evennia/timmy_world/server/conf/mssp.py
Normal file
@@ -0,0 +1,105 @@
|
||||
"""
|
||||
|
||||
MSSP (Mud Server Status Protocol) meta information
|
||||
|
||||
Modify this file to specify what MUD listing sites will report about your game.
|
||||
All fields are static. The number of currently active players and your game's
|
||||
current uptime will be added automatically by Evennia.
|
||||
|
||||
You don't have to fill in everything (and most fields are not shown/used by all
|
||||
crawlers anyway); leave the default if so needed. You need to reload the server
|
||||
before the updated information is made available to crawlers (reloading does
|
||||
not affect uptime).
|
||||
|
||||
After changing the values in this file, you must register your game with the
|
||||
MUD website list you want to track you. The listing crawler will then regularly
|
||||
connect to your server to get the latest info. No further configuration is
|
||||
needed on the Evennia side.
|
||||
|
||||
"""
|
||||
|
||||
MSSPTable = {
|
||||
# Required fields
|
||||
"NAME": "Mygame", # usually the same as SERVERNAME
|
||||
# Generic
|
||||
"CRAWL DELAY": "-1", # limit how often crawler may update the listing. -1 for no limit
|
||||
"HOSTNAME": "", # telnet hostname
|
||||
"PORT": ["4000"], # telnet port - most important port should be *last* in list!
|
||||
"CODEBASE": "Evennia",
|
||||
"CONTACT": "", # email for contacting the mud
|
||||
"CREATED": "", # year MUD was created
|
||||
"ICON": "", # url to icon 32x32 or larger; <32kb.
|
||||
"IP": "", # current or new IP address
|
||||
"LANGUAGE": "", # name of language used, e.g. English
|
||||
"LOCATION": "", # full English name of server country
|
||||
"MINIMUM AGE": "0", # set to 0 if not applicable
|
||||
"WEBSITE": "", # http:// address to your game website
|
||||
# Categorisation
|
||||
"FAMILY": "Evennia",
|
||||
"GENRE": "None", # Adult, Fantasy, Historical, Horror, Modern, None, or Science Fiction
|
||||
# Gameplay: Adventure, Educational, Hack and Slash, None,
|
||||
# Player versus Player, Player versus Environment,
|
||||
# Roleplaying, Simulation, Social or Strategy
|
||||
"GAMEPLAY": "",
|
||||
"STATUS": "Open Beta", # Allowed: Alpha, Closed Beta, Open Beta, Live
|
||||
"GAMESYSTEM": "Custom", # D&D, d20 System, World of Darkness, etc. Use Custom if homebrew
|
||||
# Subgenre: LASG, Medieval Fantasy, World War II, Frankenstein,
|
||||
# Cyberpunk, Dragonlance, etc. Or None if not applicable.
|
||||
"SUBGENRE": "None",
|
||||
# World
|
||||
"AREAS": "0",
|
||||
"HELPFILES": "0",
|
||||
"MOBILES": "0",
|
||||
"OBJECTS": "0",
|
||||
"ROOMS": "0", # use 0 if room-less
|
||||
"CLASSES": "0", # use 0 if class-less
|
||||
"LEVELS": "0", # use 0 if level-less
|
||||
"RACES": "0", # use 0 if race-less
|
||||
"SKILLS": "0", # use 0 if skill-less
|
||||
# Protocols set to 1 or 0; should usually not be changed)
|
||||
"ANSI": "1",
|
||||
"GMCP": "1",
|
||||
"MSDP": "1",
|
||||
"MXP": "1",
|
||||
"SSL": "1",
|
||||
"UTF-8": "1",
|
||||
"MCCP": "1",
|
||||
"XTERM 256 COLORS": "1",
|
||||
"XTERM TRUE COLORS": "0",
|
||||
"ATCP": "0",
|
||||
"MCP": "0",
|
||||
"MSP": "0",
|
||||
"VT100": "0",
|
||||
"PUEBLO": "0",
|
||||
"ZMP": "0",
|
||||
# Commercial set to 1 or 0)
|
||||
"PAY TO PLAY": "0",
|
||||
"PAY FOR PERKS": "0",
|
||||
# Hiring set to 1 or 0)
|
||||
"HIRING BUILDERS": "0",
|
||||
"HIRING CODERS": "0",
|
||||
# Extended variables
|
||||
# World
|
||||
"DBSIZE": "0",
|
||||
"EXITS": "0",
|
||||
"EXTRA DESCRIPTIONS": "0",
|
||||
"MUDPROGS": "0",
|
||||
"MUDTRIGS": "0",
|
||||
"RESETS": "0",
|
||||
# Game (set to 1 or 0, or one of the given alternatives)
|
||||
"ADULT MATERIAL": "0",
|
||||
"MULTICLASSING": "0",
|
||||
"NEWBIE FRIENDLY": "0",
|
||||
"PLAYER CITIES": "0",
|
||||
"PLAYER CLANS": "0",
|
||||
"PLAYER CRAFTING": "0",
|
||||
"PLAYER GUILDS": "0",
|
||||
"EQUIPMENT SYSTEM": "None", # "None", "Level", "Skill", "Both"
|
||||
"MULTIPLAYING": "None", # "None", "Restricted", "Full"
|
||||
"PLAYERKILLING": "None", # "None", "Restricted", "Full"
|
||||
"QUEST SYSTEM": "None", # "None", "Immortal Run", "Automated", "Integrated"
|
||||
"ROLEPLAYING": "None", # "None", "Accepted", "Encouraged", "Enforced"
|
||||
"TRAINING SYSTEM": "None", # "None", "Level", "Skill", "Both"
|
||||
# World originality: "All Stock", "Mostly Stock", "Mostly Original", "All Original"
|
||||
"WORLD ORIGINALITY": "All Original",
|
||||
}
|
||||
24
evennia/timmy_world/server/conf/portal_services_plugins.py
Normal file
24
evennia/timmy_world/server/conf/portal_services_plugins.py
Normal file
@@ -0,0 +1,24 @@
|
||||
"""
|
||||
Start plugin services
|
||||
|
||||
This plugin module can define user-created services for the Portal to
|
||||
start.
|
||||
|
||||
This module must handle all imports and setups required to start
|
||||
twisted services (see examples in evennia.server.portal.portal). It
|
||||
must also contain a function start_plugin_services(application).
|
||||
Evennia will call this function with the main Portal application (so
|
||||
your services can be added to it). The function should not return
|
||||
anything. Plugin services are started last in the Portal startup
|
||||
process.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def start_plugin_services(portal):
|
||||
"""
|
||||
This hook is called by Evennia, last in the Portal startup process.
|
||||
|
||||
portal - a reference to the main portal application.
|
||||
"""
|
||||
pass
|
||||
24
evennia/timmy_world/server/conf/server_services_plugins.py
Normal file
24
evennia/timmy_world/server/conf/server_services_plugins.py
Normal file
@@ -0,0 +1,24 @@
|
||||
"""
|
||||
|
||||
Server plugin services
|
||||
|
||||
This plugin module can define user-created services for the Server to
|
||||
start.
|
||||
|
||||
This module must handle all imports and setups required to start a
|
||||
twisted service (see examples in evennia.server.server). It must also
|
||||
contain a function start_plugin_services(application). Evennia will
|
||||
call this function with the main Server application (so your services
|
||||
can be added to it). The function should not return anything. Plugin
|
||||
services are started last in the Server startup process.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def start_plugin_services(server):
|
||||
"""
|
||||
This hook is called by Evennia, last in the Server startup process.
|
||||
|
||||
server - a reference to the main server application.
|
||||
"""
|
||||
pass
|
||||
37
evennia/timmy_world/server/conf/serversession.py
Normal file
37
evennia/timmy_world/server/conf/serversession.py
Normal file
@@ -0,0 +1,37 @@
|
||||
"""
|
||||
ServerSession
|
||||
|
||||
The serversession is the Server-side in-memory representation of a
|
||||
user connecting to the game. Evennia manages one Session per
|
||||
connection to the game. So a user logged into the game with multiple
|
||||
clients (if Evennia is configured to allow that) will have multiple
|
||||
sessions tied to one Account object. All communication between Evennia
|
||||
and the real-world user goes through the Session(s) associated with that user.
|
||||
|
||||
It should be noted that modifying the Session object is not usually
|
||||
necessary except for the most custom and exotic designs - and even
|
||||
then it might be enough to just add custom session-level commands to
|
||||
the SessionCmdSet instead.
|
||||
|
||||
This module is not normally called. To tell Evennia to use the class
|
||||
in this module instead of the default one, add the following to your
|
||||
settings file:
|
||||
|
||||
SERVER_SESSION_CLASS = "server.conf.serversession.ServerSession"
|
||||
|
||||
"""
|
||||
|
||||
from evennia.server.serversession import ServerSession as BaseServerSession
|
||||
|
||||
|
||||
class ServerSession(BaseServerSession):
|
||||
"""
|
||||
This class represents a player's session and is a template for
|
||||
individual protocols to communicate with Evennia.
|
||||
|
||||
Each account gets one or more sessions assigned to them whenever they connect
|
||||
to the game server. All communication between game and account goes
|
||||
through their session(s).
|
||||
"""
|
||||
|
||||
pass
|
||||
44
evennia/timmy_world/server/conf/settings.py
Normal file
44
evennia/timmy_world/server/conf/settings.py
Normal file
@@ -0,0 +1,44 @@
|
||||
r"""
|
||||
Evennia settings file.
|
||||
|
||||
The available options are found in the default settings file found
|
||||
here:
|
||||
|
||||
https://www.evennia.com/docs/latest/Setup/Settings-Default.html
|
||||
|
||||
Remember:
|
||||
|
||||
Don't copy more from the default file than you actually intend to
|
||||
change; this will make sure that you don't overload upstream updates
|
||||
unnecessarily.
|
||||
|
||||
When changing a setting requiring a file system path (like
|
||||
path/to/actual/file.py), use GAME_DIR and EVENNIA_DIR to reference
|
||||
your game folder and the Evennia library folders respectively. Python
|
||||
paths (path.to.module) should be given relative to the game's root
|
||||
folder (typeclasses.foo) whereas paths within the Evennia library
|
||||
needs to be given explicitly (evennia.foo).
|
||||
|
||||
If you want to share your game dir, including its settings, you can
|
||||
put secret game- or server-specific settings in secret_settings.py.
|
||||
|
||||
"""
|
||||
|
||||
# Use the defaults from Evennia unless explicitly overridden
|
||||
from evennia.settings_default import *
|
||||
|
||||
######################################################################
|
||||
# Evennia base server config
|
||||
######################################################################
|
||||
|
||||
# This is the name of your game. Make it catchy!
|
||||
SERVERNAME = "timmy_world"
|
||||
|
||||
|
||||
######################################################################
|
||||
# Settings given in secret_settings.py override those in this file.
|
||||
######################################################################
|
||||
try:
|
||||
from server.conf.secret_settings import *
|
||||
except ImportError:
|
||||
print("secret_settings.py file not found or failed to import.")
|
||||
41
evennia/timmy_world/server/conf/web_plugins.py
Normal file
41
evennia/timmy_world/server/conf/web_plugins.py
Normal file
@@ -0,0 +1,41 @@
|
||||
"""
|
||||
Web plugin hooks.
|
||||
"""
|
||||
|
||||
|
||||
def at_webserver_root_creation(web_root):
|
||||
"""
|
||||
This is called as the web server has finished building its default
|
||||
path tree. At this point, the media/ and static/ URIs have already
|
||||
been added to the web root.
|
||||
|
||||
Args:
|
||||
web_root (twisted.web.resource.Resource): The root
|
||||
resource of the URI tree. Use .putChild() to
|
||||
add new subdomains to the tree.
|
||||
|
||||
Returns:
|
||||
web_root (twisted.web.resource.Resource): The potentially
|
||||
modified root structure.
|
||||
|
||||
Example:
|
||||
from twisted.web import static
|
||||
my_page = static.File("web/mypage/")
|
||||
my_page.indexNames = ["index.html"]
|
||||
web_root.putChild("mypage", my_page)
|
||||
|
||||
"""
|
||||
return web_root
|
||||
|
||||
|
||||
def at_webproxy_root_creation(web_root):
|
||||
"""
|
||||
This function can modify the portal proxy service.
|
||||
Args:
|
||||
web_root (evennia.server.webserver.Website): The Evennia
|
||||
Website application. Use .putChild() to add new
|
||||
subdomains that are Portal-accessible over TCP;
|
||||
primarily for new protocol development, but suitable
|
||||
for other shenanigans.
|
||||
"""
|
||||
return web_root
|
||||
16
evennia/timmy_world/typeclasses/README.md
Normal file
16
evennia/timmy_world/typeclasses/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# typeclasses/
|
||||
|
||||
This directory holds the modules for overloading all the typeclasses
|
||||
representing the game entities and many systems of the game. Other
|
||||
server functionality not covered here is usually modified by the
|
||||
modules in `server/conf/`.
|
||||
|
||||
Each module holds empty classes that just imports Evennia's defaults.
|
||||
Any modifications done to these classes will overload the defaults.
|
||||
|
||||
You can change the structure of this directory (even rename the
|
||||
directory itself) as you please, but if you do you must add the
|
||||
appropriate new paths to your settings.py file so Evennia knows where
|
||||
to look. Also remember that for Python to find your modules, it
|
||||
requires you to add an empty `__init__.py` file in any new sub
|
||||
directories you create.
|
||||
0
evennia/timmy_world/typeclasses/__init__.py
Normal file
0
evennia/timmy_world/typeclasses/__init__.py
Normal file
148
evennia/timmy_world/typeclasses/accounts.py
Normal file
148
evennia/timmy_world/typeclasses/accounts.py
Normal file
@@ -0,0 +1,148 @@
|
||||
"""
|
||||
Account
|
||||
|
||||
The Account represents the game "account" and each login has only one
|
||||
Account object. An Account is what chats on default channels but has no
|
||||
other in-game-world existence. Rather the Account puppets Objects (such
|
||||
as Characters) in order to actually participate in the game world.
|
||||
|
||||
|
||||
Guest
|
||||
|
||||
Guest accounts are simple low-level accounts that are created/deleted
|
||||
on the fly and allows users to test the game without the commitment
|
||||
of a full registration. Guest accounts are deactivated by default; to
|
||||
activate them, add the following line to your settings file:
|
||||
|
||||
GUEST_ENABLED = True
|
||||
|
||||
You will also need to modify the connection screen to reflect the
|
||||
possibility to connect with a guest account. The setting file accepts
|
||||
several more options for customizing the Guest account system.
|
||||
|
||||
"""
|
||||
|
||||
from evennia.accounts.accounts import DefaultAccount, DefaultGuest
|
||||
|
||||
|
||||
class Account(DefaultAccount):
|
||||
"""
|
||||
An Account is the actual OOC player entity. It doesn't exist in the game,
|
||||
but puppets characters.
|
||||
|
||||
This is the base Typeclass for all Accounts. Accounts represent
|
||||
the person playing the game and tracks account info, password
|
||||
etc. They are OOC entities without presence in-game. An Account
|
||||
can connect to a Character Object in order to "enter" the
|
||||
game.
|
||||
|
||||
Account Typeclass API:
|
||||
|
||||
* Available properties (only available on initiated typeclass objects)
|
||||
|
||||
- key (string) - name of account
|
||||
- name (string)- wrapper for user.username
|
||||
- aliases (list of strings) - aliases to the object. Will be saved to
|
||||
database as AliasDB entries but returned as strings.
|
||||
- dbref (int, read-only) - unique #id-number. Also "id" can be used.
|
||||
- date_created (string) - time stamp of object creation
|
||||
- permissions (list of strings) - list of permission strings
|
||||
- user (User, read-only) - django User authorization object
|
||||
- obj (Object) - game object controlled by account. 'character' can also
|
||||
be used.
|
||||
- is_superuser (bool, read-only) - if the connected user is a superuser
|
||||
|
||||
* Handlers
|
||||
|
||||
- locks - lock-handler: use locks.add() to add new lock strings
|
||||
- db - attribute-handler: store/retrieve database attributes on this
|
||||
self.db.myattr=val, val=self.db.myattr
|
||||
- ndb - non-persistent attribute handler: same as db but does not
|
||||
create a database entry when storing data
|
||||
- scripts - script-handler. Add new scripts to object with scripts.add()
|
||||
- cmdset - cmdset-handler. Use cmdset.add() to add new cmdsets to object
|
||||
- nicks - nick-handler. New nicks with nicks.add().
|
||||
- sessions - session-handler. Use session.get() to see all sessions connected, if any
|
||||
- options - option-handler. Defaults are taken from settings.OPTIONS_ACCOUNT_DEFAULT
|
||||
- characters - handler for listing the account's playable characters
|
||||
|
||||
* Helper methods (check autodocs for full updated listing)
|
||||
|
||||
- msg(text=None, from_obj=None, session=None, options=None, **kwargs)
|
||||
- execute_cmd(raw_string)
|
||||
- search(searchdata, return_puppet=False, search_object=False, typeclass=None,
|
||||
nofound_string=None, multimatch_string=None, use_nicks=True,
|
||||
quiet=False, **kwargs)
|
||||
- is_typeclass(typeclass, exact=False)
|
||||
- swap_typeclass(new_typeclass, clean_attributes=False, no_default=True)
|
||||
- access(accessing_obj, access_type='read', default=False, no_superuser_bypass=False, **kwargs)
|
||||
- check_permstring(permstring)
|
||||
- get_cmdsets(caller, current, **kwargs)
|
||||
- get_cmdset_providers()
|
||||
- uses_screenreader(session=None)
|
||||
- get_display_name(looker, **kwargs)
|
||||
- get_extra_display_name_info(looker, **kwargs)
|
||||
- disconnect_session_from_account()
|
||||
- puppet_object(session, obj)
|
||||
- unpuppet_object(session)
|
||||
- unpuppet_all()
|
||||
- get_puppet(session)
|
||||
- get_all_puppets()
|
||||
- is_banned(**kwargs)
|
||||
- get_username_validators(validator_config=settings.AUTH_USERNAME_VALIDATORS)
|
||||
- authenticate(username, password, ip="", **kwargs)
|
||||
- normalize_username(username)
|
||||
- validate_username(username)
|
||||
- validate_password(password, account=None)
|
||||
- set_password(password, **kwargs)
|
||||
- get_character_slots()
|
||||
- get_available_character_slots()
|
||||
- create_character(*args, **kwargs)
|
||||
- create(*args, **kwargs)
|
||||
- delete(*args, **kwargs)
|
||||
- channel_msg(message, channel, senders=None, **kwargs)
|
||||
- idle_time()
|
||||
- connection_time()
|
||||
|
||||
* Hook methods
|
||||
|
||||
basetype_setup()
|
||||
at_account_creation()
|
||||
|
||||
> note that the following hooks are also found on Objects and are
|
||||
usually handled on the character level:
|
||||
|
||||
- at_init()
|
||||
- at_first_save()
|
||||
- at_access()
|
||||
- at_cmdset_get(**kwargs)
|
||||
- at_password_change(**kwargs)
|
||||
- at_first_login()
|
||||
- at_pre_login()
|
||||
- at_post_login(session=None)
|
||||
- at_failed_login(session, **kwargs)
|
||||
- at_disconnect(reason=None, **kwargs)
|
||||
- at_post_disconnect(**kwargs)
|
||||
- at_message_receive()
|
||||
- at_message_send()
|
||||
- at_server_reload()
|
||||
- at_server_shutdown()
|
||||
- at_look(target=None, session=None, **kwargs)
|
||||
- at_post_create_character(character, **kwargs)
|
||||
- at_post_add_character(char)
|
||||
- at_post_remove_character(char)
|
||||
- at_pre_channel_msg(message, channel, senders=None, **kwargs)
|
||||
- at_post_chnnel_msg(message, channel, senders=None, **kwargs)
|
||||
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class Guest(DefaultGuest):
|
||||
"""
|
||||
This class is used for guest logins. Unlike Accounts, Guests and their
|
||||
characters are deleted after disconnection.
|
||||
"""
|
||||
|
||||
pass
|
||||
118
evennia/timmy_world/typeclasses/channels.py
Normal file
118
evennia/timmy_world/typeclasses/channels.py
Normal file
@@ -0,0 +1,118 @@
|
||||
"""
|
||||
Channel
|
||||
|
||||
The channel class represents the out-of-character chat-room usable by
|
||||
Accounts in-game. It is mostly overloaded to change its appearance, but
|
||||
channels can be used to implement many different forms of message
|
||||
distribution systems.
|
||||
|
||||
Note that sending data to channels are handled via the CMD_CHANNEL
|
||||
syscommand (see evennia.syscmds). The sending should normally not need
|
||||
to be modified.
|
||||
|
||||
"""
|
||||
|
||||
from evennia.comms.comms import DefaultChannel
|
||||
|
||||
|
||||
class Channel(DefaultChannel):
|
||||
r"""
|
||||
This is the base class for all Channel Comms. Inherit from this to
|
||||
create different types of communication channels.
|
||||
|
||||
Class-level variables:
|
||||
- `send_to_online_only` (bool, default True) - if set, will only try to
|
||||
send to subscribers that are actually active. This is a useful optimization.
|
||||
- `log_file` (str, default `"channel_{channelname}.log"`). This is the
|
||||
log file to which the channel history will be saved. The `{channelname}` tag
|
||||
will be replaced by the key of the Channel. If an Attribute 'log_file'
|
||||
is set, this will be used instead. If this is None and no Attribute is found,
|
||||
no history will be saved.
|
||||
- `channel_prefix_string` (str, default `"[{channelname} ]"`) - this is used
|
||||
as a simple template to get the channel prefix with `.channel_prefix()`. It is used
|
||||
in front of every channel message; use `{channelmessage}` token to insert the
|
||||
name of the current channel. Set to `None` if you want no prefix (or want to
|
||||
handle it in a hook during message generation instead.
|
||||
- `channel_msg_nick_pattern`(str, default `"{alias}\s*?|{alias}\s+?(?P<arg1>.+?)") -
|
||||
this is what used when a channel subscriber gets a channel nick assigned to this
|
||||
channel. The nickhandler uses the pattern to pick out this channel's name from user
|
||||
input. The `{alias}` token will get both the channel's key and any set/custom aliases
|
||||
per subscriber. You need to allow for an `<arg1>` regex group to catch any message
|
||||
that should be send to the channel. You usually don't need to change this pattern
|
||||
unless you are changing channel command-style entirely.
|
||||
- `channel_msg_nick_replacement` (str, default `"channel {channelname} = $1"` - this
|
||||
is used by the nickhandler to generate a replacement string once the nickhandler (using
|
||||
the `channel_msg_nick_pattern`) identifies that the channel should be addressed
|
||||
to send a message to it. The `<arg1>` regex pattern match from `channel_msg_nick_pattern`
|
||||
will end up at the `$1` position in the replacement. Together, this allows you do e.g.
|
||||
'public Hello' and have that become a mapping to `channel public = Hello`. By default,
|
||||
the account-level `channel` command is used. If you were to rename that command you must
|
||||
tweak the output to something like `yourchannelcommandname {channelname} = $1`.
|
||||
|
||||
* Properties:
|
||||
mutelist
|
||||
banlist
|
||||
wholist
|
||||
|
||||
* Working methods:
|
||||
get_log_filename()
|
||||
set_log_filename(filename)
|
||||
has_connection(account) - check if the given account listens to this channel
|
||||
connect(account) - connect account to this channel
|
||||
disconnect(account) - disconnect account from channel
|
||||
access(access_obj, access_type='listen', default=False) - check the
|
||||
access on this channel (default access_type is listen)
|
||||
create(key, creator=None, *args, **kwargs)
|
||||
delete() - delete this channel
|
||||
message_transform(msg, emit=False, prefix=True,
|
||||
sender_strings=None, external=False) - called by
|
||||
the comm system and triggers the hooks below
|
||||
msg(msgobj, header=None, senders=None, sender_strings=None,
|
||||
persistent=None, online=False, emit=False, external=False) - main
|
||||
send method, builds and sends a new message to channel.
|
||||
tempmsg(msg, header=None, senders=None) - wrapper for sending non-persistent
|
||||
messages.
|
||||
distribute_message(msg, online=False) - send a message to all
|
||||
connected accounts on channel, optionally sending only
|
||||
to accounts that are currently online (optimized for very large sends)
|
||||
mute(subscriber, **kwargs)
|
||||
unmute(subscriber, **kwargs)
|
||||
ban(target, **kwargs)
|
||||
unban(target, **kwargs)
|
||||
add_user_channel_alias(user, alias, **kwargs)
|
||||
remove_user_channel_alias(user, alias, **kwargs)
|
||||
|
||||
|
||||
Useful hooks:
|
||||
at_channel_creation() - called once, when the channel is created
|
||||
basetype_setup()
|
||||
at_init()
|
||||
at_first_save()
|
||||
channel_prefix() - how the channel should be
|
||||
prefixed when returning to user. Returns a string
|
||||
format_senders(senders) - should return how to display multiple
|
||||
senders to a channel
|
||||
pose_transform(msg, sender_string) - should detect if the
|
||||
sender is posing, and if so, modify the string
|
||||
format_external(msg, senders, emit=False) - format messages sent
|
||||
from outside the game, like from IRC
|
||||
format_message(msg, emit=False) - format the message body before
|
||||
displaying it to the user. 'emit' generally means that the
|
||||
message should not be displayed with the sender's name.
|
||||
channel_prefix()
|
||||
|
||||
pre_join_channel(joiner) - if returning False, abort join
|
||||
post_join_channel(joiner) - called right after successful join
|
||||
pre_leave_channel(leaver) - if returning False, abort leave
|
||||
post_leave_channel(leaver) - called right after successful leave
|
||||
at_pre_msg(message, **kwargs)
|
||||
at_post_msg(message, **kwargs)
|
||||
web_get_admin_url()
|
||||
web_get_create_url()
|
||||
web_get_detail_url()
|
||||
web_get_update_url()
|
||||
web_get_delete_url()
|
||||
|
||||
"""
|
||||
|
||||
pass
|
||||
26
evennia/timmy_world/typeclasses/characters.py
Normal file
26
evennia/timmy_world/typeclasses/characters.py
Normal file
@@ -0,0 +1,26 @@
|
||||
"""
|
||||
Characters
|
||||
|
||||
Characters are (by default) Objects setup to be puppeted by Accounts.
|
||||
They are what you "see" in game. The Character class in this module
|
||||
is setup to be the "default" character type created by the default
|
||||
creation commands.
|
||||
|
||||
"""
|
||||
|
||||
from evennia.objects.objects import DefaultCharacter
|
||||
|
||||
from .objects import ObjectParent
|
||||
|
||||
|
||||
class Character(ObjectParent, DefaultCharacter):
|
||||
"""
|
||||
The Character just re-implements some of the Object's methods and hooks
|
||||
to represent a Character entity in-game.
|
||||
|
||||
See mygame/typeclasses/objects.py for a list of
|
||||
properties and methods available on all Object child classes like this.
|
||||
|
||||
"""
|
||||
|
||||
pass
|
||||
26
evennia/timmy_world/typeclasses/exits.py
Normal file
26
evennia/timmy_world/typeclasses/exits.py
Normal file
@@ -0,0 +1,26 @@
|
||||
"""
|
||||
Exits
|
||||
|
||||
Exits are connectors between Rooms. An exit always has a destination property
|
||||
set and has a single command defined on itself with the same name as its key,
|
||||
for allowing Characters to traverse the exit to its destination.
|
||||
|
||||
"""
|
||||
|
||||
from evennia.objects.objects import DefaultExit
|
||||
|
||||
from .objects import ObjectParent
|
||||
|
||||
|
||||
class Exit(ObjectParent, DefaultExit):
|
||||
"""
|
||||
Exits are connectors between rooms. Exits are normal Objects except
|
||||
they defines the `destination` property and overrides some hooks
|
||||
and methods to represent the exits.
|
||||
|
||||
See mygame/typeclasses/objects.py for a list of
|
||||
properties and methods available on all Objects child classes like this.
|
||||
|
||||
"""
|
||||
|
||||
pass
|
||||
217
evennia/timmy_world/typeclasses/objects.py
Normal file
217
evennia/timmy_world/typeclasses/objects.py
Normal file
@@ -0,0 +1,217 @@
|
||||
"""
|
||||
Object
|
||||
|
||||
The Object is the class for general items in the game world.
|
||||
|
||||
Use the ObjectParent class to implement common features for *all* entities
|
||||
with a location in the game world (like Characters, Rooms, Exits).
|
||||
|
||||
"""
|
||||
|
||||
from evennia.objects.objects import DefaultObject
|
||||
|
||||
|
||||
class ObjectParent:
|
||||
"""
|
||||
This is a mixin that can be used to override *all* entities inheriting at
|
||||
some distance from DefaultObject (Objects, Exits, Characters and Rooms).
|
||||
|
||||
Just add any method that exists on `DefaultObject` to this class. If one
|
||||
of the derived classes has itself defined that same hook already, that will
|
||||
take precedence.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
class Object(ObjectParent, DefaultObject):
|
||||
"""
|
||||
This is the root Object typeclass, representing all entities that
|
||||
have an actual presence in-game. DefaultObjects generally have a
|
||||
location. They can also be manipulated and looked at. Game
|
||||
entities you define should inherit from DefaultObject at some distance.
|
||||
|
||||
It is recommended to create children of this class using the
|
||||
`evennia.create_object()` function rather than to initialize the class
|
||||
directly - this will both set things up and efficiently save the object
|
||||
without `obj.save()` having to be called explicitly.
|
||||
|
||||
Note: Check the autodocs for complete class members, this may not always
|
||||
be up-to date.
|
||||
|
||||
* Base properties defined/available on all Objects
|
||||
|
||||
key (string) - name of object
|
||||
name (string)- same as key
|
||||
dbref (int, read-only) - unique #id-number. Also "id" can be used.
|
||||
date_created (string) - time stamp of object creation
|
||||
|
||||
account (Account) - controlling account (if any, only set together with
|
||||
sessid below)
|
||||
sessid (int, read-only) - session id (if any, only set together with
|
||||
account above). Use `sessions` handler to get the
|
||||
Sessions directly.
|
||||
location (Object) - current location. Is None if this is a room
|
||||
home (Object) - safety start-location
|
||||
has_account (bool, read-only)- will only return *connected* accounts
|
||||
contents (list, read only) - returns all objects inside this object
|
||||
exits (list of Objects, read-only) - returns all exits from this
|
||||
object, if any
|
||||
destination (Object) - only set if this object is an exit.
|
||||
is_superuser (bool, read-only) - True/False if this user is a superuser
|
||||
is_connected (bool, read-only) - True if this object is associated with
|
||||
an Account with any connected sessions.
|
||||
has_account (bool, read-only) - True is this object has an associated account.
|
||||
is_superuser (bool, read-only): True if this object has an account and that
|
||||
account is a superuser.
|
||||
|
||||
* Handlers available
|
||||
|
||||
aliases - alias-handler: use aliases.add/remove/get() to use.
|
||||
permissions - permission-handler: use permissions.add/remove() to
|
||||
add/remove new perms.
|
||||
locks - lock-handler: use locks.add() to add new lock strings
|
||||
scripts - script-handler. Add new scripts to object with scripts.add()
|
||||
cmdset - cmdset-handler. Use cmdset.add() to add new cmdsets to object
|
||||
nicks - nick-handler. New nicks with nicks.add().
|
||||
sessions - sessions-handler. Get Sessions connected to this
|
||||
object with sessions.get()
|
||||
attributes - attribute-handler. Use attributes.add/remove/get.
|
||||
db - attribute-handler: Shortcut for attribute-handler. Store/retrieve
|
||||
database attributes using self.db.myattr=val, val=self.db.myattr
|
||||
ndb - non-persistent attribute handler: same as db but does not create
|
||||
a database entry when storing data
|
||||
|
||||
* Helper methods (see src.objects.objects.py for full headers)
|
||||
|
||||
get_search_query_replacement(searchdata, **kwargs)
|
||||
get_search_direct_match(searchdata, **kwargs)
|
||||
get_search_candidates(searchdata, **kwargs)
|
||||
get_search_result(searchdata, attribute_name=None, typeclass=None,
|
||||
candidates=None, exact=False, use_dbref=None, tags=None, **kwargs)
|
||||
get_stacked_result(results, **kwargs)
|
||||
handle_search_results(searchdata, results, **kwargs)
|
||||
search(searchdata, global_search=False, use_nicks=True, typeclass=None,
|
||||
location=None, attribute_name=None, quiet=False, exact=False,
|
||||
candidates=None, use_locks=True, nofound_string=None,
|
||||
multimatch_string=None, use_dbref=None, tags=None, stacked=0)
|
||||
search_account(searchdata, quiet=False)
|
||||
execute_cmd(raw_string, session=None, **kwargs))
|
||||
msg(text=None, from_obj=None, session=None, options=None, **kwargs)
|
||||
for_contents(func, exclude=None, **kwargs)
|
||||
msg_contents(message, exclude=None, from_obj=None, mapping=None,
|
||||
raise_funcparse_errors=False, **kwargs)
|
||||
move_to(destination, quiet=False, emit_to_obj=None, use_destination=True)
|
||||
clear_contents()
|
||||
create(key, account, caller, method, **kwargs)
|
||||
copy(new_key=None)
|
||||
at_object_post_copy(new_obj, **kwargs)
|
||||
delete()
|
||||
is_typeclass(typeclass, exact=False)
|
||||
swap_typeclass(new_typeclass, clean_attributes=False, no_default=True)
|
||||
access(accessing_obj, access_type='read', default=False,
|
||||
no_superuser_bypass=False, **kwargs)
|
||||
filter_visible(obj_list, looker, **kwargs)
|
||||
get_default_lockstring()
|
||||
get_cmdsets(caller, current, **kwargs)
|
||||
check_permstring(permstring)
|
||||
get_cmdset_providers()
|
||||
get_display_name(looker=None, **kwargs)
|
||||
get_extra_display_name_info(looker=None, **kwargs)
|
||||
get_numbered_name(count, looker, **kwargs)
|
||||
get_display_header(looker, **kwargs)
|
||||
get_display_desc(looker, **kwargs)
|
||||
get_display_exits(looker, **kwargs)
|
||||
get_display_characters(looker, **kwargs)
|
||||
get_display_things(looker, **kwargs)
|
||||
get_display_footer(looker, **kwargs)
|
||||
format_appearance(appearance, looker, **kwargs)
|
||||
return_apperance(looker, **kwargs)
|
||||
|
||||
* Hooks (these are class methods, so args should start with self):
|
||||
|
||||
basetype_setup() - only called once, used for behind-the-scenes
|
||||
setup. Normally not modified.
|
||||
basetype_posthook_setup() - customization in basetype, after the object
|
||||
has been created; Normally not modified.
|
||||
|
||||
at_object_creation() - only called once, when object is first created.
|
||||
Object customizations go here.
|
||||
at_object_delete() - called just before deleting an object. If returning
|
||||
False, deletion is aborted. Note that all objects
|
||||
inside a deleted object are automatically moved
|
||||
to their <home>, they don't need to be removed here.
|
||||
|
||||
at_init() - called whenever typeclass is cached from memory,
|
||||
at least once every server restart/reload
|
||||
at_first_save()
|
||||
at_cmdset_get(**kwargs) - this is called just before the command handler
|
||||
requests a cmdset from this object. The kwargs are
|
||||
not normally used unless the cmdset is created
|
||||
dynamically (see e.g. Exits).
|
||||
at_pre_puppet(account)- (account-controlled objects only) called just
|
||||
before puppeting
|
||||
at_post_puppet() - (account-controlled objects only) called just
|
||||
after completing connection account<->object
|
||||
at_pre_unpuppet() - (account-controlled objects only) called just
|
||||
before un-puppeting
|
||||
at_post_unpuppet(account) - (account-controlled objects only) called just
|
||||
after disconnecting account<->object link
|
||||
at_server_reload() - called before server is reloaded
|
||||
at_server_shutdown() - called just before server is fully shut down
|
||||
|
||||
at_access(result, accessing_obj, access_type) - called with the result
|
||||
of a lock access check on this object. Return value
|
||||
does not affect check result.
|
||||
|
||||
at_pre_move(destination) - called just before moving object
|
||||
to the destination. If returns False, move is cancelled.
|
||||
announce_move_from(destination) - called in old location, just
|
||||
before move, if obj.move_to() has quiet=False
|
||||
announce_move_to(source_location) - called in new location, just
|
||||
after move, if obj.move_to() has quiet=False
|
||||
at_post_move(source_location) - always called after a move has
|
||||
been successfully performed.
|
||||
at_pre_object_leave(leaving_object, destination, **kwargs)
|
||||
at_object_leave(obj, target_location, move_type="move", **kwargs)
|
||||
at_object_leave(obj, target_location) - called when an object leaves
|
||||
this object in any fashion
|
||||
at_pre_object_receive(obj, source_location)
|
||||
at_object_receive(obj, source_location, move_type="move", **kwargs) - called when this object receives
|
||||
another object
|
||||
at_post_move(source_location, move_type="move", **kwargs)
|
||||
|
||||
at_traverse(traversing_object, target_location, **kwargs) - (exit-objects only)
|
||||
handles all moving across the exit, including
|
||||
calling the other exit hooks. Use super() to retain
|
||||
the default functionality.
|
||||
at_post_traverse(traversing_object, source_location) - (exit-objects only)
|
||||
called just after a traversal has happened.
|
||||
at_failed_traverse(traversing_object) - (exit-objects only) called if
|
||||
traversal fails and property err_traverse is not defined.
|
||||
|
||||
at_msg_receive(self, msg, from_obj=None, **kwargs) - called when a message
|
||||
(via self.msg()) is sent to this obj.
|
||||
If returns false, aborts send.
|
||||
at_msg_send(self, msg, to_obj=None, **kwargs) - called when this objects
|
||||
sends a message to someone via self.msg().
|
||||
|
||||
return_appearance(looker) - describes this object. Used by "look"
|
||||
command by default
|
||||
at_desc(looker=None) - called by 'look' whenever the
|
||||
appearance is requested.
|
||||
at_pre_get(getter, **kwargs)
|
||||
at_get(getter) - called after object has been picked up.
|
||||
Does not stop pickup.
|
||||
at_pre_give(giver, getter, **kwargs)
|
||||
at_give(giver, getter, **kwargs)
|
||||
at_pre_drop(dropper, **kwargs)
|
||||
at_drop(dropper, **kwargs) - called when this object has been dropped.
|
||||
at_pre_say(speaker, message, **kwargs)
|
||||
at_say(message, msg_self=None, msg_location=None, receivers=None, msg_receivers=None, **kwargs)
|
||||
|
||||
at_look(target, **kwargs)
|
||||
at_desc(looker=None)
|
||||
|
||||
"""
|
||||
|
||||
pass
|
||||
24
evennia/timmy_world/typeclasses/rooms.py
Normal file
24
evennia/timmy_world/typeclasses/rooms.py
Normal file
@@ -0,0 +1,24 @@
|
||||
"""
|
||||
Room
|
||||
|
||||
Rooms are simple containers that has no location of their own.
|
||||
|
||||
"""
|
||||
|
||||
from evennia.objects.objects import DefaultRoom
|
||||
|
||||
from .objects import ObjectParent
|
||||
|
||||
|
||||
class Room(ObjectParent, DefaultRoom):
|
||||
"""
|
||||
Rooms are like any Object, except their location is None
|
||||
(which is default). They also use basetype_setup() to
|
||||
add locks so they cannot be puppeted or picked up.
|
||||
(to change that, use at_object_creation instead)
|
||||
|
||||
See mygame/typeclasses/objects.py for a list of
|
||||
properties and methods available on all Objects.
|
||||
"""
|
||||
|
||||
pass
|
||||
103
evennia/timmy_world/typeclasses/scripts.py
Normal file
103
evennia/timmy_world/typeclasses/scripts.py
Normal file
@@ -0,0 +1,103 @@
|
||||
"""
|
||||
Scripts
|
||||
|
||||
Scripts are powerful jacks-of-all-trades. They have no in-game
|
||||
existence and can be used to represent persistent game systems in some
|
||||
circumstances. Scripts can also have a time component that allows them
|
||||
to "fire" regularly or a limited number of times.
|
||||
|
||||
There is generally no "tree" of Scripts inheriting from each other.
|
||||
Rather, each script tends to inherit from the base Script class and
|
||||
just overloads its hooks to have it perform its function.
|
||||
|
||||
"""
|
||||
|
||||
from evennia.scripts.scripts import DefaultScript
|
||||
|
||||
|
||||
class Script(DefaultScript):
|
||||
"""
|
||||
This is the base TypeClass for all Scripts. Scripts describe
|
||||
all entities/systems without a physical existence in the game world
|
||||
that require database storage (like an economic system or
|
||||
combat tracker). They
|
||||
can also have a timer/ticker component.
|
||||
|
||||
A script type is customized by redefining some or all of its hook
|
||||
methods and variables.
|
||||
|
||||
* available properties (check docs for full listing, this could be
|
||||
outdated).
|
||||
|
||||
key (string) - name of object
|
||||
name (string)- same as key
|
||||
aliases (list of strings) - aliases to the object. Will be saved
|
||||
to database as AliasDB entries but returned as strings.
|
||||
dbref (int, read-only) - unique #id-number. Also "id" can be used.
|
||||
date_created (string) - time stamp of object creation
|
||||
permissions (list of strings) - list of permission strings
|
||||
|
||||
desc (string) - optional description of script, shown in listings
|
||||
obj (Object) - optional object that this script is connected to
|
||||
and acts on (set automatically by obj.scripts.add())
|
||||
interval (int) - how often script should run, in seconds. <0 turns
|
||||
off ticker
|
||||
start_delay (bool) - if the script should start repeating right away or
|
||||
wait self.interval seconds
|
||||
repeats (int) - how many times the script should repeat before
|
||||
stopping. 0 means infinite repeats
|
||||
persistent (bool) - if script should survive a server shutdown or not
|
||||
is_active (bool) - if script is currently running
|
||||
|
||||
* Handlers
|
||||
|
||||
locks - lock-handler: use locks.add() to add new lock strings
|
||||
db - attribute-handler: store/retrieve database attributes on this
|
||||
self.db.myattr=val, val=self.db.myattr
|
||||
ndb - non-persistent attribute handler: same as db but does not
|
||||
create a database entry when storing data
|
||||
|
||||
* Helper methods
|
||||
|
||||
create(key, **kwargs)
|
||||
start() - start script (this usually happens automatically at creation
|
||||
and obj.script.add() etc)
|
||||
stop() - stop script, and delete it
|
||||
pause() - put the script on hold, until unpause() is called. If script
|
||||
is persistent, the pause state will survive a shutdown.
|
||||
unpause() - restart a previously paused script. The script will continue
|
||||
from the paused timer (but at_start() will be called).
|
||||
time_until_next_repeat() - if a timed script (interval>0), returns time
|
||||
until next tick
|
||||
|
||||
* Hook methods (should also include self as the first argument):
|
||||
|
||||
at_script_creation() - called only once, when an object of this
|
||||
class is first created.
|
||||
is_valid() - is called to check if the script is valid to be running
|
||||
at the current time. If is_valid() returns False, the running
|
||||
script is stopped and removed from the game. You can use this
|
||||
to check state changes (i.e. an script tracking some combat
|
||||
stats at regular intervals is only valid to run while there is
|
||||
actual combat going on).
|
||||
at_start() - Called every time the script is started, which for persistent
|
||||
scripts is at least once every server start. Note that this is
|
||||
unaffected by self.delay_start, which only delays the first
|
||||
call to at_repeat().
|
||||
at_repeat() - Called every self.interval seconds. It will be called
|
||||
immediately upon launch unless self.delay_start is True, which
|
||||
will delay the first call of this method by self.interval
|
||||
seconds. If self.interval==0, this method will never
|
||||
be called.
|
||||
at_pause()
|
||||
at_stop() - Called as the script object is stopped and is about to be
|
||||
removed from the game, e.g. because is_valid() returned False.
|
||||
at_script_delete()
|
||||
at_server_reload() - Called when server reloads. Can be used to
|
||||
save temporary variables you want should survive a reload.
|
||||
at_server_shutdown() - called at a full server shutdown.
|
||||
at_server_start()
|
||||
|
||||
"""
|
||||
|
||||
pass
|
||||
51
evennia/timmy_world/web/README.md
Normal file
51
evennia/timmy_world/web/README.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Web
|
||||
|
||||
This folder contains overriding of web assets - the website and webclient
|
||||
coming with the game.
|
||||
|
||||
This is the process for serving a new web site (see also the Django docs for
|
||||
more details):
|
||||
|
||||
1. A user enters an url in their browser (or clicks a button). This leads to
|
||||
the browser sending a _HTTP request_ to the server, with a specific type
|
||||
(GET,POST etc) and url-path (like for `https://localhost:4001/`, the part of
|
||||
the url we need to consider is `/`).
|
||||
2. Evennia (through Django) will make use of the regular expressions registered
|
||||
in the `urls.py` file. This acts as a rerouter to _views_, which are
|
||||
regular Python functions able to process the incoming request (think of
|
||||
these as similar to the right Evennia Command being selected to handle your
|
||||
input - views are like Commands in this sense). In the case of `/` we
|
||||
reroute to a view handling the main index-page of the website. The view is
|
||||
either a function or a callable class (Evennia tends to have them as
|
||||
functions).
|
||||
3. The view-function will prepare all the data needed by the web page. For the default
|
||||
index page, this means gather the game statistics so you can see how many
|
||||
are currently connected to the game etc.
|
||||
4. The view will next fetch a _template_. A template is a HTML-document with special
|
||||
'placeholder' tags (written as `{{...}}` or `{% ... %}` usually). These
|
||||
placeholders allow the view to inject dynamic content into the HTML and make
|
||||
the page customized to the current situation. For the index page, it means
|
||||
injecting the current player-count in the right places of the html page. This
|
||||
is called 'rendering' the template. The result is a complete HTML page.
|
||||
5. (The view can also pull in a _form_ to customize user-input in a similar way.)
|
||||
6. The finished HTML page is packed in a _HTTP response_ and is returned to the
|
||||
web browser, which can now display the page!
|
||||
|
||||
## A note on the webclient
|
||||
|
||||
The web browser can also execute code directly without talking to the Server.
|
||||
This code must be written/loaded into the web page and is written using the
|
||||
Javascript programming language (there is no way around this, it is what web
|
||||
browsers understand). Executing Javascript is something the web browser does,
|
||||
it operates independently from Evennia. Small snippets of javascript can be
|
||||
used on a page to have buttons react, make small animations etc that doesn't
|
||||
require the server.
|
||||
|
||||
In the case of the Webclient, Evennia will load the Webclient page as above,
|
||||
but the page then contains Javascript code responsible for actually displaying
|
||||
the client GUI, allows you to resize windows etc.
|
||||
|
||||
After it starts, the webclient 'calls home' and spins up a websocket link to
|
||||
the Evennia Portal - this is how all data is then exchanged. So after the
|
||||
initial loading of the webclient page, the above sequence doesn't happen again
|
||||
until close the tab and come back or you reload it manually in your browser.
|
||||
0
evennia/timmy_world/web/__init__.py
Normal file
0
evennia/timmy_world/web/__init__.py
Normal file
5
evennia/timmy_world/web/admin/README.md
Normal file
5
evennia/timmy_world/web/admin/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Admin views
|
||||
|
||||
Evennia makes several customizations to the Django web admin, but you can make
|
||||
further changes here. Customizing the admin is a big topic and
|
||||
you are best off reading more about it in the [Django admin site documentation](https://docs.djangoproject.com/en/4.1/ref/contrib/admin/).
|
||||
0
evennia/timmy_world/web/admin/__init__.py
Normal file
0
evennia/timmy_world/web/admin/__init__.py
Normal file
20
evennia/timmy_world/web/admin/urls.py
Normal file
20
evennia/timmy_world/web/admin/urls.py
Normal file
@@ -0,0 +1,20 @@
|
||||
"""
|
||||
This reroutes from an URL to a python view-function/class.
|
||||
|
||||
The main web/urls.py includes these routes for all urls starting with `admin/`
|
||||
(the `admin/` part should not be included again here).
|
||||
|
||||
"""
|
||||
|
||||
from django.urls import path
|
||||
|
||||
from evennia.web.admin.urls import urlpatterns as evennia_admin_urlpatterns
|
||||
|
||||
# add patterns here
|
||||
urlpatterns = [
|
||||
# path("url-pattern", imported_python_view),
|
||||
# path("url-pattern", imported_python_view),
|
||||
]
|
||||
|
||||
# read by Django
|
||||
urlpatterns = urlpatterns + evennia_admin_urlpatterns
|
||||
0
evennia/timmy_world/web/api/__init__.py
Normal file
0
evennia/timmy_world/web/api/__init__.py
Normal file
17
evennia/timmy_world/web/static/README.md
Normal file
17
evennia/timmy_world/web/static/README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
## Static files
|
||||
|
||||
This is the place to put static resources you want to serve from the
|
||||
Evennia server. This is usually CSS and Javascript files but you _could_ also
|
||||
serve other media like images, videos and music files from here.
|
||||
|
||||
> If you serve a lot of large files (especially videos) you will see a lot
|
||||
> better performance doing so from a separate dedicated media host.
|
||||
|
||||
You can also override default Evennia files from here. The original files are
|
||||
found in `evennia/web/static/`. Copy the original file into the same
|
||||
corresponding location/sublocation in this folder (such as website CSS files
|
||||
into `mygame/static/website/css/`) and modify it, then reload the server.
|
||||
|
||||
Note that all static resources will be collected from all over Evennia into
|
||||
`mygame/server/.static` for serving by the webserver. That folder should not be
|
||||
modified manually.
|
||||
@@ -0,0 +1,3 @@
|
||||
# Evennia API static files
|
||||
|
||||
Overrides for API files.
|
||||
@@ -0,0 +1,3 @@
|
||||
# Static files for API
|
||||
|
||||
Override images here.
|
||||
3
evennia/timmy_world/web/static/webclient/css/README.md
Normal file
3
evennia/timmy_world/web/static/webclient/css/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
You can replace the CSS files for Evennia's webclient here.
|
||||
|
||||
You can find the original files in `evennia/web/static/webclient/css/`
|
||||
3
evennia/timmy_world/web/static/webclient/js/README.md
Normal file
3
evennia/timmy_world/web/static/webclient/js/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
You can replace the javascript files for Evennia's webclient page here.
|
||||
|
||||
You can find the original files in `evennia/web/static/webclient/js/`
|
||||
3
evennia/timmy_world/web/static/website/css/README.md
Normal file
3
evennia/timmy_world/web/static/website/css/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
You can replace the CSS files for Evennia's homepage here.
|
||||
|
||||
You can find the original files in `evennia/web/static/website/css/`
|
||||
3
evennia/timmy_world/web/static/website/images/README.md
Normal file
3
evennia/timmy_world/web/static/website/images/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
You can replace the image files for Evennia's home page here.
|
||||
|
||||
You can find the original files in `evennia/web/static/website/images/`
|
||||
14
evennia/timmy_world/web/templates/README.md
Normal file
14
evennia/timmy_world/web/templates/README.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# HTML templates
|
||||
|
||||
Templates are HTML files that (usually) have special `{{ ... }}` template
|
||||
markers in them to allow Evennia/Django to insert dynamic content in a web
|
||||
page. An example is listing how many users are currently online in the game.
|
||||
|
||||
Templates are referenced by _views_ - Python functions or callable classes that
|
||||
prepare the data needed by the template and 'renders' them into a finished
|
||||
HTML page to return to the user.
|
||||
|
||||
You can replace Evennia's default templates by overriding them in this folder.
|
||||
The originals are in `evennia/web/templates/` - just copy the template into the
|
||||
corresponding location here (so the website's `index.html` should be copied to
|
||||
`website/index.html` where it can be modified). Reload the server to see your changes.
|
||||
@@ -0,0 +1,3 @@
|
||||
# Templates for the Evennia API
|
||||
|
||||
Override templates here.
|
||||
4
evennia/timmy_world/web/templates/webclient/README.md
Normal file
4
evennia/timmy_world/web/templates/webclient/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
Replace Evennia's webclient django template with your own here.
|
||||
|
||||
You can find the original files in `evennia/web/templates/webclient/`. Just copy
|
||||
the original here and modify - after a reload the new template will be used.
|
||||
5
evennia/timmy_world/web/templates/website/README.md
Normal file
5
evennia/timmy_world/web/templates/website/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
You can replace the django templates (html files) for the website
|
||||
here.
|
||||
|
||||
You can find the original files under `evennia/web/templates/website/`. Just
|
||||
copy a template here and modify to have it override the default.
|
||||
@@ -0,0 +1,3 @@
|
||||
Flatpages require a default.html template, which can be overwritten by placing it in this folder.
|
||||
|
||||
You can find the original files in `evennia/web/website/templates/website/flatpages/`
|
||||
@@ -0,0 +1,3 @@
|
||||
The templates involving login/logout can be overwritten here.
|
||||
|
||||
You can find the original files in `evennia/web/website/templates/website/registration/`
|
||||
34
evennia/timmy_world/web/urls.py
Normal file
34
evennia/timmy_world/web/urls.py
Normal file
@@ -0,0 +1,34 @@
|
||||
"""
|
||||
This is the starting point when a user enters a url in their web browser.
|
||||
|
||||
The urls is matched (by regex) and mapped to a 'view' - a Python function or
|
||||
callable class that in turn (usually) makes use of a 'template' (a html file
|
||||
with slots that can be replaced by dynamic content) in order to render a HTML
|
||||
page to show the user.
|
||||
|
||||
This file includes the urls in website, webclient and admin. To override you
|
||||
should modify urls.py in those sub directories.
|
||||
|
||||
Search the Django documentation for "URL dispatcher" for more help.
|
||||
|
||||
"""
|
||||
|
||||
from django.urls import include, path
|
||||
|
||||
# default evennia patterns
|
||||
from evennia.web.urls import urlpatterns as evennia_default_urlpatterns
|
||||
|
||||
# add patterns
|
||||
urlpatterns = [
|
||||
# website
|
||||
path("", include("web.website.urls")),
|
||||
# webclient
|
||||
path("webclient/", include("web.webclient.urls")),
|
||||
# web admin
|
||||
path("admin/", include("web.admin.urls")),
|
||||
# add any extra urls here:
|
||||
# path("mypath/", include("path.to.my.urls.file")),
|
||||
]
|
||||
|
||||
# 'urlpatterns' must be named such for Django to find it.
|
||||
urlpatterns = urlpatterns + evennia_default_urlpatterns
|
||||
23
evennia/timmy_world/web/webclient/README.md
Normal file
23
evennia/timmy_world/web/webclient/README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Webclient Views
|
||||
|
||||
The webclient is mainly controlled by Javascript directly in the browser, so
|
||||
you usually customize it via `mygame/web/static/webclient/js/` - files instead.
|
||||
|
||||
There is very little you can change from here, unless you want to implement
|
||||
your very own client from scratch.
|
||||
|
||||
## On views
|
||||
|
||||
A 'view' is python code (a function or callable class) responsible for
|
||||
producing a HTML page for a user to view in response for going to a given URL
|
||||
in their browser. In Evennia lingo, it's similar in function to a Command, with
|
||||
the input/args being the URL/request and the output being a new web-page.
|
||||
|
||||
The urls.py file contains regular expressions that are run against the provided
|
||||
URL - when a match is found, the execution is passed to a view which is then
|
||||
responsible (usually) for producing the web page by filling in a _template_ - a
|
||||
HTML document that can have special tags in it that are replaced for dynamic
|
||||
content. It then returns the finished HTML page for the user to view.
|
||||
|
||||
See the [Django docs on Views](https://docs.djangoproject.com/en/4.1/topics/http/views/) for
|
||||
more information.
|
||||
0
evennia/timmy_world/web/webclient/__init__.py
Normal file
0
evennia/timmy_world/web/webclient/__init__.py
Normal file
20
evennia/timmy_world/web/webclient/urls.py
Normal file
20
evennia/timmy_world/web/webclient/urls.py
Normal file
@@ -0,0 +1,20 @@
|
||||
"""
|
||||
This reroutes from an URL to a python view-function/class.
|
||||
|
||||
The main web/urls.py includes these routes for all urls starting with `webclient/`
|
||||
(the `webclient/` part should not be included again here).
|
||||
|
||||
"""
|
||||
|
||||
from django.urls import path
|
||||
|
||||
from evennia.web.webclient.urls import urlpatterns as evennia_webclient_urlpatterns
|
||||
|
||||
# add patterns here
|
||||
urlpatterns = [
|
||||
# path("url-pattern", imported_python_view),
|
||||
# path("url-pattern", imported_python_view),
|
||||
]
|
||||
|
||||
# read by Django
|
||||
urlpatterns = urlpatterns + evennia_webclient_urlpatterns
|
||||
24
evennia/timmy_world/web/website/README.md
Normal file
24
evennia/timmy_world/web/website/README.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Website views and other code
|
||||
|
||||
A 'view' is python code (a function or callable class) responsible for
|
||||
producing a HTML page for a user to view in response for going to a given URL
|
||||
in their browser. In Evennia lingo, it's similar in function to a Command, with
|
||||
the input/args being the URL/request and the output being a new web-page.
|
||||
|
||||
The urls.py file contains regular expressions that are run against the provided
|
||||
URL - when a match is found, the execution is passed to a view which is then
|
||||
responsible (usually) for producing the web page by filling in a _template_ - a
|
||||
HTML document that can have special tags in it that are replaced for dynamic
|
||||
content. It then returns the finished HTML page for the user to view.
|
||||
|
||||
See the [Django docs on Views](https://docs.djangoproject.com/en/4.1/topics/http/views/) for
|
||||
more information.
|
||||
|
||||
## Overriding a view
|
||||
|
||||
1. Copy the original code you want to change from `evennia/web/website/views/` into
|
||||
`mygame/web/website/views/` and edit it as you like.
|
||||
2. Look at `evennia/web/website/urls.py` and find the regex pointing to the view. Add this regex
|
||||
to your own `mygam/website/urls.pye` but change it to import and point to your
|
||||
changed version instead.
|
||||
3. Reload the server and the page now uses your version of the view.
|
||||
0
evennia/timmy_world/web/website/__init__.py
Normal file
0
evennia/timmy_world/web/website/__init__.py
Normal file
20
evennia/timmy_world/web/website/urls.py
Normal file
20
evennia/timmy_world/web/website/urls.py
Normal file
@@ -0,0 +1,20 @@
|
||||
"""
|
||||
This reroutes from an URL to a python view-function/class.
|
||||
|
||||
The main web/urls.py includes these routes for all urls (the root of the url)
|
||||
so it can reroute to all website pages.
|
||||
|
||||
"""
|
||||
|
||||
from django.urls import path
|
||||
|
||||
from evennia.web.website.urls import urlpatterns as evennia_website_urlpatterns
|
||||
|
||||
# add patterns here
|
||||
urlpatterns = [
|
||||
# path("url-pattern", imported_python_view),
|
||||
# path("url-pattern", imported_python_view),
|
||||
]
|
||||
|
||||
# read by Django
|
||||
urlpatterns = urlpatterns + evennia_website_urlpatterns
|
||||
0
evennia/timmy_world/web/website/views/__init__.py
Normal file
0
evennia/timmy_world/web/website/views/__init__.py
Normal file
10
evennia/timmy_world/world/README.md
Normal file
10
evennia/timmy_world/world/README.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# world/
|
||||
|
||||
This folder is meant as a miscellaneous folder for all that other stuff
|
||||
related to the game. Code which are not commands or typeclasses go
|
||||
here, like custom economy systems, combat code, batch-files etc.
|
||||
|
||||
You can restructure and even rename this folder as best fits your
|
||||
sense of organisation. Just remember that if you add new sub
|
||||
directories, you must add (optionally empty) `__init__.py` files in
|
||||
them for Python to be able to find the modules within.
|
||||
0
evennia/timmy_world/world/__init__.py
Normal file
0
evennia/timmy_world/world/__init__.py
Normal file
26
evennia/timmy_world/world/batch_cmds.ev
Normal file
26
evennia/timmy_world/world/batch_cmds.ev
Normal file
@@ -0,0 +1,26 @@
|
||||
#
|
||||
# A batch-command file is a way to build a game world
|
||||
# in a programmatic way, by placing a sequence of
|
||||
# build commands after one another. This allows for
|
||||
# using a real text editor to edit e.g. descriptions
|
||||
# rather than entering text on the command line.
|
||||
#
|
||||
# A batch-command file is loaded with @batchprocess in-game:
|
||||
#
|
||||
# @batchprocess[/interactive] tutorial_examples.batch_cmds
|
||||
#
|
||||
# A # as the first symbol on a line begins a comment and
|
||||
# marks the end of a previous command definition. This is important,
|
||||
# - every command must be separated by at least one line of comment.
|
||||
#
|
||||
# All supplied commands are given as normal, on their own line
|
||||
# and accept arguments in any format up until the first next
|
||||
# comment line begins. Extra whitespace is removed; an empty
|
||||
# line in a command definition translates into a newline.
|
||||
#
|
||||
# See `evennia/contrib/tutorial_examples/batch_cmds.ev` for
|
||||
# an example of a batch-command code. See also the batch-code
|
||||
# system for loading python-code this way.
|
||||
#
|
||||
|
||||
|
||||
58
evennia/timmy_world/world/help_entries.py
Normal file
58
evennia/timmy_world/world/help_entries.py
Normal file
@@ -0,0 +1,58 @@
|
||||
"""
|
||||
File-based help entries. These complements command-based help and help entries
|
||||
added in the database using the `sethelp` command in-game.
|
||||
|
||||
Control where Evennia reads these entries with `settings.FILE_HELP_ENTRY_MODULES`,
|
||||
which is a list of python-paths to modules to read.
|
||||
|
||||
A module like this should hold a global `HELP_ENTRY_DICTS` list, containing
|
||||
dicts that each represent a help entry. If no `HELP_ENTRY_DICTS` variable is
|
||||
given, all top-level variables that are dicts in the module are read as help
|
||||
entries.
|
||||
|
||||
Each dict is on the form
|
||||
::
|
||||
|
||||
{'key': <str>,
|
||||
'text': <str>}`` # the actual help text. Can contain # subtopic sections
|
||||
'category': <str>, # optional, otherwise settings.DEFAULT_HELP_CATEGORY
|
||||
'aliases': <list>, # optional
|
||||
'locks': <str> # optional, 'view' controls seeing in help index, 'read'
|
||||
# if the entry can be read. If 'view' is unset,
|
||||
# 'read' is used for the index. If unset, everyone
|
||||
# can read/view the entry.
|
||||
|
||||
"""
|
||||
|
||||
HELP_ENTRY_DICTS = [
|
||||
{
|
||||
"key": "evennia",
|
||||
"aliases": ["ev"],
|
||||
"category": "General",
|
||||
"locks": "read:perm(Developer)",
|
||||
"text": """
|
||||
Evennia is a MU-game server and framework written in Python. You can read more
|
||||
on https://www.evennia.com.
|
||||
|
||||
# subtopics
|
||||
|
||||
## Installation
|
||||
|
||||
You'll find installation instructions on https://www.evennia.com.
|
||||
|
||||
## Community
|
||||
|
||||
There are many ways to get help and communicate with other devs!
|
||||
|
||||
### Discussions
|
||||
|
||||
The Discussions forum is found at https://github.com/evennia/evennia/discussions.
|
||||
|
||||
### Discord
|
||||
|
||||
There is also a discord channel for chatting - connect using the
|
||||
following link: https://discord.gg/AJJpcRUhtF
|
||||
|
||||
""",
|
||||
},
|
||||
]
|
||||
90
evennia/timmy_world/world/prototypes.py
Normal file
90
evennia/timmy_world/world/prototypes.py
Normal file
@@ -0,0 +1,90 @@
|
||||
"""
|
||||
Prototypes
|
||||
|
||||
A prototype is a simple way to create individualized instances of a
|
||||
given typeclass. It is dictionary with specific key names.
|
||||
|
||||
For example, you might have a Sword typeclass that implements everything a
|
||||
Sword would need to do. The only difference between different individual Swords
|
||||
would be their key, description and some Attributes. The Prototype system
|
||||
allows to create a range of such Swords with only minor variations. Prototypes
|
||||
can also inherit and combine together to form entire hierarchies (such as
|
||||
giving all Sabres and all Broadswords some common properties). Note that bigger
|
||||
variations, such as custom commands or functionality belong in a hierarchy of
|
||||
typeclasses instead.
|
||||
|
||||
A prototype can either be a dictionary placed into a global variable in a
|
||||
python module (a 'module-prototype') or stored in the database as a dict on a
|
||||
special Script (a db-prototype). The former can be created just by adding dicts
|
||||
to modules Evennia looks at for prototypes, the latter is easiest created
|
||||
in-game via the `olc` command/menu.
|
||||
|
||||
Prototypes are read and used to create new objects with the `spawn` command
|
||||
or directly via `evennia.spawn` or the full path `evennia.prototypes.spawner.spawn`.
|
||||
|
||||
A prototype dictionary have the following keywords:
|
||||
|
||||
Possible keywords are:
|
||||
- `prototype_key` - the name of the prototype. This is required for db-prototypes,
|
||||
for module-prototypes, the global variable name of the dict is used instead
|
||||
- `prototype_parent` - string pointing to parent prototype if any. Prototype inherits
|
||||
in a similar way as classes, with children overriding values in their parents.
|
||||
- `key` - string, the main object identifier.
|
||||
- `typeclass` - string, if not set, will use `settings.BASE_OBJECT_TYPECLASS`.
|
||||
- `location` - this should be a valid object or #dbref.
|
||||
- `home` - valid object or #dbref.
|
||||
- `destination` - only valid for exits (object or #dbref).
|
||||
- `permissions` - string or list of permission strings.
|
||||
- `locks` - a lock-string to use for the spawned object.
|
||||
- `aliases` - string or list of strings.
|
||||
- `attrs` - Attributes, expressed as a list of tuples on the form `(attrname, value)`,
|
||||
`(attrname, value, category)`, or `(attrname, value, category, locks)`. If using one
|
||||
of the shorter forms, defaults are used for the rest.
|
||||
- `tags` - Tags, as a list of tuples `(tag,)`, `(tag, category)` or `(tag, category, data)`.
|
||||
- Any other keywords are interpreted as Attributes with no category or lock.
|
||||
These will internally be added to `attrs` (equivalent to `(attrname, value)`.
|
||||
|
||||
See the `spawn` command and `evennia.prototypes.spawner.spawn` for more info.
|
||||
|
||||
"""
|
||||
|
||||
## example of module-based prototypes using
|
||||
## the variable name as `prototype_key` and
|
||||
## simple Attributes
|
||||
|
||||
# from random import randint
|
||||
#
|
||||
# GOBLIN = {
|
||||
# "key": "goblin grunt",
|
||||
# "health": lambda: randint(20,30),
|
||||
# "resists": ["cold", "poison"],
|
||||
# "attacks": ["fists"],
|
||||
# "weaknesses": ["fire", "light"],
|
||||
# "tags": = [("greenskin", "monster"), ("humanoid", "monster")]
|
||||
# }
|
||||
#
|
||||
# GOBLIN_WIZARD = {
|
||||
# "prototype_parent": "GOBLIN",
|
||||
# "key": "goblin wizard",
|
||||
# "spells": ["fire ball", "lighting bolt"]
|
||||
# }
|
||||
#
|
||||
# GOBLIN_ARCHER = {
|
||||
# "prototype_parent": "GOBLIN",
|
||||
# "key": "goblin archer",
|
||||
# "attacks": ["short bow"]
|
||||
# }
|
||||
#
|
||||
# This is an example of a prototype without a prototype
|
||||
# (nor key) of its own, so it should normally only be
|
||||
# used as a mix-in, as in the example of the goblin
|
||||
# archwizard below.
|
||||
# ARCHWIZARD_MIXIN = {
|
||||
# "attacks": ["archwizard staff"],
|
||||
# "spells": ["greater fire ball", "greater lighting"]
|
||||
# }
|
||||
#
|
||||
# GOBLIN_ARCHWIZARD = {
|
||||
# "key": "goblin archwizard",
|
||||
# "prototype_parent" : ("GOBLIN_WIZARD", "ARCHWIZARD_MIXIN")
|
||||
# }
|
||||
49
evolution/bitcoin_scripter.py
Normal file
49
evolution/bitcoin_scripter.py
Normal file
@@ -0,0 +1,49 @@
|
||||
"""Phase 22: Autonomous Bitcoin Scripting.
|
||||
|
||||
Generates and validates complex Bitcoin scripts (multisig, timelocks, etc.) for sovereign asset management.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import json
|
||||
from typing import List, Dict, Any
|
||||
from agent.gemini_adapter import GeminiAdapter
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class BitcoinScripter:
|
||||
def __init__(self):
|
||||
# In a real implementation, this would use a library like python-bitcoinlib
|
||||
self.adapter = GeminiAdapter()
|
||||
|
||||
def generate_script(self, requirements: str) -> Dict[str, Any]:
|
||||
"""Generates a Bitcoin script based on natural language requirements."""
|
||||
logger.info(f"Generating Bitcoin script for requirements: {requirements}")
|
||||
|
||||
prompt = f"""
|
||||
Requirements: {requirements}
|
||||
|
||||
Please generate a valid Bitcoin Script (Miniscript or raw Script) that satisfies these requirements.
|
||||
Include a detailed explanation of the script's logic, security properties, and potential failure modes.
|
||||
Identify the 'Sovereign Safeguards' implemented in the script.
|
||||
|
||||
Format the output as JSON:
|
||||
{{
|
||||
"requirements": "{requirements}",
|
||||
"script_type": "...",
|
||||
"script_hex": "...",
|
||||
"script_asm": "...",
|
||||
"explanation": "...",
|
||||
"security_properties": [...],
|
||||
"sovereign_safeguards": [...]
|
||||
}}
|
||||
"""
|
||||
result = self.adapter.generate(
|
||||
model="gemini-3.1-pro-preview",
|
||||
prompt=prompt,
|
||||
system_instruction="You are Timmy's Bitcoin Scripter. Your goal is to ensure Timmy's financial assets are protected by the most secure and sovereign code possible.",
|
||||
thinking=True,
|
||||
response_mime_type="application/json"
|
||||
)
|
||||
|
||||
script_data = json.loads(result["text"])
|
||||
return script_data
|
||||
1
evolution/cognitive_personalizer.py
Normal file
1
evolution/cognitive_personalizer.py
Normal file
@@ -0,0 +1 @@
|
||||
...
|
||||
1
evolution/data_lake_optimizer.py
Normal file
1
evolution/data_lake_optimizer.py
Normal file
@@ -0,0 +1 @@
|
||||
...
|
||||
1
evolution/domain_distiller.py
Normal file
1
evolution/domain_distiller.py
Normal file
@@ -0,0 +1 @@
|
||||
...
|
||||
49
evolution/lightning_client.py
Normal file
49
evolution/lightning_client.py
Normal file
@@ -0,0 +1,49 @@
|
||||
"""Phase 22: Lightning Network Integration.
|
||||
|
||||
Manages Lightning channels and payments for low-latency, sovereign transactions.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import json
|
||||
from typing import List, Dict, Any
|
||||
from agent.gemini_adapter import GeminiAdapter
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class LightningClient:
|
||||
def __init__(self):
|
||||
# In a real implementation, this would interface with LND, Core Lightning, or Greenlight
|
||||
self.adapter = GeminiAdapter()
|
||||
|
||||
def plan_payment_route(self, destination: str, amount_sats: int) -> Dict[str, Any]:
|
||||
"""Plans an optimal payment route through the Lightning Network."""
|
||||
logger.info(f"Planning Lightning payment of {amount_sats} sats to {destination}.")
|
||||
|
||||
prompt = f"""
|
||||
Destination: {destination}
|
||||
Amount: {amount_sats} sats
|
||||
|
||||
Please simulate an optimal payment route through the Lightning Network.
|
||||
Identify potential bottlenecks, fee estimates, and privacy-preserving routing strategies.
|
||||
Generate a 'Lightning Execution Plan'.
|
||||
|
||||
Format the output as JSON:
|
||||
{{
|
||||
"destination": "{destination}",
|
||||
"amount_sats": {amount_sats},
|
||||
"route_plan": [...],
|
||||
"fee_estimate_sats": "...",
|
||||
"privacy_score": "...",
|
||||
"execution_directives": [...]
|
||||
}}
|
||||
"""
|
||||
result = self.adapter.generate(
|
||||
model="gemini-3.1-pro-preview",
|
||||
prompt=prompt,
|
||||
system_instruction="You are Timmy's Lightning Client. Your goal is to ensure Timmy's transactions are fast, cheap, and private.",
|
||||
thinking=True,
|
||||
response_mime_type="application/json"
|
||||
)
|
||||
|
||||
route_data = json.loads(result["text"])
|
||||
return route_data
|
||||
1
evolution/memory_compressor.py
Normal file
1
evolution/memory_compressor.py
Normal file
@@ -0,0 +1 @@
|
||||
...
|
||||
1
evolution/self_correction_generator.py
Normal file
1
evolution/self_correction_generator.py
Normal file
@@ -0,0 +1 @@
|
||||
...
|
||||
1
evolution/skill_synthesizer.py
Normal file
1
evolution/skill_synthesizer.py
Normal file
@@ -0,0 +1 @@
|
||||
...
|
||||
47
evolution/sovereign_accountant.py
Normal file
47
evolution/sovereign_accountant.py
Normal file
@@ -0,0 +1,47 @@
|
||||
"""Phase 22: Sovereign Accountant.
|
||||
|
||||
Tracks balances, transaction history, and financial health across the sovereign vault.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import json
|
||||
from typing import List, Dict, Any
|
||||
from agent.gemini_adapter import GeminiAdapter
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class SovereignAccountant:
|
||||
def __init__(self):
|
||||
self.adapter = GeminiAdapter()
|
||||
|
||||
def generate_financial_report(self, transaction_history: List[Dict[str, Any]]) -> Dict[str, Any]:
|
||||
"""Generates a comprehensive financial health report."""
|
||||
logger.info("Generating sovereign financial health report.")
|
||||
|
||||
prompt = f"""
|
||||
Transaction History:
|
||||
{json.dumps(transaction_history, indent=2)}
|
||||
|
||||
Please perform a 'Deep Financial Audit' of this history.
|
||||
Identify spending patterns, income sources, and potential 'Sovereign Risks' (e.g., over-exposure to a single counterparty).
|
||||
Generate a 'Financial Health Score' and proposed 'Sovereign Rebalancing' strategies.
|
||||
|
||||
Format the output as JSON:
|
||||
{{
|
||||
"health_score": "...",
|
||||
"audit_summary": "...",
|
||||
"spending_patterns": [...],
|
||||
"sovereign_risks": [...],
|
||||
"rebalancing_strategies": [...]
|
||||
}}
|
||||
"""
|
||||
result = self.adapter.generate(
|
||||
model="gemini-3.1-pro-preview",
|
||||
prompt=prompt,
|
||||
system_instruction="You are Timmy's Sovereign Accountant. Your goal is to ensure Timmy's financial foundation is robust and aligned with his long-term goals.",
|
||||
thinking=True,
|
||||
response_mime_type="application/json"
|
||||
)
|
||||
|
||||
report_data = json.loads(result["text"])
|
||||
return report_data
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user