pacs-core
v0.1.0
Published
PACS — Personal AI Control System. Encrypted memory, routing, and learn mode for AI assistants.
Maintainers
Readme
PACS Core — Personal AI Control System
Phase 1 Foundation | MIT License | Encrypted Storage Layer
PACS replaces the traditional Agent/Skill MD-file system with encrypted, AI-controlled memory and routing.
What is PACS?
PACS (Personal AI Control System) gives AI assistants persistent, encrypted memory and user-controlled routing — without relying on plaintext markdown files.
Key Features
- AES-256-GCM Encryption — All sensitive data is encrypted at rest
- 3-Layer Architecture — Commands (plaintext) → AI Logic (encrypted) → System Kernel (hardcoded)
- Learn Mode — AI asks before storing facts; user controls auto vs. manual learning
- Trigger Commands — Plaintext, human-editable commands in any language
- Standalone or Integrated — Works with OpenClaw or as a standalone npm package
Installation
npm install pacs-coreOr for development:
git clone https://github.com/yourusername/pacs-core.git
cd pacs-core
npm installQuick Start
1. Set your encryption key
export PACS_ENCRYPTION_KEY="$(openssl rand -hex 32)"The key lives in the environment only — never in files.
2. Bootstrap your PACS directory
npm run bootstrapThis creates ~/.openclaw/pacs/ with sample encrypted files.
3. Use in your code
const pacs = require('pacs-core');
// Parse a trigger command
const parsed = pacs.triggers.parse('MERKE: Ich spreche Deutsch');
if (parsed) {
console.log(`Trigger: ${parsed.trigger.id}`);
console.log(`Payload: ${parsed.payload}`);
}
// Add a fact to memory
const memory = pacs.memory.addFact('I prefer German when writing');
// Search memory
const results = pacs.memory.search('German');
// Show help
console.log(pacs.triggers.getHelp());Architecture
┌─────────────────────────────────────────────┐
│ Layer 1: COMMANDS (plaintext) │
│ Human-editable, multi-language triggers │
│ MERKE:, ROUTING:, REGEL:, SUCHE:, etc. │
├─────────────────────────────────────────────┤
│ Layer 2: PERSONAL AI LOGIC (encrypted) │
│ AES-256-GCM — AI can reject conflicts │
│ Memory, routing hints, core rules │
├─────────────────────────────────────────────┤
│ Layer 3: SYSTEM KERNEL (hardcoded) │
│ AI's own immutable rules — never changed │
└─────────────────────────────────────────────┘Trigger Commands
| Command | Aliases | Description |
|---------|---------|-------------|
| MERKE: | LEARN: | Store a fact in encrypted memory |
| PRIVAT: | PRIVATE: | Mark info as private (excluded from exports) |
| ROUTING: | ROUTE: | Add routing hint for task delegation |
| REGEL: | RULE: | Add a personal AI behavior rule |
| BEFEHLE: | COMMANDS: | Show available commands |
| WAS WEISST DU? | WHAT DO YOU KNOW? | Query what AI remembers |
| SUCHE: | SEARCH: | Search encrypted memory |
| VERGISS: | FORGET: | Delete something from memory |
| EXPORTIEREN: | EXPORT: | Export memory (excludes private) |
Learn Modes
Control how PACS handles learning:
AI: lern automatisch → Auto-learn mode (AI offers to remember)
AI: nur auf Befehl → Manual mode only (MERKE: required)Security
- Encryption: AES-256-GCM (Node.js built-in crypto)
- Key storage:
PACS_ENCRYPTION_KEYenv variable only - Private facts: Excluded from all exports
- AI override: AI can reject facts that contradict its logic
- No external dependencies beyond Node.js standard library
API Reference
pacs.crypto
pacs.crypto.encrypt(plaintext) // → "iv:authTag:ciphertext"
pacs.crypto.decrypt(encryptedData) // → plaintext
pacs.crypto.generateKey() // → 64-char hex stringpacs.memory
pacs.memory.read() // → { facts: [], private: [], learnedAt: {} }
pacs.memory.addFact(fact, isPrivate) // → updated memory
pacs.memory.removeFact(fact) // → updated memory
pacs.memory.search(query) // → string[]pacs.triggers
pacs.triggers.parse(message) // → { trigger, payload } or null
pacs.triggers.find('MERKE') // → trigger object or null
pacs.triggers.contains(message) // → boolean
pacs.triggers.detectLearnMode(msg) // → 'auto' | 'onDemand' | null
pacs.triggers.getHelp() // → formatted help stringpacs.routing
pacs.routing.read() // → { hints: [], agents: [] }
pacs.routing.addHint(hint) // → updated routingpacs.rules
pacs.rules.read() // → { rules: [] }
pacs.rules.addRule(rule) // → updated rulesFile Structure
pacs-core/
├── package.json
├── README.md
├── LICENSE
└── src/
├── index.js # Main entry point
├── crypto.js # AES-256-GCM encryption
├── memory-store.js # Encrypted storage layer
├── trigger-parser.js # Command parsing
├── triggers.json # Trigger definitions (plaintext)
├── bootstrap.js # Setup script
└── test.js # Test suite
~/.openclaw/pacs/ # Created by bootstrap
├── memory.enc # Encrypted user facts
├── routing.enc # Encrypted routing hints
├── core-rules.enc # Encrypted AI rules
└── triggers/
└── triggers.json # Copy of trigger definitionsMulti-Agent System
PACS Phase 2 adds an inter-agent communication bus and agent registry, enabling multi-agent workflows.
Agent Registry
Manages agent definitions with encrypted persistence.
const { AgentRegistry } = require('pacs-core');
// Register an agent
const agent = AgentRegistry.registerAgent('finance-agent', {
domain: 'finance',
capabilities: ['analysis', 'planning'],
description: 'Handles all financial analysis tasks'
});
// Get agent by name
const found = AgentRegistry.getAgent('finance-agent');
// List all agents
const all = AgentRegistry.listAgents();
const active = AgentRegistry.listAgents({ status: 'active' });
// Remove an agent
AgentRegistry.removeAgent('finance-agent');Inter-Agent Bus
Message passing between agents with encrypted message history.
const { InterAgentBus } = require('pacs-core');
// Send a message to a specific agent
InterAgentBus.emit('orchestrator', 'finance-agent', 'Analyze Q1 expenses');
// Broadcast to all agents
InterAgentBus.broadcast('orchestrator', 'System update: all agents report status');
// Request-response pattern (async)
const response = await InterAgentBus.request('orchestrator', 'finance-agent', {
task: 'calculate_budget',
params: { quarter: 'Q2' }
});
// Respond to a request
InterAgentBus.respond(requestId, 'finance-agent', { result: 45000 });
// Get messages for an agent
const msgs = InterAgentBus.getMessages('finance-agent', { limit: 50 });Agent Creator
Creates agents from natural language text or markdown definitions.
const { AgentCreator } = require('pacs-core');
// From natural language (German or English)
const agent = AgentCreator.createFromText(
'Erstelle einen Agenten für Finanzen, der Analysen und Planung kann'
);
// From markdown definition
const agent2 = AgentCreator.createFromMD(`
---
name: marketing-agent
domain: marketing
status: active
---
This agent handles marketing campaigns, social media, and CRM.
`);Agent Object Schema
{
id: 'm1abc2def3', // Unique ID
name: 'finance-agent', // Unique name
domain: 'finance', // Domain category
capabilities: [], // List of capabilities
status: 'active', // 'active' | 'sleep'
lastActive: 'ISO8601', // Last activity timestamp
createdAt: 'ISO8601', // Creation timestamp
description: '', // Human-readable description
metadata: {} // Custom metadata
}Message Schema
{
id: 'msg_id',
from: 'orchestrator',
to: 'finance-agent', // or '*' for broadcast
type: 'message', // 'message' | 'request' | 'response'
payload: '...', // string or JSON string
timestamp: 'ISO8601',
inReplyTo: 'msg_id' // only for response type
}Storage
Agent definitions are stored in ~/.openclaw/pacs/agents.enc and messages in messages.enc, both AES-256-GCM encrypted.
Routing Engine
Routes incoming user tasks to the correct agent(s) using capability matching and scoring.
const { RoutingEngine } = require('pacs-core');
// Route to best matching agents
const result = RoutingEngine.route('Organisiere meine Finanzen');
// → { task: 'Organisiere meine Finanzen', plan: [{ agent: 'finance', mode: 'parallel', score: 0.85 }], confidence: 0.85 }
// Get agents for parallel execution
const parallel = RoutingEngine.routeParallel('Analyze Q1 AND plan marketing campaign');
// Get agents for sequential execution
const sequential = RoutingEngine.routeSequential('Plan my week and send emails');
// Find who handles a topic
const handlers = RoutingEngine.whoHandles('Steuererklärung');
// → ['finance']Special queries:
"alle Agenten"/"alle agenten"— returns all registered agents"wer ist für X zuständig?"— returns the best agent for topic X
Scoring: 0.0–1.0 based on domain keywords, capabilities, description match, and stored routing hints.
Learning Loop
Handles SAFE learning mode with "Soll ich mir das merken?" confirmation flow.
const { LearningLoop } = require('pacs-core');
// Check if a message should be remembered
const result = LearningLoop.shouldRemember('Ich spreche lieber Deutsch');
// → { should: true, reason: 'safe-detect', fact: 'Ich spreche lieber Deutsch' }
// Get the confirmation prompt in user's language
const prompt = LearningLoop.promptConfirmation('Ich spreche lieber Deutsch');
// → 'Soll ich mir das merken? "Ich spreche lieber Deutsch"'
// After user confirms, store the fact
LearningLoop.storeConfirmed('user123', 'Ich sprebe lieber Deutsch');
// If user rejects, mark as ignored
LearningLoop.rejectLearning('user123', 'some-fact');Supported learn modes:
safe— AI asks before storing ("Soll ich mir das merken?") defaultexplicit— Only store when explicitly commanded viaMERKE:/LEARN:auto— Auto-store and confirm after the fact
Learn Mode Manager
const { LearnMode } = require('pacs-core');
LearnMode.getMode(); // → 'safe'
LearnMode.setMode('auto'); // → 'auto'
LearnMode.getModeDescription(); // → human-readable descriptionStored in ~/.openclaw/pacs/settings.enc.
Phase 2 (Planned)
- [x] Agent routing engine with match scoring
- [x] Learning loop with confirmation prompts
- [x] OpenClaw integration bridge
- [x] CLI tool for management
- Export/import functionality
OpenClaw Bridge
The OpenClaw Bridge integrates PACS into the OpenClaw agent framework.
const { OpenClawBridge } = require('pacs-core');
// Initialize with OpenClaw config
const result = OpenClawBridge.init({
pacs: {
enabled: true,
mode: 'integrated' // 'integrated' | 'standalone'
}
}, openClawAgentApi); // optional OpenClaw agent API for fallbackModes
| Mode | Description |
|------|-------------|
| integrated | PACS and OpenClaw agents coexist. PACS processes messages first; falls back to OpenClaw agents if no PACS handler matches. |
| standalone | PACS runs independently. OpenClaw is used only as the UI layer. |
API
// Process a user message through PACS
const result = await OpenClawBridge.query(userMessage);
// → { response, routedTo, fallback }
// Check if a message is a PACS command
const isPACS = OpenClawBridge.isPACSQuery('MERKE: Ich spreche Deutsch');
// → true
// Check bridge status
const status = OpenClawBridge.status();
// → { initialized, mode, enabled, openClawConnected }How Routing Works
isPACSQuery()checks for PACS keywords (MERKE, SUCHE, LERNE, AGENT, ROUTING, REGELN, MEMORY) or trigger patterns- If matched: PACS handles it → routes to memory, agent registry, or learn mode
- If not matched in
integratedmode → delegates to OpenClaw agents viafallbackToOpenClawAgents() - If not matched in
standalonemode → returns "no handler found" response
CLI Reference
PACS includes a full command-line interface for managing agents, memory, and settings.
Installation
npm install # install dependencies
npm link # make `pacs` command available globallyOr run directly:
node src/cli.js <command>Commands
pacs --help
Show all available commands.
pacs init
Initialize the PACS storage directory (~/.openclaw/pacs/).
pacs initpacs status
Show PACS system status.
pacs status
# === PACS Status ===
# Bridge initialisiert : ja/nein
# Bridge Mode : integrated/standalone
# Agenten registriert : 3
# Memory Facts : 12pacs agents list
List all registered agents.
pacs agents listpacs agents add <name>
Register a new agent.
pacs agents add finbot --text "Finance and invoicing agent"
pacs agents add devbot --md ./agent-definition.mdpacs agents remove <name>
Remove an agent by name.
pacs agents remove finbotpacs agents get <name>
Show full details for a specific agent.
pacs agents get finbotpacs memory list
List all stored memory facts.
pacs memory listpacs memory search <query>
Search memory facts.
pacs memory search "preferences"pacs memory add "<fact>"
Add a new memory fact.
pacs memory add "Lech prefers German in the morning"pacs memory forget "<fact>"
Remove a memory fact by content match.
pacs memory forget "Lech prefers German in the morning"pacs learn-mode get
Show the current learn mode.
pacs learn-mode get
# Learn-Mode: safepacs learn-mode set <mode>
Set the learn mode.
pacs learn-mode set explicit
# Valid modes: safe | explicit | autopacs messages <agent>
Show message history for an agent.
pacs messages finbotEnvironment Variables
| Variable | Required | Description |
|----------|----------|-------------|
| PACS_ENCRYPTION_KEY | Yes | 256-bit encryption key (required for all encrypted operations) |
Generate a key:
openssl rand -hex 32Set permanently (Linux/macOS):
echo 'export PACS_ENCRYPTION_KEY=$(openssl rand -hex 32)' >> ~/.bashrcSet permanently (Windows PowerShell):
[System.Environment]::SetEnvironmentVariable("PACS_ENCRYPTION_KEY", $(openssl rand -hex 32), "User")File Structure
pacs-core/
├── bin/
│ └── pacs-cli.js # CLI executable entry point
├── src/
│ ├── index.js # Main entry point + all exports
│ ├── cli.js # CLI implementation
│ ├── openclaw-bridge.cjs # OpenClaw integration
│ ├── agent-registry.js # Agent management
│ ├── memory-store.js # Encrypted storage
│ ├── crypto.js # AES-256-GCM
│ ├── trigger-parser.js # Command parsing
│ ├── routing-engine.js # Task routing
│ ├── learn-mode.js # Learn mode settings
│ ├── learning-loop.js # Learning confirmation flow
│ ├── inter-agent-bus.js # Agent messaging
│ └── agent-creator.js # Agent creation
└── package.json # "bin": { "pacs": "./bin/pacs-cli.js" }
~/.openclaw/pacs/ # Created at runtime
├── memory.enc # Encrypted user facts
├── routing.enc # Encrypted routing hints
├── core-rules.enc # Encrypted AI rules
├── agents.enc # Encrypted agent registry
├── settings.enc # Encrypted settings (learn mode)
└── messages.enc # Encrypted inter-agent messagesLicense
MIT — see LICENSE
Contributing
Contributions welcome. Open an issue first for major changes.
