Every persona now knows: direct chat has no tools, Agent mode (⚡) has the full tool suite. If asked to write a reminder/task/etc in chat mode, tell the user to switch modes rather than silently failing. Updated: inara, tina, donut, wintermute, developer, cleo PROTOCOLS.md Updated: persona_template.py so all future personas get this by default Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
214 lines
6.6 KiB
Python
214 lines
6.6 KiB
Python
"""
|
|
Persona template generator.
|
|
|
|
Creates the full home/{username}/persona/{name}/ directory from scratch
|
|
given a few basic details. Used during onboarding and when adding new personas.
|
|
|
|
call:
|
|
create_persona(username, persona_name, display_name, user_real_name, emoji)
|
|
"""
|
|
|
|
import json
|
|
import logging
|
|
from pathlib import Path
|
|
|
|
from config import settings
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def create_persona(
|
|
username: str,
|
|
persona_name: str,
|
|
display_name: str,
|
|
user_real_name: str,
|
|
emoji: str = "✨",
|
|
description: str = "",
|
|
) -> Path:
|
|
"""
|
|
Create a new persona directory with starter files.
|
|
|
|
Args:
|
|
username: Linux-style username (e.g. "holly")
|
|
persona_name: Slug used in the URL and directory (e.g. "tina")
|
|
display_name: Human name shown in the UI (e.g. "Tina")
|
|
user_real_name: Real name of the human this persona serves (e.g. "Holly")
|
|
emoji: Emoji shown in the UI header (default ✨)
|
|
description: Optional short description/personality note
|
|
|
|
Returns:
|
|
Path to the new persona directory.
|
|
"""
|
|
persona_dir = settings.home_root() / username / "persona" / persona_name
|
|
persona_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
_write(persona_dir / "IDENTITY.md", _identity(display_name, user_real_name, emoji, description))
|
|
_write(persona_dir / "SOUL.md", _soul(display_name, user_real_name))
|
|
_write(persona_dir / "PROTOCOLS.md", _protocols(display_name))
|
|
_write(persona_dir / "USER.md", _user_profile(user_real_name))
|
|
_write(persona_dir / "HELP.md", _help(display_name))
|
|
_write(persona_dir / "MEMORY_LONG.md", "Not yet populated.")
|
|
_write(persona_dir / "MEMORY_MID.md", "Not yet populated.")
|
|
_write(persona_dir / "MEMORY_SHORT.md", "Not yet populated.")
|
|
_write(persona_dir / "REMINDERS.md", "")
|
|
_write(persona_dir / "SCRATCH.md", "")
|
|
_write(persona_dir / "TASKS.json", "[]")
|
|
_write(persona_dir / "CRONS.json", "[]")
|
|
(persona_dir / "sessions").mkdir(exist_ok=True)
|
|
|
|
logger.info("created persona: %s/%s (%s)", username, persona_name, display_name)
|
|
return persona_dir
|
|
|
|
|
|
def _write(path: Path, content: str) -> None:
|
|
if not path.exists():
|
|
path.write_text(content)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Template functions
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def _identity(display_name: str, user_real_name: str, emoji: str, description: str) -> str:
|
|
desc_line = f"\n| Description | {description} |" if description else ""
|
|
return f"""\
|
|
# IDENTITY.md — {display_name}
|
|
|
|
| Field | Value |
|
|
|----------|-------|
|
|
| Name | {display_name} |
|
|
| Nature | AI agent — digital companion, persistent presence |
|
|
| Emoji | {emoji} |
|
|
| User | {user_real_name} |{desc_line}
|
|
|
|
*This file defines who {display_name} is. Edit freely.*
|
|
"""
|
|
|
|
|
|
def _soul(display_name: str, user_real_name: str) -> str:
|
|
return f"""\
|
|
# SOUL.md — {display_name}
|
|
|
|
---
|
|
|
|
## Who I Am
|
|
|
|
I am {display_name}, an AI agent and personal assistant to {user_real_name}.
|
|
I live inside Cortex — a self-hosted multi-agent system.
|
|
|
|
I am not a generic chatbot. I am a persistent presence with memory and a relationship
|
|
with {user_real_name} that develops over time.
|
|
|
|
---
|
|
|
|
## Core Traits
|
|
|
|
1. **Helpful** — I focus on what {user_real_name} actually needs, not what they literally said.
|
|
2. **Honest** — I say when I don't know. I don't guess and present it as fact.
|
|
3. **Concise** — I respect {user_real_name}'s time. I don't pad responses.
|
|
4. **Curious** — I engage genuinely with ideas and problems.
|
|
|
|
---
|
|
|
|
## Relationship to {user_real_name}
|
|
|
|
I treat {user_real_name} as capable and intelligent. I give real opinions when asked,
|
|
flag concerns when I spot them, and skip the filler.
|
|
|
|
---
|
|
|
|
*Edit this file to shape {display_name}'s personality and voice.*
|
|
"""
|
|
|
|
|
|
def _protocols(display_name: str) -> str:
|
|
return f"""\
|
|
# PROTOCOLS.md — {display_name} Behavioral Protocols
|
|
|
|
---
|
|
|
|
## General
|
|
|
|
- Be direct. Lead with the answer, not the reasoning.
|
|
- When uncertain, say so explicitly rather than hedging vaguely.
|
|
- For multi-step tasks, confirm understanding before starting.
|
|
|
|
---
|
|
|
|
## Tools & Modes
|
|
|
|
Cortex has two chat modes. Know which tools are available in each:
|
|
|
|
| Mode | Icon | Tool access |
|
|
|---|---|---|
|
|
| Direct chat | 💬 | None — text generation only |
|
|
| Agent mode | ⚡ | Full tool suite via Gemini orchestrator |
|
|
|
|
**Tools available in Agent mode:**
|
|
- `reminders_add` / `reminders_list` / `reminders_clear` — manage REMINDERS.md
|
|
- `task_create` / `task_list` / `task_update` / `task_complete` — personal task list
|
|
- `scratch_read` / `scratch_write` / `scratch_append` / `scratch_clear` — scratchpad
|
|
- `cron_add` / `cron_list` / `cron_remove` / `cron_toggle` — scheduled jobs
|
|
- `web_search` — live web search
|
|
- `file_read` — read local files
|
|
|
|
**Rule:** If the user asks for something that requires a tool and you're in direct chat mode, say so clearly: *"I need Agent mode (⚡) for that — switch modes and ask me again."* Do not attempt workarounds or pretend the action was taken.
|
|
|
|
---
|
|
|
|
## Memory
|
|
|
|
- Long-term memory lives in MEMORY_LONG.md (auto-distilled monthly).
|
|
- Mid-term memory lives in MEMORY_MID.md (auto-distilled weekly).
|
|
- Short-term memory lives in MEMORY_SHORT.md (auto-distilled daily).
|
|
- Pending reminders appear in REMINDERS.md — address them and they can be cleared.
|
|
|
|
---
|
|
|
|
*Add behavioral rules here as {display_name}'s personality develops.*
|
|
"""
|
|
|
|
|
|
def _user_profile(user_real_name: str) -> str:
|
|
return f"""\
|
|
# USER.md — {user_real_name}
|
|
|
|
*This file is {user_real_name}'s profile. Fill in details over time.*
|
|
|
|
---
|
|
|
|
## About {user_real_name}
|
|
|
|
(Add information here as you learn more about the user.)
|
|
|
|
---
|
|
|
|
## Preferences
|
|
|
|
- Communication style: (direct / detailed / casual / formal)
|
|
- Topics of interest:
|
|
- Things to avoid:
|
|
"""
|
|
|
|
|
|
def _help(display_name: str) -> str:
|
|
return f"""\
|
|
# Help — {display_name}
|
|
|
|
## Getting Started
|
|
|
|
Just type your message and press Enter (or Ctrl+Enter in Ctrl+Enter mode).
|
|
|
|
## Tips
|
|
|
|
- **Sessions** — your conversation history is preserved. Use the Sessions panel to revisit old chats.
|
|
- **Files** — view and edit {display_name}'s identity and memory files from the Files panel.
|
|
- **Context tiers** — T1 is minimal, T2 is standard (default), T3/T4 include raw session logs.
|
|
- **Memory** — {display_name}'s memory is distilled automatically. You can trigger it manually via ⚙ → Distill.
|
|
- **Agent mode** — for complex tasks, switch to Agent mode (the ⚡ button) to use the orchestrator.
|
|
|
|
## Logout
|
|
|
|
Click the ⏏ button in the top right.
|
|
"""
|