opencode-working-memory 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +340 -0
- package/LICENSE +21 -0
- package/README.md +242 -0
- package/docs/architecture.md +375 -0
- package/docs/configuration.md +376 -0
- package/docs/installation.md +131 -0
- package/index.ts +2080 -0
- package/package.json +40 -0
- package/tsconfig.json +28 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
# Architecture Documentation
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The Working Memory Plugin implements a **four-tier memory architecture** designed to maximize context efficiency for AI agents in OpenCode sessions.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
9
|
+
│ TIER 1: CORE MEMORY │
|
|
10
|
+
│ Persistent blocks: goal (1000) | progress (2000) | context (1500) │
|
|
11
|
+
│ Survives compaction, always visible in system prompt │
|
|
12
|
+
└─────────────────────────────────────────────────────────────┘
|
|
13
|
+
↓
|
|
14
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
15
|
+
│ TIER 2: WORKING MEMORY │
|
|
16
|
+
│ Session-scoped slots + memory pool │
|
|
17
|
+
│ Slots: error(3) | decision(5) | todo(3) | dependency(3) │
|
|
18
|
+
│ Pool: Exponential decay (γ=0.85) + mention tracking │
|
|
19
|
+
└─────────────────────────────────────────────────────────────┘
|
|
20
|
+
↓
|
|
21
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
22
|
+
│ TIER 3: SMART PRUNING │
|
|
23
|
+
│ Filters tool outputs before adding to conversation │
|
|
24
|
+
│ Removes: file lists, verbose logs, repetitive content │
|
|
25
|
+
│ Modes: normal → aggressive → hyper-aggressive │
|
|
26
|
+
└─────────────────────────────────────────────────────────────┘
|
|
27
|
+
↓
|
|
28
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
29
|
+
│ TIER 4: PRESSURE MONITORING │
|
|
30
|
+
│ Tracks context usage: safe → moderate → high │
|
|
31
|
+
│ Thresholds: 75% (moderate) | 90% (high) │
|
|
32
|
+
│ Intervention: Sends promptAsync() with full visible prompt │
|
|
33
|
+
└─────────────────────────────────────────────────────────────┘
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Phase 1: Core Memory Foundation
|
|
37
|
+
|
|
38
|
+
### Purpose
|
|
39
|
+
Provide persistent memory blocks that survive conversation compaction and are always injected into the system prompt.
|
|
40
|
+
|
|
41
|
+
### Storage
|
|
42
|
+
- **Location**: `.opencode/memory-core/<sessionID>.json`
|
|
43
|
+
- **Schema**:
|
|
44
|
+
```typescript
|
|
45
|
+
{
|
|
46
|
+
sessionID: string;
|
|
47
|
+
blocks: {
|
|
48
|
+
goal: { content: string; chars: number; maxChars: 1000; updatedAt: string };
|
|
49
|
+
progress: { content: string; chars: number; maxChars: 2000; updatedAt: string };
|
|
50
|
+
context: { content: string; chars: number; maxChars: 1500; updatedAt: string };
|
|
51
|
+
};
|
|
52
|
+
updatedAt: string;
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Character Limits
|
|
57
|
+
- **goal**: 1000 chars (ONE specific task)
|
|
58
|
+
- **progress**: 2000 chars (done/in-progress/blocked checklist)
|
|
59
|
+
- **context**: 1500 chars (current working files + key patterns)
|
|
60
|
+
|
|
61
|
+
### Operations
|
|
62
|
+
- **replace**: Completely replace block content
|
|
63
|
+
- **append**: Add content to end (auto-adds newline)
|
|
64
|
+
|
|
65
|
+
### Tools
|
|
66
|
+
- `core_memory_update`: Update or append to blocks
|
|
67
|
+
- `core_memory_read`: Read current state of all blocks
|
|
68
|
+
|
|
69
|
+
### System Prompt Injection
|
|
70
|
+
Blocks are injected into every agent message as:
|
|
71
|
+
```
|
|
72
|
+
<core_memory>
|
|
73
|
+
<goal chars="87/1000">...</goal>
|
|
74
|
+
<progress chars="560/2000">...</progress>
|
|
75
|
+
<context chars="479/1500">...</context>
|
|
76
|
+
</core_memory>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Phase 2: Smart Pruning
|
|
80
|
+
|
|
81
|
+
### Purpose
|
|
82
|
+
Reduce context bloat by filtering tool outputs before they enter the conversation history.
|
|
83
|
+
|
|
84
|
+
### Pruning Modes
|
|
85
|
+
|
|
86
|
+
#### Normal Mode (Pressure < 75%)
|
|
87
|
+
- Remove file/directory listings > 50 lines
|
|
88
|
+
- Truncate verbose tool outputs
|
|
89
|
+
- Keep first/last 30 lines of long outputs
|
|
90
|
+
- Preserve error messages and key information
|
|
91
|
+
|
|
92
|
+
#### Aggressive Mode (75% ≤ Pressure < 90%)
|
|
93
|
+
- Threshold drops to 30 lines
|
|
94
|
+
- More aggressive truncation (first/last 20 lines)
|
|
95
|
+
- Filter repetitive content
|
|
96
|
+
|
|
97
|
+
#### Hyper-Aggressive Mode (Pressure ≥ 90%)
|
|
98
|
+
- Threshold drops to 15 lines
|
|
99
|
+
- Keep only first/last 10 lines
|
|
100
|
+
- Maximum compression
|
|
101
|
+
|
|
102
|
+
### Pruning Heuristics
|
|
103
|
+
|
|
104
|
+
1. **File Listings**: Detect `ls`, `find`, `glob` outputs
|
|
105
|
+
2. **Directory Trees**: Detect tree-like structures with `/`
|
|
106
|
+
3. **Log Files**: Detect timestamp patterns, stack traces
|
|
107
|
+
4. **Repetitive Content**: Detect similar consecutive lines
|
|
108
|
+
5. **Synthetic Content**: Preserve `synthetic: true` markers
|
|
109
|
+
|
|
110
|
+
### Implementation
|
|
111
|
+
Pruning happens in `tool.execute.after` hook before tool output enters conversation.
|
|
112
|
+
|
|
113
|
+
## Phase 3: Working Memory
|
|
114
|
+
|
|
115
|
+
### Purpose
|
|
116
|
+
Provide session-scoped memory with structured slots and a general-purpose pool with intelligent decay.
|
|
117
|
+
|
|
118
|
+
### Storage
|
|
119
|
+
- **Location**: `.opencode/memory-working/<sessionID>.json`
|
|
120
|
+
- **Schema**:
|
|
121
|
+
```typescript
|
|
122
|
+
{
|
|
123
|
+
sessionID: string;
|
|
124
|
+
slots: {
|
|
125
|
+
error: Array<WorkingMemoryItem>; // Max 3
|
|
126
|
+
decision: Array<WorkingMemoryItem>; // Max 5
|
|
127
|
+
todo: Array<WorkingMemoryItem>; // Max 3
|
|
128
|
+
dependency: Array<WorkingMemoryItem>; // Max 3
|
|
129
|
+
};
|
|
130
|
+
pool: Array<WorkingMemoryItem>;
|
|
131
|
+
eventCounter: number;
|
|
132
|
+
updatedAt: string;
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Slot Types
|
|
137
|
+
|
|
138
|
+
| Slot | Max Items | Purpose |
|
|
139
|
+
|------|-----------|---------|
|
|
140
|
+
| **error** | 3 | Recent errors that need fixing |
|
|
141
|
+
| **decision** | 5 | Important decisions made |
|
|
142
|
+
| **todo** | 3 | Current task checklist |
|
|
143
|
+
| **dependency** | 3 | File/package dependencies |
|
|
144
|
+
|
|
145
|
+
### Memory Pool
|
|
146
|
+
|
|
147
|
+
General-purpose storage with **exponential decay**:
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
score = exp(-γ * age) + mentionCount
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Where:
|
|
154
|
+
- `γ = 0.85` (decay rate, 15% per event)
|
|
155
|
+
- `age = eventCounter - item.eventNumber`
|
|
156
|
+
- `mentionCount`: Number of times item mentioned in conversation
|
|
157
|
+
|
|
158
|
+
Items with `score < 0.01` are pruned.
|
|
159
|
+
|
|
160
|
+
### Auto-Extraction
|
|
161
|
+
|
|
162
|
+
Working memory items are **automatically extracted** from:
|
|
163
|
+
- Tool outputs (file paths, errors, dependencies)
|
|
164
|
+
- User messages (decisions, todos)
|
|
165
|
+
- Assistant responses (key information)
|
|
166
|
+
|
|
167
|
+
### Manual Management
|
|
168
|
+
|
|
169
|
+
Tools:
|
|
170
|
+
- `working_memory_add`: Manually add item
|
|
171
|
+
- `working_memory_clear`: Clear all items
|
|
172
|
+
- `working_memory_clear_slot`: Clear specific slot (e.g., after fixing all errors)
|
|
173
|
+
- `working_memory_remove`: Remove specific item by content match
|
|
174
|
+
|
|
175
|
+
### System Prompt Injection
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
<working_memory>
|
|
179
|
+
Recent session context (auto-managed, sorted by relevance):
|
|
180
|
+
|
|
181
|
+
⚠️ Errors:
|
|
182
|
+
- TypeError at line 42 in utils.ts
|
|
183
|
+
- Missing import in index.ts
|
|
184
|
+
|
|
185
|
+
📁 Key Files:
|
|
186
|
+
- src/components/Button.tsx
|
|
187
|
+
- src/utils/helpers.ts
|
|
188
|
+
|
|
189
|
+
(15 items shown, updated: 9:46:47 AM)
|
|
190
|
+
</working_memory>
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Phase 4: Pressure Monitoring
|
|
194
|
+
|
|
195
|
+
### Purpose
|
|
196
|
+
Track conversation context usage and trigger interventions when approaching limits.
|
|
197
|
+
|
|
198
|
+
### Pressure Calculation
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
pressure = (visiblePromptChars / estimatedContextLimit) * 100
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Where:
|
|
205
|
+
- `visiblePromptChars`: Total characters in system prompt + tool outputs
|
|
206
|
+
- `estimatedContextLimit`: ~180,000 chars (conservative estimate)
|
|
207
|
+
|
|
208
|
+
### Pressure Levels
|
|
209
|
+
|
|
210
|
+
| Level | Threshold | Behavior |
|
|
211
|
+
|-------|-----------|----------|
|
|
212
|
+
| **safe** | < 75% | Normal operation |
|
|
213
|
+
| **moderate** | 75-89% | Warning in system prompt + aggressive pruning |
|
|
214
|
+
| **high** | ≥ 90% | Hyper-aggressive pruning + intervention |
|
|
215
|
+
|
|
216
|
+
### Pressure Storage
|
|
217
|
+
|
|
218
|
+
- **Location**: `.opencode/memory-working/<sessionID>_pressure.json`
|
|
219
|
+
- **Schema**:
|
|
220
|
+
```typescript
|
|
221
|
+
{
|
|
222
|
+
sessionID: string;
|
|
223
|
+
level: "safe" | "moderate" | "high";
|
|
224
|
+
percentage: number;
|
|
225
|
+
visiblePromptChars: number;
|
|
226
|
+
estimatedLimit: 180000;
|
|
227
|
+
lastChecked: string;
|
|
228
|
+
interventionsSent: number;
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Intervention Mechanism
|
|
233
|
+
|
|
234
|
+
When pressure reaches **high** (≥90%):
|
|
235
|
+
1. Plugin sends `promptAsync()` message to agent
|
|
236
|
+
2. Message includes full visible prompt for review
|
|
237
|
+
3. Agent can compress core memory, clear working memory, or continue
|
|
238
|
+
4. Intervention tracked in `interventionsSent` counter
|
|
239
|
+
|
|
240
|
+
### System Prompt Injection
|
|
241
|
+
|
|
242
|
+
```
|
|
243
|
+
[Memory Pressure: 87% (high) - 156,600/180,000 chars]
|
|
244
|
+
⚠️ High memory pressure detected. Consider:
|
|
245
|
+
- Compressing core_memory blocks (use core_memory_update)
|
|
246
|
+
- Clearing resolved errors (use working_memory_clear_slot)
|
|
247
|
+
- Removing old pool items (auto-pruned at score < 0.01)
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Phase 4.5: Storage Governance
|
|
251
|
+
|
|
252
|
+
### Purpose
|
|
253
|
+
Prevent `.opencode/` directory bloat from accumulating tool output caches and orphaned memory files.
|
|
254
|
+
|
|
255
|
+
### Layer 1: Session Deletion Cleanup
|
|
256
|
+
|
|
257
|
+
**Trigger**: `experimental.session.deleted` hook
|
|
258
|
+
|
|
259
|
+
**Actions**:
|
|
260
|
+
1. Remove `.opencode/memory-core/<sessionID>.json`
|
|
261
|
+
2. Remove `.opencode/memory-working/<sessionID>.json`
|
|
262
|
+
3. Remove `.opencode/memory-working/<sessionID>_pressure.json`
|
|
263
|
+
4. Remove `.opencode/memory-working/<sessionID>_compaction.json`
|
|
264
|
+
|
|
265
|
+
### Layer 2: Tool Output Cache Sweep
|
|
266
|
+
|
|
267
|
+
**Trigger**: Every 500 events (`eventCounter % 500 === 0`)
|
|
268
|
+
|
|
269
|
+
**Target**: `.opencode/cache/tool-outputs/` directory
|
|
270
|
+
|
|
271
|
+
**Policy**:
|
|
272
|
+
- Keep most recent **300 files** (sorted by mtime)
|
|
273
|
+
- Delete files older than **7 days** (TTL policy)
|
|
274
|
+
|
|
275
|
+
**Logging**: Write sweep results to `.opencode/memory-working/<sessionID>_sweep.json`
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
{
|
|
279
|
+
sessionID: string;
|
|
280
|
+
timestamp: string;
|
|
281
|
+
eventCounter: number;
|
|
282
|
+
results: {
|
|
283
|
+
filesScanned: number;
|
|
284
|
+
filesDeleted: number;
|
|
285
|
+
bytesReclaimed: number;
|
|
286
|
+
errors: Array<string>;
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## Performance Considerations
|
|
292
|
+
|
|
293
|
+
### Memory Budgets
|
|
294
|
+
- **Core Memory**: 4,500 chars (injected every message)
|
|
295
|
+
- **Working Memory**: ~1,600 chars (injected every message)
|
|
296
|
+
- **Total Overhead**: ~6,100 chars per message
|
|
297
|
+
|
|
298
|
+
### Compaction Behavior
|
|
299
|
+
When OpenCode compacts conversation (clears old messages):
|
|
300
|
+
- Core memory: **Preserved** (persistent across compactions)
|
|
301
|
+
- Working memory: **Preserved** (session-scoped, cleared on session end)
|
|
302
|
+
- Pressure state: **Preserved** (tracks across compaction)
|
|
303
|
+
- Compaction log: Saved to `<sessionID>_compaction.json`
|
|
304
|
+
|
|
305
|
+
### Storage Footprint
|
|
306
|
+
- Each session: 4 JSON files (~5-20 KB total)
|
|
307
|
+
- Tool output cache: Max 300 files (~10-50 MB depending on outputs)
|
|
308
|
+
- Sweep every 500 events keeps storage bounded
|
|
309
|
+
|
|
310
|
+
## Extension Points
|
|
311
|
+
|
|
312
|
+
### Custom Slot Types
|
|
313
|
+
To add new slot types:
|
|
314
|
+
1. Update `SlotType` union in types
|
|
315
|
+
2. Add to `SLOT_CONFIG` with max items
|
|
316
|
+
3. Update `formatWorkingMemoryForPrompt()` for display
|
|
317
|
+
4. Update extraction heuristics in `tool.execute.after`
|
|
318
|
+
|
|
319
|
+
### Custom Pruning Rules
|
|
320
|
+
To add pruning heuristics:
|
|
321
|
+
1. Update `shouldPrune()` with new detection logic
|
|
322
|
+
2. Add to `pruneToolOutput()` with filtering rules
|
|
323
|
+
3. Test with representative tool outputs
|
|
324
|
+
|
|
325
|
+
### Custom Pressure Thresholds
|
|
326
|
+
Adjust in constants:
|
|
327
|
+
```typescript
|
|
328
|
+
const PRESSURE_THRESHOLDS = {
|
|
329
|
+
moderate: 70,
|
|
330
|
+
high: 85,
|
|
331
|
+
critical: 95,
|
|
332
|
+
};
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## Migration & Compatibility
|
|
336
|
+
|
|
337
|
+
### Old Format → New Format
|
|
338
|
+
Plugin automatically migrates from old format:
|
|
339
|
+
```typescript
|
|
340
|
+
// Old format (pre-Phase 3)
|
|
341
|
+
{ items: Array<Item> }
|
|
342
|
+
|
|
343
|
+
// New format (Phase 3+)
|
|
344
|
+
{ slots: Record<SlotType, Array<Item>>, pool: Array<Item> }
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
Migration happens on first load of old format files.
|
|
348
|
+
|
|
349
|
+
## File System Layout
|
|
350
|
+
|
|
351
|
+
```
|
|
352
|
+
.opencode/
|
|
353
|
+
├── memory-core/
|
|
354
|
+
│ └── <sessionID>.json # Core memory blocks
|
|
355
|
+
├── memory-working/
|
|
356
|
+
│ ├── <sessionID>.json # Working memory (slots + pool)
|
|
357
|
+
│ ├── <sessionID>_pressure.json # Pressure monitoring state
|
|
358
|
+
│ ├── <sessionID>_compaction.json # Compaction event log
|
|
359
|
+
│ └── <sessionID>_sweep.json # Storage sweep log
|
|
360
|
+
└── cache/
|
|
361
|
+
└── tool-outputs/
|
|
362
|
+
└── *.json # Tool output cache (auto-swept)
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
## Security Considerations
|
|
366
|
+
|
|
367
|
+
- All files written with `0644` permissions (owner read/write, group/others read)
|
|
368
|
+
- Directories created with `0755` permissions (owner rwx, group/others rx)
|
|
369
|
+
- No sensitive data should be stored in memory blocks (user responsibility)
|
|
370
|
+
- Session IDs are opaque identifiers, not derived from sensitive data
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
**Last Updated**: February 2026
|
|
375
|
+
**Implementation**: `index.ts` (1700+ lines)
|