Initial commit: OpenCode Working Memory Plugin v1.0.0

- Four-tier memory architecture (Core, Working, Pruning, Pressure)
- Phase 1: Core Memory blocks (goal/progress/context)
- Phase 2: Smart Pruning with adaptive thresholds
- Phase 3: Working Memory with slots + pool decay
- Phase 4: Pressure monitoring with interventions
- Phase 4.5: Storage governance (session cleanup + cache sweep)
- Complete documentation (README, AGENTS, installation, architecture, configuration)
- MIT licensed
This commit is contained in:
Ralph Chang
2026-02-18 09:49:09 +08:00
commit 32d5ddfb50
10 changed files with 3668 additions and 0 deletions
+376
View File
@@ -0,0 +1,376 @@
# Architecture Documentation
## Overview
The Working Memory Plugin implements a **four-tier memory architecture** designed to maximize context efficiency for AI agents in OpenCode sessions.
```
┌─────────────────────────────────────────────────────────────┐
│ TIER 1: CORE MEMORY │
│ Persistent blocks: goal (1000) | progress (2000) | context (1500) │
│ Survives compaction, always visible in system prompt │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ TIER 2: WORKING MEMORY │
│ Session-scoped slots + memory pool │
│ Slots: error(3) | decision(5) | todo(3) | dependency(3) │
│ Pool: Exponential decay (γ=0.85) + mention tracking │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ TIER 3: SMART PRUNING │
│ Filters tool outputs before adding to conversation │
│ Removes: file lists, verbose logs, repetitive content │
│ Modes: normal → aggressive → hyper-aggressive │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ TIER 4: PRESSURE MONITORING │
│ Tracks context usage: safe → moderate → high → critical │
│ Thresholds: 70% | 85% | 95% │
│ Intervention: Sends promptAsync() with full visible prompt │
└─────────────────────────────────────────────────────────────┘
```
## Phase 1: Core Memory Foundation
### Purpose
Provide persistent memory blocks that survive conversation compaction and are always injected into the system prompt.
### Storage
- **Location**: `.opencode/memory-core/<sessionID>.json`
- **Schema**:
```typescript
{
sessionID: string;
blocks: {
goal: { content: string; chars: number; maxChars: 1000; updatedAt: string };
progress: { content: string; chars: number; maxChars: 2000; updatedAt: string };
context: { content: string; chars: number; maxChars: 1500; updatedAt: string };
};
updatedAt: string;
}
```
### Character Limits
- **goal**: 1000 chars (ONE specific task)
- **progress**: 2000 chars (done/in-progress/blocked checklist)
- **context**: 1500 chars (current working files + key patterns)
### Operations
- **replace**: Completely replace block content
- **append**: Add content to end (auto-adds newline)
### Tools
- `core_memory_update`: Update or append to blocks
- `core_memory_read`: Read current state of all blocks
### System Prompt Injection
Blocks are injected into every agent message as:
```
<core_memory>
<goal chars="87/1000">...</goal>
<progress chars="560/2000">...</progress>
<context chars="479/1500">...</context>
</core_memory>
```
## Phase 2: Smart Pruning
### Purpose
Reduce context bloat by filtering tool outputs before they enter the conversation history.
### Pruning Modes
#### Normal Mode (Pressure < 85%)
- Remove file/directory listings > 50 lines
- Truncate verbose tool outputs
- Keep first/last 30 lines of long outputs
- Preserve error messages and key information
#### Aggressive Mode (85% ≤ Pressure < 95%)
- Threshold drops to 30 lines
- More aggressive truncation (first/last 20 lines)
- Filter repetitive content
#### Hyper-Aggressive Mode (Pressure ≥ 95%)
- Threshold drops to 15 lines
- Keep only first/last 10 lines
- Maximum compression
### Pruning Heuristics
1. **File Listings**: Detect `ls`, `find`, `glob` outputs
2. **Directory Trees**: Detect tree-like structures with `/`
3. **Log Files**: Detect timestamp patterns, stack traces
4. **Repetitive Content**: Detect similar consecutive lines
5. **Synthetic Content**: Preserve `synthetic: true` markers
### Implementation
Pruning happens in `tool.execute.after` hook before tool output enters conversation.
## Phase 3: Working Memory
### Purpose
Provide session-scoped memory with structured slots and a general-purpose pool with intelligent decay.
### Storage
- **Location**: `.opencode/memory-working/<sessionID>.json`
- **Schema**:
```typescript
{
sessionID: string;
slots: {
error: Array<WorkingMemoryItem>; // Max 3
decision: Array<WorkingMemoryItem>; // Max 5
todo: Array<WorkingMemoryItem>; // Max 3
dependency: Array<WorkingMemoryItem>; // Max 3
};
pool: Array<WorkingMemoryItem>;
eventCounter: number;
updatedAt: string;
}
```
### Slot Types
| Slot | Max Items | Purpose |
|------|-----------|---------|
| **error** | 3 | Recent errors that need fixing |
| **decision** | 5 | Important decisions made |
| **todo** | 3 | Current task checklist |
| **dependency** | 3 | File/package dependencies |
### Memory Pool
General-purpose storage with **exponential decay**:
```typescript
score = exp(-γ * age) + mentionCount
```
Where:
- `γ = 0.85` (decay rate, 15% per event)
- `age = eventCounter - item.eventNumber`
- `mentionCount`: Number of times item mentioned in conversation
Items with `score < 0.01` are pruned.
### Auto-Extraction
Working memory items are **automatically extracted** from:
- Tool outputs (file paths, errors, dependencies)
- User messages (decisions, todos)
- Assistant responses (key information)
### Manual Management
Tools:
- `working_memory_add`: Manually add item
- `working_memory_clear`: Clear all items
- `working_memory_clear_slot`: Clear specific slot (e.g., after fixing all errors)
- `working_memory_remove`: Remove specific item by content match
### System Prompt Injection
```
<working_memory>
Recent session context (auto-managed, sorted by relevance):
⚠️ Errors:
- TypeError at line 42 in utils.ts
- Missing import in index.ts
📁 Key Files:
- src/components/Button.tsx
- src/utils/helpers.ts
(15 items shown, updated: 9:46:47 AM)
</working_memory>
```
## Phase 4: Pressure Monitoring
### Purpose
Track conversation context usage and trigger interventions when approaching limits.
### Pressure Calculation
```typescript
pressure = (visiblePromptChars / estimatedContextLimit) * 100
```
Where:
- `visiblePromptChars`: Total characters in system prompt + tool outputs
- `estimatedContextLimit`: ~180,000 chars (conservative estimate)
### Pressure Levels
| Level | Threshold | Behavior |
|-------|-----------|----------|
| **safe** | < 70% | Normal operation |
| **moderate** | 70-84% | Warning in system prompt |
| **high** | 85-94% | Aggressive pruning + warning |
| **critical** | ≥ 95% | Hyper-aggressive pruning + intervention |
### Pressure Storage
- **Location**: `.opencode/memory-working/<sessionID>_pressure.json`
- **Schema**:
```typescript
{
sessionID: string;
level: "safe" | "moderate" | "high" | "critical";
percentage: number;
visiblePromptChars: number;
estimatedLimit: 180000;
lastChecked: string;
interventionsSent: number;
}
```
### Intervention Mechanism
When pressure reaches **critical** (≥95%):
1. Plugin sends `promptAsync()` message to agent
2. Message includes full visible prompt for review
3. Agent can compress core memory, clear working memory, or continue
4. Intervention tracked in `interventionsSent` counter
### System Prompt Injection
```
[Memory Pressure: 87% (high) - 156,600/180,000 chars]
⚠️ High memory pressure detected. Consider:
- Compressing core_memory blocks (use core_memory_update)
- Clearing resolved errors (use working_memory_clear_slot)
- Removing old pool items (auto-pruned at score < 0.01)
```
## Phase 4.5: Storage Governance
### Purpose
Prevent `.opencode/` directory bloat from accumulating tool output caches and orphaned memory files.
### Layer 1: Session Deletion Cleanup
**Trigger**: `experimental.session.deleted` hook
**Actions**:
1. Remove `.opencode/memory-core/<sessionID>.json`
2. Remove `.opencode/memory-working/<sessionID>.json`
3. Remove `.opencode/memory-working/<sessionID>_pressure.json`
4. Remove `.opencode/memory-working/<sessionID>_compaction.json`
### Layer 2: Tool Output Cache Sweep
**Trigger**: Every 500 events (`eventCounter % 500 === 0`)
**Target**: `.opencode/cache/tool-outputs/` directory
**Policy**:
- Keep most recent **300 files** (sorted by mtime)
- Delete files older than **7 days** (TTL policy)
**Logging**: Write sweep results to `.opencode/memory-working/<sessionID>_sweep.json`
```typescript
{
sessionID: string;
timestamp: string;
eventCounter: number;
results: {
filesScanned: number;
filesDeleted: number;
bytesReclaimed: number;
errors: Array<string>;
};
}
```
## Performance Considerations
### Memory Budgets
- **Core Memory**: 4,500 chars (injected every message)
- **Working Memory**: ~1,600 chars (injected every message)
- **Total Overhead**: ~6,100 chars per message
### Compaction Behavior
When OpenCode compacts conversation (clears old messages):
- Core memory: **Preserved** (persistent across compactions)
- Working memory: **Preserved** (session-scoped, cleared on session end)
- Pressure state: **Preserved** (tracks across compaction)
- Compaction log: Saved to `<sessionID>_compaction.json`
### Storage Footprint
- Each session: 4 JSON files (~5-20 KB total)
- Tool output cache: Max 300 files (~10-50 MB depending on outputs)
- Sweep every 500 events keeps storage bounded
## Extension Points
### Custom Slot Types
To add new slot types:
1. Update `SlotType` union in types
2. Add to `SLOT_CONFIG` with max items
3. Update `formatWorkingMemoryForPrompt()` for display
4. Update extraction heuristics in `tool.execute.after`
### Custom Pruning Rules
To add pruning heuristics:
1. Update `shouldPrune()` with new detection logic
2. Add to `pruneToolOutput()` with filtering rules
3. Test with representative tool outputs
### Custom Pressure Thresholds
Adjust in constants:
```typescript
const PRESSURE_THRESHOLDS = {
moderate: 70,
high: 85,
critical: 95,
};
```
## Migration & Compatibility
### Old Format → New Format
Plugin automatically migrates from old format:
```typescript
// Old format (pre-Phase 3)
{ items: Array<Item> }
// New format (Phase 3+)
{ slots: Record<SlotType, Array<Item>>, pool: Array<Item> }
```
Migration happens on first load of old format files.
## File System Layout
```
.opencode/
├── memory-core/
│ └── <sessionID>.json # Core memory blocks
├── memory-working/
│ ├── <sessionID>.json # Working memory (slots + pool)
│ ├── <sessionID>_pressure.json # Pressure monitoring state
│ ├── <sessionID>_compaction.json # Compaction event log
│ └── <sessionID>_sweep.json # Storage sweep log
└── cache/
└── tool-outputs/
└── *.json # Tool output cache (auto-swept)
```
## Security Considerations
- All files written with `0644` permissions (owner read/write, group/others read)
- Directories created with `0755` permissions (owner rwx, group/others rx)
- No sensitive data should be stored in memory blocks (user responsibility)
- Session IDs are opaque identifiers, not derived from sensitive data
---
**Last Updated**: February 2026
**Implementation**: `index.ts` (1700+ lines)
+376
View File
@@ -0,0 +1,376 @@
# Configuration Guide
## Overview
The Working Memory Plugin works out-of-the-box with sensible defaults. Advanced users can customize behavior by modifying constants in `index.ts`.
## Core Memory Limits
```typescript
const CORE_MEMORY_LIMITS = {
goal: 1000, // ONE specific task (not project-wide goals)
progress: 2000, // Checklist format (✅ done, ⏳ in-progress, ❌ blocked)
context: 1500, // Current working files + key patterns
};
```
**Recommendations**:
- Keep **goal** focused on current task (clear when completed)
- Use **progress** for checklists (avoid line numbers, commit hashes, API signatures)
- Use **context** for files you're actively editing (avoid type definitions, function signatures)
## Working Memory Configuration
### Slot Limits
```typescript
const SLOT_CONFIG: Record<SlotType, number> = {
error: 3, // Recent errors needing fixes
decision: 5, // Important decisions made
todo: 3, // Current task checklist
dependency: 3, // File/package dependencies
};
```
**Tuning**:
- Increase slot limits if you need more items tracked
- Decrease for stricter memory budgets
- Total overhead: ~100-200 chars per item
### Memory Pool Decay
```typescript
const POOL_DECAY_GAMMA = 0.85; // Exponential decay rate (15% per event)
const POOL_MIN_SCORE = 0.01; // Items below this score are pruned
```
**Formula**: `score = exp(-γ * age) + mentionCount`
**Tuning**:
- Lower `γ` (e.g., 0.75) → faster decay, more aggressive pruning
- Higher `γ` (e.g., 0.90) → slower decay, items stay longer
- Lower `POOL_MIN_SCORE` (e.g., 0.005) → more items retained
### Pool Size Limits
```typescript
const POOL_MAX_ITEMS = 50; // Hard limit on pool size
```
**Tuning**:
- Increase for longer sessions with more context
- Decrease for stricter memory budgets
- Each item adds ~50-150 chars to system prompt
## Pressure Monitoring
### Thresholds
```typescript
const PRESSURE_THRESHOLDS = {
moderate: 70, // Warning appears in system prompt
high: 85, // Aggressive pruning activates
critical: 95, // Intervention sent to agent
};
```
**Tuning**:
- Increase thresholds for more relaxed monitoring
- Decrease for earlier warnings and interventions
### Context Limit Estimate
```typescript
const ESTIMATED_CONTEXT_LIMIT = 180000; // Conservative estimate (chars)
```
**Note**: OpenCode actual limit varies by model. Adjust based on your observations.
## Smart Pruning
### Line Thresholds
```typescript
// Normal mode (pressure < 85%)
const PRUNE_THRESHOLD_NORMAL = 50;
// Aggressive mode (85% ≤ pressure < 95%)
const PRUNE_THRESHOLD_AGGRESSIVE = 30;
// Hyper-aggressive mode (pressure ≥ 95%)
const PRUNE_THRESHOLD_HYPER = 15;
```
**Tuning**:
- Increase thresholds to keep more tool output
- Decrease for more aggressive pruning
### Keep Lines
```typescript
// Normal mode
const KEEP_LINES_NORMAL = 30; // Keep first/last 30 lines
// Aggressive mode
const KEEP_LINES_AGGRESSIVE = 20; // Keep first/last 20 lines
// Hyper-aggressive mode
const KEEP_LINES_HYPER = 10; // Keep first/last 10 lines
```
**Tuning**:
- Increase to preserve more context from tool outputs
- Decrease for stricter truncation
## Storage Governance
### Session Cleanup
Automatically triggered on `experimental.session.deleted` hook. No configuration needed.
### Tool Output Cache Sweep
```typescript
const SWEEP_INTERVAL = 500; // Trigger every N events
const SWEEP_MAX_FILES = 300; // Keep most recent N files
const SWEEP_TTL_DAYS = 7; // Delete files older than N days
```
**Tuning**:
- Increase `SWEEP_INTERVAL` for less frequent sweeps (lower overhead)
- Increase `SWEEP_MAX_FILES` to cache more tool outputs (more disk usage)
- Increase `SWEEP_TTL_DAYS` to keep older files longer
## Compaction Behavior
### Item Preservation
```typescript
const COMPACTION_KEEP_ITEMS = 10; // Preserve N most recent items on compaction
```
**Tuning**:
- Increase to preserve more working memory across compactions
- Decrease for stricter memory reset on compaction
## System Prompt Injection
### Core Memory Format
```typescript
// Injected as:
<core_memory>
<goal chars="87/1000">...</goal>
<progress chars="560/2000">...</progress>
<context chars="479/1500">...</context>
</core_memory>
```
**Customization**: Modify `formatCoreMemoryForPrompt()` in `index.ts` to change format.
### Working Memory Format
```typescript
// Injected as:
<working_memory>
Recent session context (auto-managed, sorted by relevance):
Errors:
- item content
📁 Key Files:
- file path
(N items shown, updated: HH:MM:SS AM)
</working_memory>
```
**Customization**: Modify `formatWorkingMemoryForPrompt()` in `index.ts` to change:
- Section emoji/icons
- Display format
- Item ordering
### Pressure Warning Format
```typescript
// Injected as:
[Memory Pressure: 87% (high) - 156,600/180,000 chars]
High memory pressure detected. Consider:
- Action item 1
- Action item 2
```
**Customization**: Modify `formatPressureWarning()` in `index.ts`.
## Auto-Extraction Heuristics
### File Path Detection
```typescript
// Detects:
- Absolute paths: /users/name/project/file.ts
- Relative paths: src/components/Button.tsx
- Dot paths: ./utils/helpers.ts
- Tilde paths: ~/project/file.ts
```
**Customization**: Modify regex in `extractFilePaths()`.
### Error Detection
```typescript
// Detects:
- "Error:", "ERROR:", "error:"
- Stack traces with "at " prefix
- TypeScript errors with "TS####:"
```
**Customization**: Modify `extractErrors()` heuristics.
### Decision Detection
```typescript
// Detects:
- "decided to...", "decision:", "chose to..."
- "using X instead of Y"
- "will use X approach"
```
**Customization**: Modify `extractDecisions()` heuristics.
## Environment Variables
Currently, the plugin does not support environment variables. All configuration is done via constants in `index.ts`.
**Future Enhancement**: Consider adding `.env` support for:
```
OPENCODE_WM_CORE_GOAL_LIMIT=1000
OPENCODE_WM_POOL_DECAY_GAMMA=0.85
OPENCODE_WM_SWEEP_INTERVAL=500
```
## Performance Tuning
### High-Frequency Sessions (500+ messages)
```typescript
// Aggressive pruning
const PRUNE_THRESHOLD_NORMAL = 30;
const PRUNE_THRESHOLD_AGGRESSIVE = 20;
// Faster decay
const POOL_DECAY_GAMMA = 0.75;
// More frequent sweeps
const SWEEP_INTERVAL = 250;
```
### Long-Running Sessions (Multi-day)
```typescript
// Preserve more context
const POOL_MAX_ITEMS = 100;
const COMPACTION_KEEP_ITEMS = 20;
// Slower decay
const POOL_DECAY_GAMMA = 0.90;
// Longer TTL
const SWEEP_TTL_DAYS = 14;
```
### Memory-Constrained Environments
```typescript
// Strict limits
const CORE_MEMORY_LIMITS = {
goal: 500,
progress: 1000,
context: 800,
};
const POOL_MAX_ITEMS = 20;
// Aggressive pruning
const PRUNE_THRESHOLD_NORMAL = 20;
```
## Debugging Configuration
### Enable Verbose Logging
Add `console.log()` statements in key functions:
```typescript
// In loadCoreMemory()
console.log("[Core Memory] Loaded:", memory);
// In applyDecay()
console.log("[Pool Decay] Pruned items:", prunedCount);
// In sweepToolOutputCache()
console.log("[Sweep] Deleted files:", deletedCount);
```
### Inspect Memory Files
```bash
# Core memory
cat .opencode/memory-core/<sessionID>.json | jq
# Working memory
cat .opencode/memory-working/<sessionID>.json | jq
# Pressure state
cat .opencode/memory-working/<sessionID>_pressure.json | jq
# Sweep log
cat .opencode/memory-working/<sessionID>_sweep.json | jq
```
## Migration Notes
### Upgrading from Pre-Phase 3
Old format files are automatically migrated:
```typescript
// Old format
{ items: Array<Item> }
// New format (auto-migrated)
{ slots: { error: [], decision: [], ... }, pool: [...] }
```
No manual intervention required.
### Upgrading from Phase 3 to Phase 4.5
Storage governance is backward compatible. No migration needed.
## Best Practices
1. **Core Memory Discipline**:
- Clear `goal` immediately after task completion
- Keep `progress` concise (use checklist format)
- Only put actively edited files in `context`
2. **Working Memory Hygiene**:
- Clear `error` slot after fixing all errors (`working_memory_clear_slot`)
- Let pool decay naturally (avoid manual removal unless necessary)
- Review working memory periodically (use `working_memory_read`)
3. **Pressure Management**:
- Respond to "moderate" warnings proactively
- Compress core memory at "high" pressure
- Clear working memory at "critical" pressure
4. **Storage Maintenance**:
- Let sweep run automatically (no manual intervention)
- Delete old session files manually if needed
- Monitor `.opencode/` directory size periodically
---
**Last Updated**: February 2026
**Configuration File**: `index.ts` (constants section)
+131
View File
@@ -0,0 +1,131 @@
# Installation Guide
## Prerequisites
- **OpenCode** 1.0.0 or higher
- **Node.js** 18+ (for development only)
## Quick Install (For Users)
### Option 1: Install from npm (Recommended)
```bash
npm install opencode-working-memory
```
Then add to your `.opencode/package.json`:
```json
{
"plugins": [
"opencode-working-memory"
]
}
```
### Option 2: Install from GitHub
Add to your `.opencode/package.json`:
```json
{
"dependencies": {
"opencode-working-memory": "github:yourusername/opencode-working-memory"
},
"plugins": [
"opencode-working-memory"
]
}
```
Then run:
```bash
cd .opencode
npm install
```
### Option 3: Local Development Install
Clone the repository:
```bash
git clone https://github.com/yourusername/opencode-working-memory.git
cd opencode-working-memory
npm install
```
Link to your OpenCode project:
```bash
cd /path/to/your/project/.opencode
npm link /path/to/opencode-working-memory
```
Add to `.opencode/package.json`:
```json
{
"plugins": [
"opencode-working-memory"
]
}
```
## Verification
After installation, start an OpenCode session and run:
```
core_memory_update goal "Test installation"
```
You should see a success message. Check `.opencode/memory-core/` for the session file.
## Configuration
The plugin works out-of-the-box with sensible defaults. For advanced configuration, see [configuration.md](./configuration.md).
## Troubleshooting
### Plugin Not Loading
**Symptom**: No `core_memory_update` tool available
**Solution**:
1. Check `.opencode/package.json` includes plugin in `"plugins": []` array
2. Verify `npm install` completed successfully
3. Restart OpenCode session
### Memory Files Not Created
**Symptom**: No `.opencode/memory-core/` or `.opencode/memory-working/` directories
**Solution**:
1. Ensure OpenCode has write permissions in project directory
2. Check plugin hooks are registered (look for "Working Memory Plugin" in session logs)
3. Trigger memory operations (e.g., use `core_memory_update` tool)
### Type Errors During Development
**Symptom**: TypeScript errors when modifying plugin
**Solution**:
1. Ensure `@opencode-ai/plugin` is installed: `npm install @opencode-ai/plugin`
2. Run type checking: `npx tsc --noEmit`
3. See [AGENTS.md](../AGENTS.md) for code style guidelines
## Uninstallation
```bash
cd .opencode
npm uninstall opencode-working-memory
```
Remove from `.opencode/package.json` plugins array. Memory files in `.opencode/memory-*` will persist unless manually deleted.
## Next Steps
- Read [Architecture Documentation](./architecture.md) to understand how memory tiers work
- See [Configuration Guide](./configuration.md) for customization options
- Check [AGENTS.md](../AGENTS.md) for development guidelines