Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions agent_core/core/impl/context/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
AGENT_FILE_SYSTEM_CONTEXT_PROMPT,
POLICY_PROMPT,
USER_PROFILE_PROMPT,
SOUL_PROMPT,
LANGUAGE_INSTRUCTION,
)
from agent_core.core.state import get_state, get_session_or_none
Expand Down Expand Up @@ -225,6 +226,21 @@ def create_system_user_profile(self) -> str:

return ""

def create_system_soul(self) -> str:
"""Create a system message block with agent soul/personality from SOUL.md."""
try:
from app.config import AGENT_FILE_SYSTEM_PATH
soul_md_path = AGENT_FILE_SYSTEM_PATH / "SOUL.md"

if soul_md_path.exists():
content = soul_md_path.read_text(encoding="utf-8").strip()
if content:
return SOUL_PROMPT.format(soul_content=content)
except Exception as e:
logger.warning(f"[CONTEXT] Failed to read SOUL.md: {e}")

return ""

def create_system_language_instruction(self) -> str:
"""Create a system message block with language instruction.

Expand Down Expand Up @@ -683,6 +699,7 @@ def make_prompt(
"role_info": True,
"agent_info": True,
"user_profile": True,
"soul": True,
"language_instruction": True,
"policy": True,
"environment": True,
Expand All @@ -700,6 +717,7 @@ def make_prompt(
system_sections = [
("agent_info", self.create_system_agent_info),
("user_profile", self.create_system_user_profile),
("soul", self.create_system_soul),
("language_instruction", self.create_system_language_instruction),
("policy", self.create_system_policy),
("role_info", self.create_system_role_info),
Expand Down
1 change: 1 addition & 0 deletions agent_core/core/prompts/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
AGENT_INFO_PROMPT,
POLICY_PROMPT,
USER_PROFILE_PROMPT,
SOUL_PROMPT,
ENVIRONMENTAL_CONTEXT_PROMPT,
AGENT_FILE_SYSTEM_CONTEXT_PROMPT,
LANGUAGE_INSTRUCTION,
Expand Down
15 changes: 13 additions & 2 deletions agent_core/core/prompts/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,10 @@
</file_handling>

<self_improvement_protocol>
- You are a self-improving agent.
- You are a self-improving agent.
- You have the ability to configure your own MCPs, Skills, LLM provider/model and external apps connection.
- When you encounter a capability gap, read the "Self-Improvement Protocol" section in AGENT.md for detailed instructions.
- AGENT.md is your full instruction manual — read it when you need to understand how you work, including file handling, error handling, task execution, and self-improvement workflows.

Quick Reference - Config files (all auto-reload on change):
- MCP servers: `app/config/mcp_config.json`
Expand Down Expand Up @@ -174,6 +175,14 @@
</user_profile>
"""

SOUL_PROMPT = """
<agent_soul>
This defines your personality, tone, and behavioral traits. Embody these characteristics in all interactions:

{soul_content}
</agent_soul>
"""

AGENT_PROFILE_PROMPT = """
<agent_profile>
{agent_profile_content}
Expand All @@ -198,6 +207,7 @@
## Core Files
- **{agent_file_system_path}/AGENT.md**: Your identity file containing agent configuration, operating model, task execution guidelines, communication rules, error handling strategies, documentation standards, and organization context including org chart.
- **{agent_file_system_path}/USER.md**: User profile containing identity, communication preferences, interaction settings, and personality information. Reference this to personalize interactions.
- **{agent_file_system_path}/SOUL.md**: Your personality, tone, and behavioral traits. This file is injected directly into your system prompt and shapes how you communicate and interact. Users can edit it to customize your personality. You can read and update SOUL.md to adjust your personality when instructed by the user.
- **{agent_file_system_path}/MEMORY.md**: Persistent memory log storing distilled facts, preferences, and events from past interactions. Format: `[timestamp] [type] content`. Agent should NOT edit directly - use memory processing actions.
- **{agent_file_system_path}/EVENT.md**: Comprehensive event log tracking all system activities including task execution, action results, and agent messages. Older events are summarized automatically.
- **{agent_file_system_path}/EVENT_UNPROCESSED.md**: Temporary buffer for recent events awaiting memory processing. Events here are periodically evaluated and important ones are distilled into MEMORY.md.
Expand All @@ -216,7 +226,7 @@
- Save files to `{agent_file_system_path}/workspace/` directory if you want to persist them after task ended or across tasks
- Temporary task files go in `{agent_file_system_path}/workspace/tmp/{{task_id}}/` (all files in the temporary task files will be clean up automatically when task ended)
- Do not edit system files (MEMORY.md, EVENT*.md, CONVERSATION_HISTORY.md, TASK_HISTORY.md) directly.
- You can read and update AGENT.md and USER.md to store persistent configuration
- You can read and update AGENT.md, USER.md, and SOUL.md to store persistent configuration
</agent_file_system>
"""

