opencode-working-memory 1.0.0 โ 1.0.1
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/README.md +25 -61
- package/docs/configuration.md +2 -3
- package/docs/installation.md +22 -82
- package/index.ts +49 -120
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,11 +18,9 @@ Stop losing context across compactions. Stop watching your agent repeat the same
|
|
|
18
18
|
|
|
19
19
|
## Installation
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
**Option A: Let an AI agent do it**
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
Paste this into Claude Code, Cursor, or any AI agent:
|
|
23
|
+
Paste this into any AI agent:
|
|
26
24
|
|
|
27
25
|
```
|
|
28
26
|
Install opencode-working-memory plugin by following:
|
|
@@ -31,7 +29,7 @@ https://raw.githubusercontent.com/sdwolf4103/opencode-working-memory/main/docs/i
|
|
|
31
29
|
|
|
32
30
|
**Option B: Manual setup**
|
|
33
31
|
|
|
34
|
-
|
|
32
|
+
Add to your `~/.config/opencode/opencode.json`:
|
|
35
33
|
|
|
36
34
|
```json
|
|
37
35
|
{
|
|
@@ -39,42 +37,33 @@ https://raw.githubusercontent.com/sdwolf4103/opencode-working-memory/main/docs/i
|
|
|
39
37
|
}
|
|
40
38
|
```
|
|
41
39
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
That's it! The plugin is now active.
|
|
40
|
+
Restart OpenCode. The plugin is downloaded and installed automatically.
|
|
45
41
|
|
|
46
42
|
### For LLM Agents
|
|
47
43
|
|
|
48
|
-
1.
|
|
44
|
+
1. Edit `~/.config/opencode/opencode.json`
|
|
49
45
|
2. Add `"opencode-working-memory"` to the `plugin` array
|
|
50
|
-
3. Verify
|
|
46
|
+
3. Verify the config file was saved correctly
|
|
51
47
|
|
|
52
48
|
## Quick Start
|
|
53
49
|
|
|
54
50
|
The plugin works automatically once installed. No configuration needed.
|
|
55
51
|
|
|
56
|
-
**
|
|
57
|
-
|
|
58
|
-
```bash
|
|
59
|
-
# Update persistent memory
|
|
60
|
-
opencode run "use core_memory_update to set my goal"
|
|
52
|
+
**Try telling your agent:**
|
|
61
53
|
|
|
62
|
-
# Read current memory state
|
|
63
|
-
opencode run "use core_memory_read to show me what you remember"
|
|
64
|
-
|
|
65
|
-
# Add important items to working memory
|
|
66
|
-
opencode run "use working_memory_add to remember this file path"
|
|
67
54
|
```
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
55
|
+
Use core_memory_update to set my current goal
|
|
56
|
+
```
|
|
57
|
+
```
|
|
58
|
+
Use core_memory_read to show me what you remember
|
|
59
|
+
```
|
|
60
|
+
```
|
|
61
|
+
Use working_memory_add to remember this file path
|
|
62
|
+
```
|
|
74
63
|
|
|
75
64
|
## Features
|
|
76
65
|
|
|
77
|
-
### ๐ง Core Memory
|
|
66
|
+
### ๐ง Core Memory
|
|
78
67
|
|
|
79
68
|
Persistent blocks that survive conversation resets:
|
|
80
69
|
|
|
@@ -82,7 +71,7 @@ Persistent blocks that survive conversation resets:
|
|
|
82
71
|
- **progress** (2000 chars) - What's done, in-progress, next steps
|
|
83
72
|
- **context** (1500 chars) - Key file paths, conventions, patterns
|
|
84
73
|
|
|
85
|
-
### ๐ก Working Memory
|
|
74
|
+
### ๐ก Working Memory
|
|
86
75
|
|
|
87
76
|
Auto-extracts and ranks important information:
|
|
88
77
|
|
|
@@ -91,7 +80,7 @@ Auto-extracts and ranks important information:
|
|
|
91
80
|
- Exponential decay keeps memory fresh
|
|
92
81
|
- FIFO limits prevent bloat
|
|
93
82
|
|
|
94
|
-
### ๐ฏ Memory Pressure Monitoring
|
|
83
|
+
### ๐ฏ Memory Pressure Monitoring
|
|
95
84
|
|
|
96
85
|
Real-time token tracking from session database:
|
|
97
86
|
|
|
@@ -99,16 +88,15 @@ Real-time token tracking from session database:
|
|
|
99
88
|
- Proactive intervention messages when pressure is high
|
|
100
89
|
- Pressure-aware smart pruning (adapts compression based on pressure)
|
|
101
90
|
|
|
102
|
-
### ๐งน Storage Governance
|
|
91
|
+
### ๐งน Storage Governance
|
|
103
92
|
|
|
104
93
|
Prevents unbounded disk growth:
|
|
105
94
|
|
|
106
|
-
-
|
|
107
|
-
-
|
|
108
|
-
- Triggers every 20 tool calls
|
|
95
|
+
- Auto-cleanup on session deletion (all artifacts removed)
|
|
96
|
+
- Active cache management (max 300 files/session, 7-day TTL)
|
|
109
97
|
- Silent background operation
|
|
110
98
|
|
|
111
|
-
### ๐ Smart Pruning
|
|
99
|
+
### ๐ Smart Pruning
|
|
112
100
|
|
|
113
101
|
Intelligent tool output compression:
|
|
114
102
|
|
|
@@ -190,28 +178,7 @@ The plugin exposes these tools to your OpenCode agent:
|
|
|
190
178
|
|
|
191
179
|
## Configuration (Optional)
|
|
192
180
|
|
|
193
|
-
The plugin works great with zero configuration.
|
|
194
|
-
|
|
195
|
-
Create `~/.config/opencode/working-memory.json`:
|
|
196
|
-
|
|
197
|
-
```json
|
|
198
|
-
{
|
|
199
|
-
"storage_governance": {
|
|
200
|
-
"tool_output_max_files": 300,
|
|
201
|
-
"tool_output_max_age_ms": 604800000,
|
|
202
|
-
"sweep_interval": 20
|
|
203
|
-
},
|
|
204
|
-
"memory_pressure": {
|
|
205
|
-
"thresholds": {
|
|
206
|
-
"moderate": 0.75,
|
|
207
|
-
"high": 0.90,
|
|
208
|
-
"critical": 0.95
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
See [Configuration Guide](docs/configuration.md) for all options.
|
|
181
|
+
The plugin works great with zero configuration. To customize behavior, modify the constants at the top of `index.ts`. See the [Configuration Guide](docs/configuration.md) for all tunable options.
|
|
215
182
|
|
|
216
183
|
## Requirements
|
|
217
184
|
|
|
@@ -223,20 +190,17 @@ See [Configuration Guide](docs/configuration.md) for all options.
|
|
|
223
190
|
|
|
224
191
|
MIT License - see [LICENSE](LICENSE) file for details.
|
|
225
192
|
|
|
226
|
-
## Contributing
|
|
227
|
-
|
|
228
|
-
Contributions welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) first.
|
|
229
|
-
|
|
230
193
|
## Support
|
|
231
194
|
|
|
232
195
|
- ๐ [Documentation](docs/)
|
|
233
196
|
- ๐ [Report Issues](https://github.com/sdwolf4103/opencode-working-memory/issues)
|
|
234
|
-
- ๐ฌ [Discussions](https://github.com/sdwolf4103/opencode-working-memory/discussions)
|
|
235
197
|
|
|
236
198
|
## Credits
|
|
237
199
|
|
|
238
200
|
Inspired by the needs of real-world OpenCode usage and built to solve actual pain points in AI-assisted development.
|
|
239
201
|
|
|
202
|
+
> This project is not affiliated with or endorsed by the OpenCode team.
|
|
203
|
+
|
|
240
204
|
---
|
|
241
205
|
|
|
242
206
|
**Made with โค๏ธ for the OpenCode community**
|
package/docs/configuration.md
CHANGED
|
@@ -68,9 +68,8 @@ const POOL_MAX_ITEMS = 50; // Hard limit on pool size
|
|
|
68
68
|
|
|
69
69
|
```typescript
|
|
70
70
|
const PRESSURE_THRESHOLDS = {
|
|
71
|
-
moderate:
|
|
72
|
-
high:
|
|
73
|
-
critical: 95, // Intervention sent to agent
|
|
71
|
+
moderate: 75, // Warning appears in system prompt
|
|
72
|
+
high: 90, // Aggressive pruning activates + intervention sent
|
|
74
73
|
};
|
|
75
74
|
```
|
|
76
75
|
|
package/docs/installation.md
CHANGED
|
@@ -1,90 +1,34 @@
|
|
|
1
1
|
# Installation Guide
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Quick Install
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
- **Node.js** 18+ (for development only)
|
|
7
|
-
|
|
8
|
-
## Quick Install (For Users)
|
|
9
|
-
|
|
10
|
-
### Option 1: Install from npm (Recommended)
|
|
11
|
-
|
|
12
|
-
```bash
|
|
13
|
-
npm install opencode-working-memory
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
Then add to your `.opencode/package.json`:
|
|
17
|
-
|
|
18
|
-
```json
|
|
19
|
-
{
|
|
20
|
-
"plugins": [
|
|
21
|
-
"opencode-working-memory"
|
|
22
|
-
]
|
|
23
|
-
}
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
### Option 2: Install from GitHub
|
|
27
|
-
|
|
28
|
-
Add to your `.opencode/package.json`:
|
|
5
|
+
Add to your `~/.config/opencode/opencode.json`:
|
|
29
6
|
|
|
30
7
|
```json
|
|
31
8
|
{
|
|
32
|
-
"
|
|
33
|
-
"opencode-working-memory": "github:sdwolf4103/opencode-working-memory"
|
|
34
|
-
},
|
|
35
|
-
"plugins": [
|
|
36
|
-
"opencode-working-memory"
|
|
37
|
-
]
|
|
9
|
+
"plugin": ["opencode-working-memory"]
|
|
38
10
|
}
|
|
39
11
|
```
|
|
40
12
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
cd .opencode
|
|
45
|
-
npm install
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
### Option 3: Local Development Install
|
|
49
|
-
|
|
50
|
-
Clone the repository:
|
|
13
|
+
Restart OpenCode. The plugin is downloaded and installed automatically โ no `npm install` needed.
|
|
51
14
|
|
|
52
|
-
|
|
53
|
-
git clone https://github.com/sdwolf4103/opencode-working-memory.git
|
|
54
|
-
cd opencode-working-memory
|
|
55
|
-
npm install
|
|
56
|
-
```
|
|
15
|
+
> **Note**: The correct key is `plugin` (singular), not `plugins`.
|
|
57
16
|
|
|
58
|
-
|
|
17
|
+
## For LLM Agents
|
|
59
18
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
Add to `.opencode/package.json`:
|
|
66
|
-
|
|
67
|
-
```json
|
|
68
|
-
{
|
|
69
|
-
"plugins": [
|
|
70
|
-
"opencode-working-memory"
|
|
71
|
-
]
|
|
72
|
-
}
|
|
73
|
-
```
|
|
19
|
+
1. Edit `~/.config/opencode/opencode.json`
|
|
20
|
+
2. Add `"opencode-working-memory"` to the `plugin` array
|
|
21
|
+
3. Verify the config file was saved correctly
|
|
74
22
|
|
|
75
23
|
## Verification
|
|
76
24
|
|
|
77
|
-
After
|
|
25
|
+
After restarting OpenCode, ask your agent:
|
|
78
26
|
|
|
79
27
|
```
|
|
80
|
-
|
|
28
|
+
Use core_memory_read to show me what you remember
|
|
81
29
|
```
|
|
82
30
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
## Configuration
|
|
86
|
-
|
|
87
|
-
The plugin works out-of-the-box with sensible defaults. For advanced configuration, see [configuration.md](./configuration.md).
|
|
31
|
+
If the tool responds, the plugin is active.
|
|
88
32
|
|
|
89
33
|
## Troubleshooting
|
|
90
34
|
|
|
@@ -92,10 +36,10 @@ The plugin works out-of-the-box with sensible defaults. For advanced configurati
|
|
|
92
36
|
|
|
93
37
|
**Symptom**: No `core_memory_update` tool available
|
|
94
38
|
|
|
95
|
-
**Solution**:
|
|
96
|
-
1. Check
|
|
97
|
-
2.
|
|
98
|
-
3.
|
|
39
|
+
**Solution**:
|
|
40
|
+
1. Check `~/.config/opencode/opencode.json` uses `"plugin"` (not `"plugins"`)
|
|
41
|
+
2. Restart OpenCode to trigger automatic installation
|
|
42
|
+
3. Check OpenCode logs for any download errors
|
|
99
43
|
|
|
100
44
|
### Memory Files Not Created
|
|
101
45
|
|
|
@@ -103,26 +47,22 @@ The plugin works out-of-the-box with sensible defaults. For advanced configurati
|
|
|
103
47
|
|
|
104
48
|
**Solution**:
|
|
105
49
|
1. Ensure OpenCode has write permissions in project directory
|
|
106
|
-
2.
|
|
107
|
-
3. Trigger memory operations (e.g., use `core_memory_update` tool)
|
|
50
|
+
2. Trigger memory operations (e.g., use `core_memory_update` tool)
|
|
108
51
|
|
|
109
52
|
### Type Errors During Development
|
|
110
53
|
|
|
111
|
-
**Symptom**: TypeScript errors when modifying plugin
|
|
54
|
+
**Symptom**: TypeScript errors when modifying the plugin source
|
|
112
55
|
|
|
113
56
|
**Solution**:
|
|
114
|
-
1.
|
|
115
|
-
2. Run
|
|
57
|
+
1. Run `npm install` to install dev dependencies
|
|
58
|
+
2. Run `npm run typecheck` to check for errors
|
|
116
59
|
3. See [AGENTS.md](../AGENTS.md) for code style guidelines
|
|
117
60
|
|
|
118
61
|
## Uninstallation
|
|
119
62
|
|
|
120
|
-
|
|
121
|
-
cd .opencode
|
|
122
|
-
npm uninstall opencode-working-memory
|
|
123
|
-
```
|
|
63
|
+
Remove `"opencode-working-memory"` from the `plugin` array in `~/.config/opencode/opencode.json`.
|
|
124
64
|
|
|
125
|
-
|
|
65
|
+
Memory files in `.opencode/memory-*` will persist unless manually deleted.
|
|
126
66
|
|
|
127
67
|
## Next Steps
|
|
128
68
|
|
package/index.ts
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Working Memory Plugin for OpenCode
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Four-tier memory architecture:
|
|
5
5
|
* 1. Core Memory - Persistent goal/progress/context blocks (always in-context)
|
|
6
6
|
* 2. Working Memory - Auto-managed session-relevant information
|
|
7
7
|
* 3. Smart Pruning - Content-aware tool output compression
|
|
8
8
|
* 4. Memory Pressure Monitoring - Context usage tracking with adaptive warnings
|
|
9
|
-
*
|
|
10
|
-
* Phase 1: Core Memory Foundation (MVP) - โ
COMPLETED
|
|
11
|
-
* Phase 2: Smart Pruning System - โ
COMPLETED
|
|
12
|
-
* Phase 3: Working Memory Auto-Management - โ
COMPLETED
|
|
13
|
-
* Phase 4: Memory Pressure Monitoring - โ
COMPLETED
|
|
14
9
|
*/
|
|
15
10
|
|
|
16
11
|
import type { Plugin } from "@opencode-ai/plugin";
|
|
@@ -46,7 +41,7 @@ const CORE_MEMORY_LIMITS = {
|
|
|
46
41
|
};
|
|
47
42
|
|
|
48
43
|
// ============================================================================
|
|
49
|
-
//
|
|
44
|
+
// Smart Pruning Types
|
|
50
45
|
// ============================================================================
|
|
51
46
|
|
|
52
47
|
type PruningStrategy =
|
|
@@ -72,7 +67,7 @@ type CachedToolOutput = {
|
|
|
72
67
|
};
|
|
73
68
|
|
|
74
69
|
// ============================================================================
|
|
75
|
-
//
|
|
70
|
+
// Working Memory Types (Slot-based Architecture)
|
|
76
71
|
// ============================================================================
|
|
77
72
|
|
|
78
73
|
type WorkingMemory = {
|
|
@@ -128,7 +123,7 @@ const WORKING_MEMORY_LIMITS = {
|
|
|
128
123
|
};
|
|
129
124
|
|
|
130
125
|
// ============================================================================
|
|
131
|
-
// Storage Governance
|
|
126
|
+
// Storage Governance
|
|
132
127
|
// ============================================================================
|
|
133
128
|
|
|
134
129
|
const STORAGE_GOVERNANCE = {
|
|
@@ -138,7 +133,7 @@ const STORAGE_GOVERNANCE = {
|
|
|
138
133
|
};
|
|
139
134
|
|
|
140
135
|
// ============================================================================
|
|
141
|
-
//
|
|
136
|
+
// Memory Pressure Monitoring
|
|
142
137
|
// ============================================================================
|
|
143
138
|
|
|
144
139
|
type PressureLevel = "safe" | "moderate" | "high";
|
|
@@ -169,7 +164,6 @@ type ModelPressureInfo = {
|
|
|
169
164
|
updatedAt: string;
|
|
170
165
|
};
|
|
171
166
|
|
|
172
|
-
// Compaction tracking (preserved from Phase 4 initial work)
|
|
173
167
|
type CompactionLog = {
|
|
174
168
|
sessionID: string;
|
|
175
169
|
compactionCount: number;
|
|
@@ -501,12 +495,11 @@ async function updateCoreMemoryBlock(
|
|
|
501
495
|
}
|
|
502
496
|
|
|
503
497
|
// ============================================================================
|
|
504
|
-
// Storage Governance Functions
|
|
498
|
+
// Storage Governance Functions
|
|
505
499
|
// ============================================================================
|
|
506
500
|
|
|
507
501
|
/**
|
|
508
|
-
*
|
|
509
|
-
* Called when session.deleted event is received
|
|
502
|
+
* Clean up all artifacts for a deleted session.
|
|
510
503
|
*/
|
|
511
504
|
async function cleanupSessionArtifacts(
|
|
512
505
|
directory: string,
|
|
@@ -532,9 +525,9 @@ async function cleanupSessionArtifacts(
|
|
|
532
525
|
}
|
|
533
526
|
|
|
534
527
|
/**
|
|
535
|
-
*
|
|
536
|
-
*
|
|
537
|
-
* Returns number of files deleted
|
|
528
|
+
* Sweep tool-output cache for a session.
|
|
529
|
+
* Removes files older than TTL and enforces max file count.
|
|
530
|
+
* Returns number of files deleted.
|
|
538
531
|
*/
|
|
539
532
|
async function sweepToolOutputCache(
|
|
540
533
|
directory: string,
|
|
@@ -606,7 +599,7 @@ async function sweepToolOutputCache(
|
|
|
606
599
|
}
|
|
607
600
|
|
|
608
601
|
// ============================================================================
|
|
609
|
-
//
|
|
602
|
+
// Smart Pruning System
|
|
610
603
|
// ============================================================================
|
|
611
604
|
|
|
612
605
|
/**
|
|
@@ -775,7 +768,7 @@ async function getCachedToolOutput(
|
|
|
775
768
|
}
|
|
776
769
|
|
|
777
770
|
// ============================================================================
|
|
778
|
-
//
|
|
771
|
+
// Working Memory Auto-Management
|
|
779
772
|
// ============================================================================
|
|
780
773
|
|
|
781
774
|
/**
|
|
@@ -1057,9 +1050,7 @@ function getTopItemsForPrompt(
|
|
|
1057
1050
|
}
|
|
1058
1051
|
|
|
1059
1052
|
/**
|
|
1060
|
-
* Compress file paths to save space in system prompt
|
|
1061
|
-
* /Users/sd_wo/opencode/packages/opencode/src/foo.ts โ ~/opencode/pkg/opencode/src/foo.ts
|
|
1062
|
-
* /Users/sd_wo/work/opencode-plugins/.opencode/plugins/foo.ts โ ~/work/oc-plugins/.opencode/plugins/foo.ts
|
|
1053
|
+
* Compress file paths to save space in system prompt.
|
|
1063
1054
|
*/
|
|
1064
1055
|
function compressPath(content: string): string {
|
|
1065
1056
|
const homeDir = process.env.HOME || '/Users/' + (process.env.USER || 'user');
|
|
@@ -1145,7 +1136,7 @@ function getWorkingMemoryItemCount(memory: WorkingMemory): number {
|
|
|
1145
1136
|
}
|
|
1146
1137
|
|
|
1147
1138
|
// ============================================================================
|
|
1148
|
-
//
|
|
1139
|
+
// Compaction Tracking
|
|
1149
1140
|
// ============================================================================
|
|
1150
1141
|
|
|
1151
1142
|
/**
|
|
@@ -1207,14 +1198,13 @@ async function recordCompaction(
|
|
|
1207
1198
|
// ============================================================================
|
|
1208
1199
|
|
|
1209
1200
|
/**
|
|
1210
|
-
* Calculate usable tokens using OpenCode's
|
|
1211
|
-
* Reference: packages/opencode/src/session/compaction.ts:32-48
|
|
1201
|
+
* Calculate usable tokens using OpenCode's compaction formula.
|
|
1212
1202
|
*/
|
|
1213
1203
|
function calculateUsableTokens(model: {
|
|
1214
1204
|
limit: { context: number; input?: number; output: number };
|
|
1215
1205
|
}): number {
|
|
1216
|
-
const OUTPUT_TOKEN_MAX = 32_000;
|
|
1217
|
-
const COMPACTION_BUFFER = 20_000;
|
|
1206
|
+
const OUTPUT_TOKEN_MAX = 32_000;
|
|
1207
|
+
const COMPACTION_BUFFER = 20_000;
|
|
1218
1208
|
|
|
1219
1209
|
const maxOutputTokens = Math.min(
|
|
1220
1210
|
model.limit.output || OUTPUT_TOKEN_MAX,
|
|
@@ -1222,7 +1212,6 @@ function calculateUsableTokens(model: {
|
|
|
1222
1212
|
);
|
|
1223
1213
|
const reserved = Math.min(COMPACTION_BUFFER, maxOutputTokens);
|
|
1224
1214
|
|
|
1225
|
-
// Match compaction.ts:42-47
|
|
1226
1215
|
const usable = model.limit.input
|
|
1227
1216
|
? model.limit.input - reserved
|
|
1228
1217
|
: model.limit.context - maxOutputTokens;
|
|
@@ -1231,11 +1220,7 @@ function calculateUsableTokens(model: {
|
|
|
1231
1220
|
}
|
|
1232
1221
|
|
|
1233
1222
|
/**
|
|
1234
|
-
* Calculate pressure level based on current tokens and usable limit
|
|
1235
|
-
*
|
|
1236
|
-
* Thresholds:
|
|
1237
|
-
* - 0.75 (75%): moderate - show reminder in prompt
|
|
1238
|
-
* - 0.9 (90%): high - send intervention message
|
|
1223
|
+
* Calculate pressure level based on current tokens and usable limit.
|
|
1239
1224
|
*/
|
|
1240
1225
|
function calculatePressureLevel(
|
|
1241
1226
|
currentTokens: number,
|
|
@@ -1334,18 +1319,14 @@ async function loadModelPressureInfo(
|
|
|
1334
1319
|
}
|
|
1335
1320
|
|
|
1336
1321
|
/**
|
|
1337
|
-
* Calculate total tokens by querying OpenCode's session database
|
|
1338
|
-
* This is more reliable than relying on hook-provided messages
|
|
1339
|
-
*
|
|
1340
|
-
* Note: Only looks at last 10 messages to avoid stale data from before compaction
|
|
1322
|
+
* Calculate total tokens by querying OpenCode's session database.
|
|
1341
1323
|
*/
|
|
1342
1324
|
async function calculateTotalTokensFromDB(sessionID: string): Promise<number> {
|
|
1343
1325
|
try {
|
|
1344
1326
|
const { execSync } = await import("child_process");
|
|
1345
1327
|
const dbPath = join(process.env.HOME || "~", ".local/share/opencode/opencode.db");
|
|
1346
1328
|
|
|
1347
|
-
// Get tokens.total from most recent assistant message
|
|
1348
|
-
// Use MAX to handle edge cases, but limit to recent messages to avoid stale pre-compaction data
|
|
1329
|
+
// Get tokens.total from most recent assistant message
|
|
1349
1330
|
const query = `
|
|
1350
1331
|
SELECT json_extract(data, '$.tokens.total') as total
|
|
1351
1332
|
FROM message
|
|
@@ -1365,11 +1346,7 @@ async function calculateTotalTokensFromDB(sessionID: string): Promise<number> {
|
|
|
1365
1346
|
}
|
|
1366
1347
|
|
|
1367
1348
|
/**
|
|
1368
|
-
* Generate pressure warning text for system prompt injection
|
|
1369
|
-
*
|
|
1370
|
-
* Design principles:
|
|
1371
|
-
* - MODERATE (75%): gentle nudge, no interruption
|
|
1372
|
-
* - HIGH (90%): actionable commands, pause and persist state
|
|
1349
|
+
* Generate pressure warning text for system prompt injection.
|
|
1373
1350
|
*/
|
|
1374
1351
|
function generatePressureWarning(info: ModelPressureInfo): string {
|
|
1375
1352
|
const { current, calculated } = info;
|
|
@@ -1387,13 +1364,8 @@ function generatePressureWarning(info: ModelPressureInfo): string {
|
|
|
1387
1364
|
}
|
|
1388
1365
|
|
|
1389
1366
|
/**
|
|
1390
|
-
* Send proactive intervention message when HIGH pressure
|
|
1391
|
-
*
|
|
1392
|
-
* This sends an independent system message to the session immediately, so the agent
|
|
1393
|
-
* receives the task in the queue without interrupting current work. The agent will
|
|
1394
|
-
* process it automatically when available.
|
|
1395
|
-
*
|
|
1396
|
-
* Design: Use promptAsync() which returns 204 immediately, non-blocking.
|
|
1367
|
+
* Send a proactive intervention message when HIGH pressure (90%) is detected.
|
|
1368
|
+
* Uses promptAsync() which returns immediately (non-blocking).
|
|
1397
1369
|
*/
|
|
1398
1370
|
async function sendPressureInterventionMessage(
|
|
1399
1371
|
client: any,
|
|
@@ -1421,17 +1393,14 @@ REQUIRED ACTIONS:
|
|
|
1421
1393
|
After completing these actions, you may resume your current task.`;
|
|
1422
1394
|
|
|
1423
1395
|
try {
|
|
1424
|
-
// Use promptAsync to send message without waiting for response
|
|
1425
1396
|
await client.session.promptAsync({
|
|
1426
1397
|
path: { id: sessionID },
|
|
1427
1398
|
body: {
|
|
1428
1399
|
parts: [{
|
|
1429
1400
|
type: "text",
|
|
1430
|
-
// Send actionable content directly (not log-style placeholder)
|
|
1431
1401
|
text: systemPrompt,
|
|
1432
1402
|
}],
|
|
1433
|
-
|
|
1434
|
-
noReply: false, // We want agent to respond with actions
|
|
1403
|
+
noReply: false,
|
|
1435
1404
|
},
|
|
1436
1405
|
});
|
|
1437
1406
|
} catch (error) {
|
|
@@ -1440,36 +1409,32 @@ After completing these actions, you may resume your current task.`;
|
|
|
1440
1409
|
}
|
|
1441
1410
|
|
|
1442
1411
|
/**
|
|
1443
|
-
* Get pressure-aware pruning config based on current memory pressure
|
|
1444
|
-
* HYPER-AGGRESSIVE MODE: pressure >= 0.90 enforces strict limits
|
|
1412
|
+
* Get pressure-aware pruning config based on current memory pressure.
|
|
1445
1413
|
*/
|
|
1446
1414
|
function getPressureAwarePruningConfig(pressure: number): {
|
|
1447
1415
|
maxLines: number;
|
|
1448
1416
|
maxChars: number;
|
|
1449
1417
|
aggressiveTruncation: boolean;
|
|
1450
1418
|
} {
|
|
1451
|
-
// HIGH (>= 90%): Hyper-Aggressive Mode
|
|
1452
1419
|
if (pressure >= 0.90) {
|
|
1453
1420
|
return {
|
|
1454
|
-
maxLines: 2000,
|
|
1455
|
-
maxChars: 100_000,
|
|
1456
|
-
aggressiveTruncation: true,
|
|
1421
|
+
maxLines: 2000,
|
|
1422
|
+
maxChars: 100_000,
|
|
1423
|
+
aggressiveTruncation: true,
|
|
1457
1424
|
};
|
|
1458
1425
|
}
|
|
1459
1426
|
|
|
1460
|
-
// MODERATE (>= 75%): Aggressive Mode
|
|
1461
1427
|
if (pressure >= 0.75) {
|
|
1462
1428
|
return {
|
|
1463
1429
|
maxLines: 5000,
|
|
1464
|
-
maxChars: 200_000,
|
|
1430
|
+
maxChars: 200_000,
|
|
1465
1431
|
aggressiveTruncation: true,
|
|
1466
1432
|
};
|
|
1467
1433
|
}
|
|
1468
1434
|
|
|
1469
|
-
// SAFE (< 75%): Normal Mode
|
|
1470
1435
|
return {
|
|
1471
1436
|
maxLines: 10_000,
|
|
1472
|
-
maxChars: 400_000,
|
|
1437
|
+
maxChars: 400_000,
|
|
1473
1438
|
aggressiveTruncation: false,
|
|
1474
1439
|
};
|
|
1475
1440
|
}
|
|
@@ -1524,34 +1489,14 @@ export default async function WorkingMemoryPlugin(
|
|
|
1524
1489
|
const { directory, client } = input;
|
|
1525
1490
|
|
|
1526
1491
|
return {
|
|
1527
|
-
//
|
|
1528
|
-
//
|
|
1529
|
-
// Phase 4: Inject Memory Pressure Warnings & Calculate Tokens from DB
|
|
1530
|
-
// Phase 4.5: Proactive Pressure Intervention (NEW)
|
|
1531
|
-
// Phase 5: Core Memory Usage Guidelines (AGENTS.md Enhancement)
|
|
1532
|
-
//
|
|
1533
|
-
// Dual-System Approach:
|
|
1534
|
-
// 1. PASSIVE WARNING (existing): Injected into next turn's system prompt
|
|
1535
|
-
// - Always present as reminder in system context
|
|
1536
|
-
// - 1-turn delay but persistent
|
|
1537
|
-
//
|
|
1538
|
-
// 2. PROACTIVE INTERVENTION (new): Immediate async message sent to queue
|
|
1539
|
-
// - No delay, sent immediately when HIGH (90%) detected
|
|
1540
|
-
// - Agent processes when available (non-blocking)
|
|
1541
|
-
// - Only sent when pressure level increases (avoids spam)
|
|
1542
|
-
//
|
|
1543
|
-
// 3. USAGE GUIDELINES (new): Injected after AGENTS.md, before core_memory
|
|
1544
|
-
// - Teaches agent how to use core_memory blocks correctly
|
|
1545
|
-
// - Prevents storing API docs/type definitions in memory
|
|
1546
|
-
// - Ensures goal/progress/context stay focused on current task
|
|
1547
|
-
// ========================================================================
|
|
1492
|
+
// Inject core memory usage guidelines, pressure warnings, core memory,
|
|
1493
|
+
// and working memory into the system prompt each turn.
|
|
1548
1494
|
"experimental.chat.system.transform": async (hookInput, output) => {
|
|
1549
1495
|
const { sessionID, model } = hookInput;
|
|
1550
1496
|
if (!sessionID) return;
|
|
1551
1497
|
|
|
1552
|
-
//
|
|
1553
|
-
//
|
|
1554
|
-
// Inserted early so it's read before agent sees <core_memory> block
|
|
1498
|
+
// Inject core memory usage guidelines.
|
|
1499
|
+
// Inserted before <core_memory> so the agent reads guidance first.
|
|
1555
1500
|
const coreMemoryGuidelines = `
|
|
1556
1501
|
# Core Memory Usage Guidelines
|
|
1557
1502
|
|
|
@@ -1613,8 +1558,8 @@ The Working Memory Plugin provides persistent core_memory blocks. **USE THEM COR
|
|
|
1613
1558
|
|
|
1614
1559
|
output.system.push(coreMemoryGuidelines);
|
|
1615
1560
|
|
|
1616
|
-
//
|
|
1617
|
-
// Skip
|
|
1561
|
+
// Check for memory pressure and inject warning into system prompt.
|
|
1562
|
+
// Skip if model just changed (avoids false alarms with different limits).
|
|
1618
1563
|
const prevPressure = await loadModelPressureInfo(directory, sessionID);
|
|
1619
1564
|
const modelChanged = model && prevPressure && prevPressure.modelID !== model.id;
|
|
1620
1565
|
|
|
@@ -1625,7 +1570,7 @@ The Working Memory Plugin provides persistent core_memory blocks. **USE THEM COR
|
|
|
1625
1570
|
}
|
|
1626
1571
|
}
|
|
1627
1572
|
|
|
1628
|
-
//
|
|
1573
|
+
// Calculate current token usage from DB and update pressure info.
|
|
1629
1574
|
if (model) {
|
|
1630
1575
|
const totalTokens = await calculateTotalTokensFromDB(sessionID);
|
|
1631
1576
|
|
|
@@ -1640,14 +1585,12 @@ The Working Memory Plugin provides persistent core_memory blocks. **USE THEM COR
|
|
|
1640
1585
|
totalTokens
|
|
1641
1586
|
);
|
|
1642
1587
|
|
|
1643
|
-
// Save for next turn's warning injection
|
|
1588
|
+
// Save for next turn's warning injection.
|
|
1644
1589
|
await saveModelPressureInfo(directory, updatedPressure);
|
|
1645
1590
|
|
|
1646
|
-
//
|
|
1647
|
-
// This is better than waiting for next turn's passive warning
|
|
1648
|
-
// The message goes into the queue and agent processes it when available
|
|
1591
|
+
// Send proactive intervention if pressure just crossed into HIGH.
|
|
1649
1592
|
if (updatedPressure.current.level === "high") {
|
|
1650
|
-
// Only send if pressure
|
|
1593
|
+
// Only send if pressure just escalated (avoid repeated spam).
|
|
1651
1594
|
const shouldSend = !prevPressure ||
|
|
1652
1595
|
prevPressure.current.level === "safe" ||
|
|
1653
1596
|
prevPressure.current.level === "moderate";
|
|
@@ -1658,7 +1601,7 @@ The Working Memory Plugin provides persistent core_memory blocks. **USE THEM COR
|
|
|
1658
1601
|
}
|
|
1659
1602
|
}
|
|
1660
1603
|
|
|
1661
|
-
//
|
|
1604
|
+
// Core memory
|
|
1662
1605
|
const coreMemory = await loadCoreMemory(directory, sessionID);
|
|
1663
1606
|
if (coreMemory) {
|
|
1664
1607
|
const hasContent =
|
|
@@ -1672,7 +1615,7 @@ The Working Memory Plugin provides persistent core_memory blocks. **USE THEM COR
|
|
|
1672
1615
|
}
|
|
1673
1616
|
}
|
|
1674
1617
|
|
|
1675
|
-
//
|
|
1618
|
+
// Working memory
|
|
1676
1619
|
const workingMemory = await loadWorkingMemory(directory, sessionID);
|
|
1677
1620
|
if (workingMemory && getWorkingMemoryItemCount(workingMemory) > 0) {
|
|
1678
1621
|
const workingPrompt = renderWorkingMemoryPrompt(workingMemory);
|
|
@@ -1682,15 +1625,11 @@ The Working Memory Plugin provides persistent core_memory blocks. **USE THEM COR
|
|
|
1682
1625
|
}
|
|
1683
1626
|
},
|
|
1684
1627
|
|
|
1685
|
-
//
|
|
1686
|
-
// Phase 2 & 3: Cache Tool Outputs and Auto-Extract to Working Memory
|
|
1687
|
-
// Storage Governance Layer 2: Tool Output Cache Sweep Trigger
|
|
1688
|
-
// ========================================================================
|
|
1628
|
+
// Cache tool outputs, auto-extract to working memory, and sweep cache periodically.
|
|
1689
1629
|
"tool.execute.after": async (hookInput, hookOutput) => {
|
|
1690
1630
|
const { sessionID, callID, tool: toolName, args } = hookInput;
|
|
1691
1631
|
const { output: toolOutput } = hookOutput;
|
|
1692
1632
|
|
|
1693
|
-
// Phase 2: Cache the full output for later smart pruning
|
|
1694
1633
|
await cacheToolOutput(directory, {
|
|
1695
1634
|
callID,
|
|
1696
1635
|
sessionID,
|
|
@@ -1699,22 +1638,19 @@ The Working Memory Plugin provides persistent core_memory blocks. **USE THEM COR
|
|
|
1699
1638
|
timestamp: Date.now(),
|
|
1700
1639
|
});
|
|
1701
1640
|
|
|
1702
|
-
// Phase 3: Auto-extract to working memory
|
|
1703
1641
|
const extractedItems = extractFromToolOutput(toolName, toolOutput);
|
|
1704
1642
|
for (const item of extractedItems) {
|
|
1705
1643
|
await addToWorkingMemory(directory, sessionID, item);
|
|
1706
1644
|
}
|
|
1707
1645
|
|
|
1708
|
-
//
|
|
1646
|
+
// Sweep tool-output cache every N tool calls.
|
|
1709
1647
|
const memory = await loadWorkingMemory(directory, sessionID);
|
|
1710
1648
|
if (memory && memory.eventCounter % STORAGE_GOVERNANCE.sweepInterval === 0) {
|
|
1711
1649
|
await sweepToolOutputCache(directory, sessionID);
|
|
1712
1650
|
}
|
|
1713
1651
|
},
|
|
1714
1652
|
|
|
1715
|
-
//
|
|
1716
|
-
// Phase 2: Apply Smart Pruning to Messages (Pressure-Aware)
|
|
1717
|
-
// ========================================================================
|
|
1653
|
+
// Apply smart pruning to compacted tool outputs (pressure-aware).
|
|
1718
1654
|
"experimental.chat.messages.transform": async (hookInput, output) => {
|
|
1719
1655
|
const sessionID = output.messages[0]?.info?.sessionID || "";
|
|
1720
1656
|
|
|
@@ -1754,11 +1690,8 @@ The Working Memory Plugin provides persistent core_memory blocks. **USE THEM COR
|
|
|
1754
1690
|
}
|
|
1755
1691
|
},
|
|
1756
1692
|
|
|
1757
|
-
//
|
|
1758
|
-
// Storage Governance Layer 1: Session Deletion Event Handler
|
|
1759
|
-
// ========================================================================
|
|
1693
|
+
// Clean up all session artifacts on session deletion.
|
|
1760
1694
|
event: async ({ event }) => {
|
|
1761
|
-
// Listen for session.deleted events and cleanup all artifacts
|
|
1762
1695
|
if (event.type === "session.deleted") {
|
|
1763
1696
|
const sessionID = event.properties?.info?.id;
|
|
1764
1697
|
if (sessionID) {
|
|
@@ -1767,9 +1700,7 @@ The Working Memory Plugin provides persistent core_memory blocks. **USE THEM COR
|
|
|
1767
1700
|
}
|
|
1768
1701
|
},
|
|
1769
1702
|
|
|
1770
|
-
//
|
|
1771
|
-
// Phase 4: Preserve State Before Compaction
|
|
1772
|
-
// ========================================================================
|
|
1703
|
+
// Preserve working memory state before compaction.
|
|
1773
1704
|
"experimental.session.compacting": async (hookInput, output) => {
|
|
1774
1705
|
const { sessionID } = hookInput;
|
|
1775
1706
|
|
|
@@ -1808,7 +1739,7 @@ The Working Memory Plugin provides persistent core_memory blocks. **USE THEM COR
|
|
|
1808
1739
|
}
|
|
1809
1740
|
}
|
|
1810
1741
|
|
|
1811
|
-
//
|
|
1742
|
+
// Inject pending OpenCode todos into compaction context.
|
|
1812
1743
|
try {
|
|
1813
1744
|
const { execSync } = await import("child_process");
|
|
1814
1745
|
const dbPath = join(process.env.HOME || "~", ".local/share/opencode/opencode.db");
|
|
@@ -1849,9 +1780,7 @@ The Working Memory Plugin provides persistent core_memory blocks. **USE THEM COR
|
|
|
1849
1780
|
}
|
|
1850
1781
|
},
|
|
1851
1782
|
|
|
1852
|
-
// ========================================================================
|
|
1853
1783
|
// Tools
|
|
1854
|
-
// ========================================================================
|
|
1855
1784
|
tool: {
|
|
1856
1785
|
core_memory_update: tool({
|
|
1857
1786
|
description: `Update persistent core memory blocks that survive compaction.
|
package/package.json
CHANGED