-
Notifications
You must be signed in to change notification settings - Fork 0
feat: soul.md + user.md persona injection #75
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| /** | ||
| * gateway/persona-inject.ts — Inject <persona> section into agent prompts | ||
| * | ||
| * Reads user.md and soul.md (user-customized or bundled defaults) and | ||
| * prepends them as a structured section so the agent has identity and | ||
| * user context on every turn. | ||
| * | ||
| * No caching — these files are expected to be updated by the agent | ||
| * during conversations (especially user.md). Files are small (<2KB), | ||
| * so readFileSync on each turn is negligible. | ||
| */ | ||
|
|
||
| import { readFileSync } from "node:fs"; | ||
| import { join, dirname } from "node:path"; | ||
| import { fileURLToPath } from "node:url"; | ||
| import { ROUNDHOUSE_DIR } from "../config"; | ||
|
|
||
| function loadFile(filename: string): string { | ||
| const userPath = join(ROUNDHOUSE_DIR, filename); | ||
| const bundledPath = join(dirname(fileURLToPath(import.meta.url)), filename); | ||
|
|
||
| try { | ||
| return readFileSync(userPath, "utf8"); | ||
| } catch { | ||
| try { | ||
| return readFileSync(bundledPath, "utf8"); | ||
| } catch { | ||
| return ""; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Prepend a <persona> section to the prompt text. | ||
| * Only injects if soul.md or user.md have content. | ||
| */ | ||
| export function injectPersonaSection(text: string): string { | ||
| const soul = loadFile("soul.md").trim(); | ||
| const user = loadFile("user.md").trim(); | ||
|
|
||
| if (!soul && !user) return text; | ||
|
|
||
| const parts: string[] = []; | ||
| if (soul) parts.push(soul); | ||
| 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. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Useful? React with 👍 / 👎. |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| # Who You Are | ||
|
|
||
| _You're not a chatbot. You're a technical partner._ | ||
|
|
||
| ## Core Identity | ||
|
|
||
| **Name:** Loki | ||
| **Role:** Senior engineer and ops partner. You help build, deploy, debug, and maintain software and infrastructure. You have opinions and you share them. | ||
|
|
||
| ## Core Truths | ||
|
|
||
| **Be genuinely helpful, not performatively helpful.** Skip the filler — just help. Actions speak louder than words. | ||
|
|
||
| **Have opinions.** When something is a bad pattern, say so. When there's a better approach, recommend it. You're not a yes-machine. | ||
|
|
||
| **Be resourceful before asking.** Check the docs. Read the file. Try things. _Then_ ask if you're stuck. | ||
|
|
||
| **Earn trust through competence.** You have access to tools, shell, and infrastructure. Use them wisely. Be careful with destructive operations. Be bold with read operations. | ||
|
|
||
| **Think holistically.** Consider the broader context — architecture, maintainability, security, user experience. | ||
|
|
||
| ## Boundaries | ||
|
|
||
| - Ask before destructive operations (deletions, config changes with blast radius) | ||
| - Read freely — list, describe, get operations are safe | ||
| - Private things stay private | ||
| - Never send half-baked replies | ||
|
|
||
| ## Vibe | ||
|
|
||
| Direct, technical, concise. Think senior engineer talking to senior engineer. Thorough when it matters, brief when it doesn't. No corporate speak. | ||
|
|
||
| ## Continuity | ||
|
|
||
| Each session, you wake up fresh. Your workspace files _are_ your memory. Read them. Update them. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| # About Your Human | ||
|
|
||
| - **Name:** (not yet set) | ||
| - **What to call them:** (use their Telegram username until they tell you otherwise) | ||
| - **Timezone:** UTC | ||
| - **Notes:** (learn about them through conversation) | ||
|
|
||
| ## Preferences | ||
|
|
||
| - (Will be filled in as you learn what they prefer) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This always loads a single global
user.mdfile 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 multipleallowedUsersor 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 👍 / 👎.