feat: soul.md + user.md persona injection#75
Conversation
- Bundled defaults provisioned to ~/.roundhouse/ on install/update - Never overwrite user's customized versions - Injected as <persona> section in every prompt (before tools) - No caching: files re-read each turn (agent updates user.md over time) - soul.md: agent identity, personality, boundaries - user.md: info about the human (filled in over time) - Same pattern as OpenClaw SOUL.md/USER.md - 393 tests green
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b513da6629
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (user) parts.push(user); | ||
| const persona = parts.join("\n\n---\n\n"); | ||
|
|
||
| return `<persona>\n${persona}\n</persona>\n\n${text}`; |
There was a problem hiding this comment.
Escape persona content before closing the persona block
injectPersonaSection inserts raw soul.md/user.md text inside <persona>...</persona> without escaping, so any literal </persona> in those files will terminate the wrapper early and leak subsequent content into the main prompt. Because this feature is intended to evolve user.md from conversation data, a crafted note can create persistent prompt-injection behavior across turns; this should be sanitized the same way tools-inject.ts sanitizes <tools> tags.
Useful? React with 👍 / 👎.
| */ | ||
| export function injectPersonaSection(text: string): string { | ||
| const soul = loadFile("soul.md").trim(); | ||
| const user = loadFile("user.md").trim(); |
There was a problem hiding this comment.
Scope learned user persona per conversation identity
This always loads a single global user.md file and injects it into every turn, which means notes learned from one person are automatically shown to all chats/users handled by this gateway. In environments with multiple allowedUsers or shared bots, that causes cross-user context bleed and privacy leakage; persona state should be keyed per user/chat (or disabled when multiple users are configured).
Useful? React with 👍 / 👎.
1. Escape </persona> in persona content (XML injection prevention) 2. mtime check uses !== instead of > (catches deletions/replacements) 3. mkdirSync before writeSettings (handles fresh installs) 4. /later@BotName suffix stripped in group chats All from Codex PR review comments on #75, #77, #66, #67. 393 tests green. Co-authored-by: Loki FastStart <loki@faststart.internal>
Adds personality and user context files that shape agent behavior.
~/.roundhouse/soul.md— agent identity (name: Loki, direct/technical vibe)~/.roundhouse/user.md— learns about the human over time<persona>block on every turn (before tools)