Persistent Instructions (NextGen Plugin)
Persistent Instructions (NextGen Plugin)
⚠️ Deprecated in favour of
MemoryPluginNextGen. This plugin keeps working unchanged for existing integrations — no breaking change — but new code should prefer the memory plugin, which replaces dumb KV with append-only facts + LLM-synthesised profiles that evolve from observations.
PersistentInstructionsPluginNextGen is a context plugin that stores agent-level custom instructions on disk as individually keyed entries. Unlike InContextMemory (volatile key-value pairs), persistent instructions survive process restarts and are automatically loaded when the agent starts.
Key Difference from InContextMemory
| Feature | InContextMemory | Persistent Instructions |
|---|---|---|
| Storage | In-memory (volatile) | Disk (persistent JSON) |
| Survives restarts | No | Yes |
| Best for | Session state, counters, flags | Agent personality, learned rules |
| LLM can modify | Yes (store_set("whiteboard", ...)) | Yes (store_set/store_delete("instructions", ...)) |
| Auto-loaded | Via session restore | Always on agent start |
| Default capacity | 20 entries, 40,000 tokens | 50 entries, 50,000 chars total |
Quick Setup
import { Agent } from 'oneringai';
const agent = Agent.create({
connector: 'openai',
model: 'gpt-4.1',
context: {
model: 'gpt-4.1',
agentId: 'my-assistant', // Used for storage path
features: {
persistentInstructions: true, // Enables PersistentInstructionsPluginNextGen
},
},
});
// Plugin is accessible via ctx.getPlugin('persistent_instructions')
// Instructions are automatically loaded from disk on first context prepare
Manual Setup
For more control, you can set up the plugin manually:
import { AgentContextNextGen, PersistentInstructionsPluginNextGen } from 'oneringai';
const ctx = AgentContextNextGen.create({ model: 'gpt-4.1' });
// Create and configure plugin
const plugin = new PersistentInstructionsPluginNextGen({
agentId: 'my-assistant',
maxTotalLength: 100000, // Characters across all entries, default is 50000
maxEntries: 50, // Maximum number of keyed entries, default is 50
});
// Register with context
ctx.registerPlugin(plugin);
// Set instructions programmatically (keyed entries)
await plugin.set('personality', 'Always respond in a friendly tone.');
await plugin.set('formatting', 'Prefer bullet points for lists.');
Configuration Options
interface PersistentInstructionsConfig {
/** Agent ID - used to determine storage path (required) */
agentId: string;
/** Custom storage implementation (default: FilePersistentInstructionsStorage) */
storage?: IPersistentInstructionsStorage;
/** Maximum total content length across all entries in characters (default: 50000) */
maxTotalLength?: number;
/** Maximum number of entries (default: 50) */
maxEntries?: number;
}
Storage Path
Instructions are stored at:
- Unix/macOS:
~/.oneringai/agents/<agentId>/custom_instructions.json - Windows:
%APPDATA%/oneringai/agents/<agentId>/custom_instructions.json
The agent ID is sanitized to be filesystem-safe (lowercase, special chars replaced with underscores).
Available Tools
The LLM manages persistent instructions through the unified store_* tools with store="instructions":
store_set("instructions", ...)
Add or update a single instruction by key:
// Tool call from LLM
{
"name": "store_set",
"arguments": {
"store": "instructions",
"key": "personality",
"data": {
"content": "Always be friendly and helpful. Use clear, simple language."
}
}
}
// Returns: { "success": true, "message": "Instruction 'personality' added", "key": "personality", "contentLength": 57 }
store_delete("instructions", ...)
Remove a single instruction by key:
// Tool call from LLM
{
"name": "store_delete",
"arguments": {
"store": "instructions",
"key": "personality"
}
}
// Returns: { "deleted": true, "key": "personality" }
store_list("instructions")
List all instructions with their keys and content:
// Tool call from LLM
{
"name": "store_list",
"arguments": {
"store": "instructions"
}
}
// Returns: {
// "total": 2,
// "entries": [
// { "key": "personality", "contentLength": 57, "createdAt": ..., "updatedAt": ... },
// { "key": "formatting", "contentLength": 35, "createdAt": ..., "updatedAt": ... }
// ]
// }
store_action("instructions", "clear", ...)
Remove all instructions (requires confirmation):
// Tool call from LLM
{
"name": "store_action",
"arguments": {
"store": "instructions",
"action": "clear",
"params": { "confirm": true }
}
}
// Returns: { "success": true, "action": "clear", "message": "All custom instructions cleared" }
Direct API Access
The plugin provides a programmatic API for direct manipulation:
const plugin = ctx.getPlugin<PersistentInstructionsPluginNextGen>('persistent_instructions')!;
// Add/update entry by key
await plugin.set('personality', 'Always be friendly and helpful.');
await plugin.set('formatting', 'Use bullet points for lists.');
// Get single entry by key
const entry = await plugin.get('personality'); // InstructionEntry | null
// entry = { id: 'personality', content: '...', createdAt: ..., updatedAt: ... }
// Get all entries (sorted by createdAt)
const all = await plugin.get(); // InstructionEntry[] | null
// List metadata for all entries
const list = await plugin.list();
// [{ key: 'personality', contentLength: 35, createdAt: ..., updatedAt: ... }, ...]
// Remove a single entry
await plugin.remove('formatting'); // true if found, false if not
// Clear all
await plugin.clear();
Context Output Format
When the LLM context is prepared, persistent instruction entries are rendered as markdown sections:
### personality
Always be friendly and helpful. Use clear, simple language.
### formatting
- Use bullet points for lists
- Keep responses concise
### user_preferences
The user prefers dark mode and verbose explanations.
Session Persistence
PersistentInstructionsPluginNextGen supports state serialization. The state format includes all entries:
// State includes all entries
const state = plugin.getState();
// state = { entries: [...], agentId: "my-assistant", version: 2 }
// Restore state (useful for in-memory state sync)
plugin.restoreState(state);
// Also handles legacy format: { content: string | null, agentId: string }
Use Cases
Ideal for:
- Agent personality/behavior - Tone, style, expertise areas
- User preferences - Formatting, verbosity, topics of interest
- Learned rules - Patterns discovered during conversation
- Tool usage guidelines - When to use specific tools
- Custom instructions - Domain-specific knowledge
Example: Building a Learning Assistant
const agent = Agent.create({
connector: 'openai',
model: 'gpt-4.1',
instructions: `You are a learning assistant. When the user expresses preferences or
gives feedback about your responses, use store_set("instructions", key, { content }) to remember them for
future sessions. Use descriptive keys like "user_preferences", "response_style", etc.
Review your instructions with store_list("instructions") at the start of each conversation.`,
context: {
model: 'gpt-4.1',
agentId: 'learning-assistant',
features: { persistentInstructions: true },
},
});
// User: "I prefer when you explain things with analogies"
// Agent calls: store_set("instructions", "response_style", { content: "Explain concepts using analogies when possible" })
// Next session, agent sees this in context automatically
Best Practices
1. Use Descriptive Keys
// GOOD: Descriptive, categorical keys
await plugin.set('personality', 'Be friendly and approachable');
await plugin.set('formatting_rules', 'Use bullet points for lists');
await plugin.set('domain_knowledge', 'User works in fintech');
// AVOID: Generic or numbered keys
await plugin.set('rule1', '...'); // Not descriptive
await plugin.set('misc', '...'); // Too vague
2. One Concern Per Entry
// GOOD: Each entry covers one topic
await plugin.set('tone', 'Use formal language');
await plugin.set('code_style', 'Use TypeScript, follow existing patterns');
await plugin.set('response_length', 'Keep responses concise, 2-3 paragraphs max');
// AVOID: Mixing concerns in one entry
await plugin.set('rules', 'Be formal. Use TypeScript. Keep it short.');
3. Combine with InContextMemory
Use both systems for their strengths:
// Persistent instructions for long-term knowledge
// - Agent personality
// - User preferences
// - Learned rules
// InContextMemory for session-specific state
// - Current task progress
// - Temporary flags
// - Running totals
4. Set Reasonable Limits
// For simple agents
const agent = Agent.create({
context: {
features: { persistentInstructions: true },
plugins: { persistentInstructions: { maxTotalLength: 10000, maxEntries: 20 } },
},
});
// For complex agents with lots of learned rules
const agentVariant2 = Agent.create({
context: {
features: { persistentInstructions: true },
plugins: { persistentInstructions: { maxTotalLength: 100000, maxEntries: 100 } },
},
});
Upgrade Guide (from single-string to KVP)
If upgrading from the previous single-string persistent instructions:
- File storage: Auto-migrated. Legacy
custom_instructions.mdfiles are read as a singlelegacy_instructionsentry and converted tocustom_instructions.jsonon next save. No action needed. - Custom storage backends: Update
load()to returnInstructionEntry[] | nullandsave()to acceptInstructionEntry[]instead ofstring. - Tool API:
instructions_appendis removed — usestore_set("instructions", key, { content })to add new entries.instructions_getis removed — usestore_list("instructions")to see all entries. - Programmatic API:
plugin.set(content)→plugin.set(key, content).plugin.append(section)→plugin.set(newKey, section).plugin.get()now returnsInstructionEntry[] | null(or a singleInstructionEntrywhen called with a key). - Session state: Existing saved sessions with old format (
{ content: string | null }) are auto-migrated onrestoreState().