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
+42
View File
@@ -0,0 +1,42 @@
# Dependencies
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Build outputs
dist/
build/
*.tsbuildinfo
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store
# Testing
coverage/
.nyc_output/
# Environment
.env
.env.local
.env.*.local
# Logs
logs/
*.log
# Temporary files
tmp/
temp/
*.tmp
# Package files
*.tgz
package-lock.json
yarn.lock
pnpm-lock.yaml
+340
View File
@@ -0,0 +1,340 @@
# AGENTS.md - OpenCode Working Memory Plugin Development Guide
## Project Overview
The **OpenCode Working Memory Plugin** provides a four-tier memory architecture for AI agents:
- **Core Memory** - Persistent blocks (goal/progress/context) that survive compaction
- **Working Memory** - Session-scoped context with slots (error/decision/todo/dependency) and memory pool
- **Smart Pruning** - Automatic filtering of tool outputs before adding to context
- **Pressure Monitoring** - Tracks context usage and triggers interventions at thresholds
Written in **TypeScript** for the OpenCode agent environment.
## Installation
```bash
# For development
git clone https://github.com/yourusername/opencode-working-memory.git
cd opencode-working-memory
npm install
# For usage (see README.md)
```
## Build & Development Commands
### Type Checking
```bash
# TypeScript strict mode - fix all type errors before committing
npx tsc --noEmit
```
### Testing
Tests are manually verified through OpenCode sessions:
```bash
# 1. Load plugin in OpenCode session
# 2. Run commands that trigger hooks (e.g., tool execution, compaction)
# 3. Inspect .opencode/memory-core/ and .opencode/memory-working/
# 4. Verify memory blocks appear in system prompts
```
### File Structure
```
opencode-working-memory/
├── index.ts # Main plugin (1700+ lines)
├── package.json # Plugin manifest
├── tsconfig.json # TypeScript config
├── LICENSE # MIT license
├── README.md # User documentation
├── AGENTS.md # This file (developer guide)
└── docs/ # Detailed documentation
├── installation.md
├── architecture.md
└── configuration.md
```
## Code Style Guidelines
### TypeScript Strict Mode
```typescript
// ✅ REQUIRED: Full type annotations, no implicit any
async function loadCoreMemory(
directory: string,
sessionID: string
): Promise<CoreMemory | null>
// ❌ AVOID: Implicit any types
async function loadCoreMemory(directory, sessionID) { }
```
### Type Definitions
```typescript
// ✅ REQUIRED: Define types at module top
type CoreMemory = {
sessionID: string;
blocks: {
goal: CoreBlock;
progress: CoreBlock;
context: CoreBlock;
};
updatedAt: string;
};
// ✅ USE: Union types for variants (not enums)
type PressureLevel = "safe" | "moderate" | "high" | "critical";
// ✅ USE: Record<> for keyed configs
const SLOT_CONFIG: Record<SlotType, number> = {
error: 3,
decision: 5,
todo: 3,
dependency: 3,
};
```
### Imports & Module Organization
```typescript
// ✅ REQUIRED: Group and order imports
// 1. Node.js built-ins
import { existsSync } from "fs";
import { mkdir, readFile, writeFile } from "fs/promises";
import { join } from "path";
// 2. Third-party (OpenCode SDK)
import type { Plugin } from "@opencode-ai/plugin";
import { tool } from "@opencode-ai/plugin";
// 3. Local modules (if any)
// (none currently)
```
### Naming Conventions
```typescript
// ✅ REQUIRED: camelCase for variables & functions
const maxItems = 50;
async function loadCoreMemory() { }
// ✅ REQUIRED: SCREAMING_SNAKE_CASE for constants
const CORE_MEMORY_LIMITS = { goal: 1000, progress: 2000, context: 1500 };
const SLOT_CONFIG = { error: 3, decision: 5, todo: 3, dependency: 3 };
// ✅ REQUIRED: PascalCase for types
type CoreMemory = { ... };
type WorkingMemoryItem = { ... };
// ✅ REQUIRED: get*/set*/load*/save* naming for file operations
function getCoreMemoryPath(directory: string, sessionID: string): string { }
async function loadCoreMemory(directory: string, sessionID: string): Promise<CoreMemory | null> { }
async function saveCoreMemory(directory: string, memory: CoreMemory): Promise<void> { }
// ✅ REQUIRED: ensure*/validate* for pre-checks
async function ensureCoreMemoryDir(directory: string): Promise<void> { }
// ✅ REQUIRED: Prefix private/internal functions with _
function _compressPath(filePath: string): string { }
```
### Function Signatures & Organization
```typescript
// ✅ REQUIRED: Parameters on separate lines if > 80 chars
async function loadWorkingMemory(
directory: string,
sessionID: string
): Promise<WorkingMemory | null> {
// ...
}
// ✅ REQUIRED: Explicit return types (no inference)
function getCompactionLogPath(directory: string, sessionID: string): string {
return join(directory, ".opencode", "memory-working", `${sessionID}_compaction.json`);
}
// ✅ REQUIRED: Async for file/network I/O
async function saveCoreMemory(directory: string, memory: CoreMemory): Promise<void> {
// ...
}
```
### Error Handling
```typescript
// ✅ REQUIRED: Try-catch with descriptive console.error
async function loadCoreMemory(directory: string, sessionID: string): Promise<CoreMemory | null> {
const path = getCoreMemoryPath(directory, sessionID);
if (!existsSync(path)) return null;
try {
const content = await readFile(path, "utf-8");
return JSON.parse(content) as CoreMemory;
} catch (error) {
console.error("Failed to load core memory:", error);
return null; // Graceful degradation
}
}
// ✅ REQUIRED: Type guards for runtime safety
if (!existsSync(path)) {
return null;
}
// ✅ REQUIRED: Validate JSON before use
const data = JSON.parse(content);
const typedData = data as CoreMemory; // Explicit cast after validation
```
### Comments & Documentation
```typescript
// ✅ REQUIRED: Section headers for major sections
// ============================================================================
// Phase 1: Core Memory Foundation
// ============================================================================
// ✅ REQUIRED: Block comments for complex logic
// Migration: Convert old format (items array) to new format (slots + pool)
if (data.items && !data.slots) {
// ... migration logic
}
// ✅ USE: Inline comments sparingly
const gamma = 0.85; // Exponential decay rate (15% per event)
// ✅ AVOID: Over-commenting obvious code
const name = "test"; // Set name to test ❌ (obvious)
```
### Code Organization
```typescript
// ✅ REQUIRED: Organize plugin file by phase/feature
// 1. Header & module documentation
// 2. Imports
// 3. Types & schemas (grouped by phase)
// 4. Constants & configs
// 5. Helper functions (private first, public after)
// 6. Main plugin export
// 7. Hook implementations
export default {
// Plugin definition
} as Plugin;
```
### Working with OpenCode Plugin SDK
```typescript
// ✅ REQUIRED: Use proper hook signatures
import { tool, type Plugin } from "@opencode-ai/plugin";
export default {
id: "working-memory",
name: "Working Memory Plugin",
// ✅ Core hooks
hooks: {
"tool.execute.after": async (ctx) => {
// Tool just executed
},
"experimental.chat.system.transform": async (ctx) => {
// Transform system prompt before sending
},
"experimental.session.compacting": async (ctx) => {
// Session is being compacted (clearing old messages)
},
},
// ✅ Exposed tools
tools: [
tool({
id: "core_memory_update",
name: "Update Core Memory",
description: "Update goal/progress/context blocks",
// ... schema & execute
}),
],
} as Plugin;
```
## Key Implementation Details
### Core Memory Files
- Location: `.opencode/memory-core/<sessionID>.json`
- Schema: `{ sessionID, blocks: { goal, progress, context }, updatedAt }`
- Limits: goal (1000 chars), progress (2000 chars), context (1500 chars)
### Working Memory Files
- Location: `.opencode/memory-working/<sessionID>.json`
- Schema: `{ sessionID, slots, pool, eventCounter, updatedAt }`
- Slot limits: error (3), decision (5), todo (3), dependency (3)
- Pool decay: γ=0.85 per event
### Pressure Monitoring
- Triggers at: 70% (safe→moderate), 85% (moderate→high), 95% (high→critical)
- Files: `.opencode/memory-working/<sessionID>_pressure.json`
- Intervention: Sends `promptAsync()` with complete visible prompt
### Storage Governance (Layer 1 & 2)
- **Layer 1**: Session deletion cleanup - removes orphaned memory files
- **Layer 2**: Tool output cache sweep - maintains 300 most recent files, 7-day TTL
- Triggered at `eventCounter % 500 === 0` (automatic maintenance)
## Debugging & Testing
### Manual Testing Steps
1. **Phase 1 (Core Memory)**: Check `.opencode/memory-core/` after `core_memory_update`
2. **Phase 2 (Smart Pruning)**: Verify tool outputs are filtered before context injection
3. **Phase 3 (Working Memory)**: Check `.opencode/memory-working/` for slot/pool items
4. **Phase 4 (Pressure Monitoring)**: Monitor pressure % in system prompts, verify interventions
5. **Phase 4.5 (Storage Governance)**: Run 500+ events, check sweep logs
### Common Issues
- **File not found**: Ensure `.opencode/` directory exists and is writable
- **Type errors**: Check all imports use `import type { ... }` for types
- **Lost memory**: Verify `.opencode/memory-*/` is in `.gitignore` (not committed)
- **Sweep not running**: Check `eventCounter` in `<sessionID>.json`, should trigger at multiples of 500
## Performance Considerations
- **Memory budgets**: Core (5.5k chars total), Working (1.6k chars for system prompt)
- **Pruning**: Hyper-aggressive mode activates at ≥85% pressure
- **Compaction**: Preserves most recent 10 items when space-constrained
- **Decay**: Pool items scored by exponential decay (γ=0.85) + mention count
- **Storage sweep**: Limits cache to 300 files, removes files older than 7 days
## File Path References
When referencing code locations in documentation/comments, use:
```
path/to/file.ts:L123 or path/to/file.ts:Line 123
```
Example: `Function sendPressureInterventionMessage() @ index.ts:L1286`
## Contributing
1. Fork the repository
2. Create a feature branch: `git checkout -b feature/my-feature`
3. Make changes following the code style guidelines above
4. Test manually in OpenCode session
5. Commit with descriptive message: `git commit -m "Add feature: ..."`
6. Push to your fork: `git push origin feature/my-feature`
7. Open a pull request
## Architecture Documentation
See `docs/architecture.md` for detailed technical documentation including:
- Memory tier hierarchy
- Pruning algorithms
- Decay formulas
- Pressure monitoring logic
- Storage governance policies
---
**Last Updated**: February 2026
**Plugin Status**: Production (Phases 1-4.5 complete)
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 OpenCode Working Memory Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+242
View File
@@ -0,0 +1,242 @@
# OpenCode Working Memory Plugin
[![npm version](https://img.shields.io/npm/v/opencode-working-memory.svg)](https://www.npmjs.com/package/opencode-working-memory)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
**Advanced four-tier memory architecture that keeps your AI agent sharp, focused, and never forgets what matters.**
Stop losing context across compactions. Stop watching your agent repeat the same mistakes. This plugin gives your OpenCode agent a professional-grade memory system that scales with your project complexity.
## What You Get
- 🧠 **Core Memory** - Persistent goal/progress/context blocks that survive compaction
- 💡 **Working Memory** - Smart slot-based system (errors, decisions, todos, dependencies)
- 🎯 **Memory Pressure Monitoring** - Real-time token tracking with automatic interventions
- 🧹 **Auto Storage Governance** - Prevents unbounded disk growth (300 files/session, 7-day TTL)
- 📊 **Smart Pruning** - Pressure-aware tool output compression
-**Zero Configuration** - Works out of the box, highly customizable
## Installation
### For Humans
**Option A: Let an LLM do it**
Paste this into Claude Code, Cursor, or any AI agent:
```
Install opencode-working-memory plugin by following:
https://raw.githubusercontent.com/yourusername/opencode-working-memory/main/docs/installation.md
```
**Option B: Manual setup**
1. Add to your `~/.config/opencode/opencode.json`:
```json
{
"plugin": ["opencode-working-memory"]
}
```
2. Restart OpenCode
That's it! The plugin is now active.
### For LLM Agents
1. Read the OpenCode configuration file at `~/.config/opencode/opencode.json`
2. Add `"opencode-working-memory"` to the `plugin` array
3. Verify installation by checking the config file
## Quick Start
The plugin works automatically once installed. No configuration needed.
**Use the tools:**
```bash
# Update persistent memory
opencode run "use core_memory_update to set my goal"
# Read current memory state
opencode run "use core_memory_read to show me what you remember"
# Add important items to working memory
opencode run "use working_memory_add to remember this file path"
```
**The agent will automatically:**
- Track memory pressure and warn when approaching compaction
- Preserve important context during compaction
- Clean up old tool-output cache files every 20 tool calls
- Remove artifacts when sessions are deleted
## Features
### 🧠 Core Memory (Phase 1)
Persistent blocks that survive conversation resets:
- **goal** (1000 chars) - Current task/objective
- **progress** (2000 chars) - What's done, in-progress, next steps
- **context** (1500 chars) - Key file paths, conventions, patterns
### 💡 Working Memory (Phase 3)
Auto-extracts and ranks important information:
- **Slots** (guaranteed visibility): errors, decisions, todos, dependencies
- **Pool** (ranked by relevance): file paths, recent activity
- Exponential decay keeps memory fresh
- FIFO limits prevent bloat
### 🎯 Memory Pressure Monitoring (Phase 4)
Real-time token tracking from session database:
- Monitors context window usage (75% → 90% → 95% thresholds)
- Proactive intervention messages when pressure is high
- Pressure-aware smart pruning (adapts compression based on pressure)
### 🧹 Storage Governance (Phase 5)
Prevents unbounded disk growth:
- **Layer 1**: Auto-cleanup on session deletion (all artifacts removed)
- **Layer 2**: Active cache management (max 300 files/session, 7-day TTL)
- Triggers every 20 tool calls
- Silent background operation
### 📊 Smart Pruning (Phase 2)
Intelligent tool output compression:
- Per-tool strategies (keep-all, keep-ends, keep-last, discard)
- Pressure-aware limits (2k/5k/10k lines based on memory pressure)
- Preserves important context while reducing noise
## Documentation
- [Installation Guide](docs/installation.md) - Detailed setup instructions
- [Architecture Overview](docs/architecture.md) - How it works under the hood
- [Configuration](docs/configuration.md) - Customization options
- [Agent Developer Guide](AGENTS.md) - For plugin developers
## Tools Provided
The plugin exposes these tools to your OpenCode agent:
- `core_memory_update` - Update goal/progress/context blocks
- `core_memory_read` - Read current memory state
- `working_memory_add` - Manually add important items
- `working_memory_clear` - Clear all working memory
- `working_memory_clear_slot` - Clear specific slot (errors/decisions)
- `working_memory_remove` - Remove specific item by content
## How It Works
```
┌─────────────────────────────────────────────────────────┐
│ Core Memory (Always Visible) │
│ ┌─────────┬──────────┬──────────┐ │
│ │ Goal │ Progress │ Context │ │
│ └─────────┴──────────┴──────────┘ │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ Working Memory (Auto-Extracted) │
│ ┌──────────────────┬──────────────────┐ │
│ │ Slots (FIFO) │ Pool (Ranked) │ │
│ │ • errors │ • file-paths │ │
│ │ • decisions │ • recent │ │
│ │ • todos │ • mentions │ │
│ │ • dependencies │ • decay score │ │
│ └──────────────────┴──────────────────┘ │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ Memory Pressure Monitor │
│ • Tracks tokens from session DB │
│ • Warns at 75% / 90% / 95% │
│ • Sends proactive interventions │
│ • Adjusts pruning aggressiveness │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ Storage Governance │
│ • Session deletion → cleanup all artifacts │
│ • Every 20 calls → sweep old cache (300 max, 7d TTL) │
│ • Silent background operation │
└─────────────────────────────────────────────────────────┘
```
## Why This Plugin?
**Without this plugin:**
- 🔴 Agent forgets context after compaction
- 🔴 Repeats resolved errors
- 🔴 Loses track of project structure
- 🔴 Context window fills up uncontrollably
- 🔴 Disk space grows unbounded
**With this plugin:**
- ✅ Persistent memory across compactions
- ✅ Smart auto-extraction of important info
- ✅ Real-time pressure monitoring with interventions
- ✅ Automatic storage cleanup
- ✅ Pressure-aware compression
- ✅ Zero configuration, works immediately
## Configuration (Optional)
The plugin works great with zero configuration. But if you want to customize:
Create `~/.config/opencode/working-memory.json`:
```json
{
"storage_governance": {
"tool_output_max_files": 300,
"tool_output_max_age_ms": 604800000,
"sweep_interval": 20
},
"memory_pressure": {
"thresholds": {
"moderate": 0.75,
"high": 0.90,
"critical": 0.95
}
}
}
```
See [Configuration Guide](docs/configuration.md) for all options.
## Requirements
- OpenCode >= 1.0.0
- Node.js >= 18.0.0
- `@opencode-ai/plugin` >= 1.2.0
## License
MIT License - see [LICENSE](LICENSE) file for details.
## Contributing
Contributions welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) first.
## Support
- 📖 [Documentation](docs/)
- 🐛 [Report Issues](https://github.com/yourusername/opencode-working-memory/issues)
- 💬 [Discussions](https://github.com/yourusername/opencode-working-memory/discussions)
## Credits
Inspired by the needs of real-world OpenCode usage and built to solve actual pain points in AI-assisted development.
---
**Made with ❤️ for the OpenCode community**
+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
+2082
View File
File diff suppressed because it is too large Load Diff
+31
View File
@@ -0,0 +1,31 @@
{
"name": "opencode-working-memory",
"version": "1.0.0",
"description": "Advanced four-tier memory architecture for OpenCode with intelligent pressure monitoring and auto-storage governance",
"type": "module",
"main": "index.ts",
"keywords": [
"opencode",
"plugin",
"memory",
"context-management",
"ai-agent",
"llm"
],
"author": "Your Name <your.email@example.com>",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/yourusername/opencode-working-memory.git"
},
"bugs": {
"url": "https://github.com/yourusername/opencode-working-memory/issues"
},
"homepage": "https://github.com/yourusername/opencode-working-memory#readme",
"peerDependencies": {
"@opencode-ai/plugin": "^1.2.0"
},
"engines": {
"node": ">=18.0.0"
}
}
+27
View File
@@ -0,0 +1,27 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"lib": ["ES2022"],
"moduleResolution": "node",
"esModuleInterop": true,
"skipLibCheck": true,
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true
},
"include": ["index.ts"],
"exclude": ["node_modules", "dist"]
}