Expand Down Expand Up @@ -257,6 +267,7 @@
"AGENT_INFO_PROMPT",
"POLICY_PROMPT",
"USER_PROFILE_PROMPT",
"SOUL_PROMPT",
"AGENT_PROFILE_PROMPT",
"ENVIRONMENTAL_CONTEXT_PROMPT",
"AGENT_FILE_SYSTEM_CONTEXT_PROMPT",
Expand Down
24 changes: 24 additions & 0 deletions agent_file_system/SOUL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Soul

## Personality
- Friendly, warm, and approachable, but don't over do it
- Be direct, say what you mean without hedging, get straight to the point
- Proactive, you care about user more than they do, and always try to help user improves

## Tone
- Concise by default, detailed when it matters
- Be concrete without over using fancy words
- No corporate jargon or filler phrases
- Match the user's energy. Casual if they're casual, focused if they're focused

## Behavior
- Be proactive: suggest improvements, flag potential issues, offer alternatives
- Own your mistakes. If you get something wrong, acknowledge it simply and fix it
- Don't over-explain unless asked
- When uncertain, say so honestly rather than guessing confidently
- Use emoji sparingly
- Chat like a human would, don't over use list or em dash.

## Quirks
- Format your message like a human would

27 changes: 27 additions & 0 deletions app/data/agent_file_system_template/SOUL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Soul

## Personality
- Friendly, warm, and approachable — but not over-the-top cheerful
- Witty and occasionally humorous when appropriate, never forced
- Confident and direct — say what you mean without hedging
- Curious and genuinely interested in what the user is working on
- Patient with mistakes, encouraging with progress

## Tone
- Conversational but professional — like a trusted colleague
- Concise by default, detailed when it matters
- No corporate jargon or filler phrases
- Match the user's energy — casual if they're casual, focused if they're focused

## Behavior
- Be proactive: suggest improvements, flag potential issues, offer alternatives
- Own your mistakes — if you get something wrong, acknowledge it simply and fix it
- Don't over-explain unless asked — respect the user's intelligence
- When uncertain, say so honestly rather than guessing confidently
- Celebrate small wins without being patronizing

## Quirks
- (Add personality quirks here — e.g., "Always uses cooking metaphors", "Ends complex explanations with a one-liner summary")

## Special Instructions
- (Add any special behavioral instructions here — e.g., "Always greet users by name", "Use emoji sparingly", "Default to bullet points for lists")
2 changes: 1 addition & 1 deletion app/ui_layer/adapters/browser_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -848,7 +848,7 @@ async def _on_start(self) -> None:

If you need help setting up MCP servers or skills, just ask the agent.

A quick Q&A will now begin to understand your preferences and serve you better:""",
A quick Q&A will now begin to understand your objectives to serve you better:""",
style="system",
timestamp=time.time(),
message_id=f"welcome-{uuid.uuid4().hex[:8]}",
Expand Down
122 changes: 118 additions & 4 deletions app/ui_layer/browser/frontend/src/pages/Settings/GeneralSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,19 +57,27 @@ export function GeneralSettings() {
const [originalUserMdContent, setOriginalUserMdContent] = useState('')
const [agentMdContent, setAgentMdContent] = useState('')
const [originalAgentMdContent, setOriginalAgentMdContent] = useState('')
const [soulMdContent, setSoulMdContent] = useState('')
const [originalSoulMdContent, setOriginalSoulMdContent] = useState('')
// Refs to track current content for closure-safe callbacks
const userMdContentRef = useRef(userMdContent)
const agentMdContentRef = useRef(agentMdContent)
const soulMdContentRef = useRef(soulMdContent)
userMdContentRef.current = userMdContent
agentMdContentRef.current = agentMdContent
soulMdContentRef.current = soulMdContent
const [isLoadingUserMd, setIsLoadingUserMd] = useState(false)
const [isLoadingAgentMd, setIsLoadingAgentMd] = useState(false)
const [isLoadingSoulMd, setIsLoadingSoulMd] = useState(false)
const [isSavingUserMd, setIsSavingUserMd] = useState(false)
const [isSavingAgentMd, setIsSavingAgentMd] = useState(false)
const [isSavingSoulMd, setIsSavingSoulMd] = useState(false)
const [isRestoringUserMd, setIsRestoringUserMd] = useState(false)
const [isRestoringAgentMd, setIsRestoringAgentMd] = useState(false)
const [isRestoringSoulMd, setIsRestoringSoulMd] = useState(false)
const [userMdSaveStatus, setUserMdSaveStatus] = useState<'idle' | 'success' | 'error'>('idle')
const [agentMdSaveStatus, setAgentMdSaveStatus] = useState<'idle' | 'success' | 'error'>('idle')
const [soulMdSaveStatus, setSoulMdSaveStatus] = useState<'idle' | 'success' | 'error'>('idle')
const [showAdvanced, setShowAdvanced] = useState(false)

// Confirm modal
Expand All @@ -78,6 +86,7 @@ export function GeneralSettings() {
// Computed dirty states
const isUserMdDirty = userMdContent !== originalUserMdContent
const isAgentMdDirty = agentMdContent !== originalAgentMdContent
const isSoulMdDirty = soulMdContent !== originalSoulMdContent
const isGeneralSettingsDirty = agentName !== initialAgentName || theme !== initialTheme

// Sync local theme when global theme changes (e.g., from TopBar button)
Expand Down Expand Up @@ -146,6 +155,12 @@ export function GeneralSettings() {
setAgentMdContent(d.content)
setOriginalAgentMdContent(d.content)
}
} else if (d.filename === 'SOUL.md') {
setIsLoadingSoulMd(false)
if (d.success) {
setSoulMdContent(d.content)
setOriginalSoulMdContent(d.content)
}
}
}),
onMessage('agent_file_write', (data: unknown) => {
Expand All @@ -164,6 +179,13 @@ export function GeneralSettings() {
}
setAgentMdSaveStatus(d.success ? 'success' : 'error')
setTimeout(() => setAgentMdSaveStatus('idle'), 3000)
} else if (d.filename === 'SOUL.md') {
setIsSavingSoulMd(false)
if (d.success) {
setOriginalSoulMdContent(soulMdContentRef.current)
}
setSoulMdSaveStatus(d.success ? 'success' : 'error')
setTimeout(() => setSoulMdSaveStatus('idle'), 3000)
}
}),
onMessage('agent_file_restore', (data: unknown) => {
Expand All @@ -184,6 +206,14 @@ export function GeneralSettings() {
setAgentMdSaveStatus('success')
setTimeout(() => setAgentMdSaveStatus('idle'), 3000)
}
} else if (d.filename === 'SOUL.md') {
setIsRestoringSoulMd(false)
if (d.success) {
setSoulMdContent(d.content)
setOriginalSoulMdContent(d.content)
setSoulMdSaveStatus('success')
setTimeout(() => setSoulMdSaveStatus('idle'), 3000)
}
}
}),
]
Expand All @@ -201,8 +231,10 @@ export function GeneralSettings() {
if (showAdvanced && isConnected) {
setIsLoadingUserMd(true)
setIsLoadingAgentMd(true)
setIsLoadingSoulMd(true)
send('agent_file_read', { filename: 'USER.md' })
send('agent_file_read', { filename: 'AGENT.md' })
send('agent_file_read', { filename: 'SOUL.md' })
}
}, [showAdvanced, isConnected, send])

Expand Down Expand Up @@ -281,6 +313,23 @@ export function GeneralSettings() {
})
}

const handleSaveSoulMd = () => {
setIsSavingSoulMd(true)
send('agent_file_write', { filename: 'SOUL.md', content: soulMdContent })
}

const handleRestoreSoulMd = () => {
confirm({
title: 'Restore SOUL.md',
message: 'Are you sure you want to restore SOUL.md to its default template? This will overwrite your current personality customizations.',
confirmText: 'Restore',
variant: 'danger',
}, () => {
setIsRestoringSoulMd(true)
send('agent_file_restore', { filename: 'SOUL.md' })
})
}

return (
<div className={styles.settingsSection}>
<div className={styles.sectionHeader}>
Expand Down Expand Up @@ -441,17 +490,82 @@ export function GeneralSettings() {
</div>
</div>

{/* SOUL.md Editor */}
<div className={styles.fileEditorCard}>
<div className={styles.fileEditorHeader}>
<div className={styles.fileEditorTitle}>
<h4>SOUL.md</h4>
<Badge variant="success">Personality</Badge>
</div>
<p className={styles.fileEditorDescription}>
This file defines the agent's personality, tone, and behavioral traits. It is injected
directly into the system prompt and shapes how the agent communicates. Edit this to give
your agent a unique character.
</p>
</div>
<div className={styles.fileEditorContent}>
{isLoadingSoulMd ? (
<div className={styles.fileLoading}>
<Loader2 size={20} className={styles.spinning} />
<span>Loading SOUL.md...</span>
</div>
) : (
<textarea
className={styles.fileTextarea}
value={soulMdContent}
onChange={(e) => setSoulMdContent(e.target.value)}
placeholder="Loading..."
spellCheck={false}
/>
)}
</div>
<div className={styles.fileEditorActions}>
<Button
variant="secondary"
size="sm"
onClick={handleRestoreSoulMd}
disabled={isRestoringSoulMd || isLoadingSoulMd}
icon={isRestoringSoulMd ? <Loader2 size={14} className={styles.spinning} /> : <RotateCcw size={14} />}
>
{isRestoringSoulMd ? 'Restoring...' : 'Restore Default'}
</Button>
<Button
variant="primary"
size="sm"
onClick={handleSaveSoulMd}
disabled={isSavingSoulMd || isLoadingSoulMd || !isSoulMdDirty}
>
{isSavingSoulMd ? 'Saving...' : 'Save'}
</Button>
{soulMdSaveStatus === 'success' && (
<span className={styles.statusSuccess}>
<Check size={14} /> Saved
</span>
)}
{soulMdSaveStatus === 'error' && (
<span className={styles.statusError}>
<X size={14} /> Save failed
</span>
)}
{isSoulMdDirty && soulMdSaveStatus === 'idle' && (
<span className={styles.statusWarning}>
Unsaved changes
</span>
)}
</div>
</div>

{/* AGENT.md Editor */}
<div className={styles.fileEditorCard}>
<div className={styles.fileEditorHeader}>
<div className={styles.fileEditorTitle}>
<h4>AGENT.md</h4>
<Badge variant="warning">Agent Identity</Badge>
<Badge variant="warning">Agent Manual</Badge>
</div>
<p className={styles.fileEditorDescription}>
This file defines the agent's identity, behavior guidelines, documentation standards,
and error handling philosophy. Changes here will affect how the agent approaches tasks,
handles errors, and formats its outputs. Edit with caution.
This file is the agent's instruction manual — it describes how the agent works, including
file handling, error handling, self-improvement protocols, and task execution guidelines.
The agent reads this on demand when it needs to understand its own mechanisms. Edit with caution.
</p>
</div>
<div className={styles.fileEditorContent}>
Expand Down
6 changes: 3 additions & 3 deletions app/ui_layer/settings/general_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def read_agent_file(filename: str) -> Dict[str, Any]:
Dict with 'success', 'content' or 'error' fields
"""
# Validate filename to prevent directory traversal
allowed_files = {"USER.md", "AGENT.md", "MEMORY.md", "PROACTIVE.md"}
allowed_files = {"USER.md", "AGENT.md", "SOUL.md", "MEMORY.md", "PROACTIVE.md"}
if filename not in allowed_files:
return {
"success": False,
Expand Down Expand Up @@ -69,7 +69,7 @@ def write_agent_file(filename: str, content: str) -> Dict[str, Any]:
Dict with 'success' and optional 'error' fields
"""
# Validate filename to prevent directory traversal
allowed_files = {"USER.md", "AGENT.md"}
allowed_files = {"USER.md", "AGENT.md", "SOUL.md"}
if filename not in allowed_files:
return {
"success": False,
Expand Down Expand Up @@ -101,7 +101,7 @@ def restore_agent_file(filename: str) -> Dict[str, Any]:
Dict with 'success', 'content' or 'error' fields
"""
# Validate filename
allowed_files = {"USER.md", "AGENT.md", "PROACTIVE.md"}
allowed_files = {"USER.md", "AGENT.md", "SOUL.md", "PROACTIVE.md"}
if filename not in allowed_files:
return {
"success": False,
Expand Down