opencode-working-memory 1.0.0 โ 1.1.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/README.md +25 -61
- package/docs/configuration.md +2 -3
- package/docs/installation.md +22 -82
- package/index.ts +148 -194
- 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');
|
|
@@ -1131,7 +1122,7 @@ Recent session context (auto-managed, sorted by relevance):
|
|
|
1131
1122
|
|
|
1132
1123
|
${sections.join("\n\n")}
|
|
1133
1124
|
|
|
1134
|
-
(${totalItems} items shown
|
|
1125
|
+
(${totalItems} items shown)
|
|
1135
1126
|
</working_memory>
|
|
1136
1127
|
`.trim();
|
|
1137
1128
|
}
|
|
@@ -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
|
}
|
|
@@ -1498,18 +1463,9 @@ ${context.value || "[No project context set - add relevant file paths, conventio
|
|
|
1498
1463
|
</context>
|
|
1499
1464
|
|
|
1500
1465
|
IMPORTANT: These blocks persist across conversation resets and compaction.
|
|
1501
|
-
Update them regularly using core_memory_update tool
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
- Important project context is discovered (file structures, patterns, conventions)
|
|
1505
|
-
|
|
1506
|
-
When memory blocks approach their character limits, compress or rephrase content.
|
|
1507
|
-
|
|
1508
|
-
**Usage Discipline** (see Core Memory Usage Guidelines above for details):
|
|
1509
|
-
- goal: ONE specific task, not project-wide goals
|
|
1510
|
-
- progress: Checklist format, NO line numbers/commit hashes/API signatures
|
|
1511
|
-
- context: ONLY files you're currently working on, NO type definitions/function signatures
|
|
1512
|
-
- NEVER store: API docs, library types, function signatures (read source instead)
|
|
1466
|
+
Update them regularly using core_memory_update tool. When blocks approach their character limits, compress or rephrase content.
|
|
1467
|
+
|
|
1468
|
+
To mark decisions for automatic capture into working memory, write inline: [Decision: chose X over Y because Z]
|
|
1513
1469
|
</core_memory>
|
|
1514
1470
|
`.trim();
|
|
1515
1471
|
}
|
|
@@ -1523,98 +1479,38 @@ export default async function WorkingMemoryPlugin(
|
|
|
1523
1479
|
): Promise<ReturnType<Plugin>> {
|
|
1524
1480
|
const { directory, client } = input;
|
|
1525
1481
|
|
|
1482
|
+
// Cache for sub-agent detection โ avoids repeated API calls per session.
|
|
1483
|
+
// Maps sessionID โ parentID (string) or null (root session).
|
|
1484
|
+
const sessionParentCache = new Map<string, string | null>();
|
|
1485
|
+
|
|
1486
|
+
async function isSubAgent(sessionID: string): Promise<boolean> {
|
|
1487
|
+
if (sessionParentCache.has(sessionID)) {
|
|
1488
|
+
return sessionParentCache.get(sessionID) !== null;
|
|
1489
|
+
}
|
|
1490
|
+
try {
|
|
1491
|
+
const result = await client.session.get({ path: { id: sessionID } });
|
|
1492
|
+
const parentID = result.data?.parentID ?? null;
|
|
1493
|
+
sessionParentCache.set(sessionID, parentID);
|
|
1494
|
+
return parentID !== null;
|
|
1495
|
+
} catch {
|
|
1496
|
+
// If we can't determine, assume it's NOT a sub-agent (safe default).
|
|
1497
|
+
sessionParentCache.set(sessionID, null);
|
|
1498
|
+
return false;
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1526
1502
|
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
|
-
// ========================================================================
|
|
1503
|
+
// Inject pressure warnings, core memory, and working memory into the system prompt each turn.
|
|
1504
|
+
// Core memory usage guidelines are in the core_memory_update tool description instead.
|
|
1548
1505
|
"experimental.chat.system.transform": async (hookInput, output) => {
|
|
1549
1506
|
const { sessionID, model } = hookInput;
|
|
1550
1507
|
if (!sessionID) return;
|
|
1551
1508
|
|
|
1552
|
-
//
|
|
1553
|
-
|
|
1554
|
-
// Inserted early so it's read before agent sees <core_memory> block
|
|
1555
|
-
const coreMemoryGuidelines = `
|
|
1556
|
-
# Core Memory Usage Guidelines
|
|
1557
|
-
|
|
1558
|
-
The Working Memory Plugin provides persistent core_memory blocks. **USE THEM CORRECTLY**:
|
|
1559
|
-
|
|
1560
|
-
## goal block (1000 chars)
|
|
1561
|
-
**Purpose**: ONE specific task you're working on RIGHT NOW
|
|
1562
|
-
|
|
1563
|
-
โ
**GOOD Examples**:
|
|
1564
|
-
- "Fix pruning bug where items with relevanceScore <0.01 are incorrectly excluded"
|
|
1565
|
-
- "Add new tool: working_memory_search to query pool items by keyword"
|
|
1566
|
-
- "Investigate why pressure warnings not showing in system prompt"
|
|
1567
|
-
|
|
1568
|
-
โ **BAD Examples**:
|
|
1569
|
-
- "Complete Phase 1-4 development and testing" (too broad, likely already done)
|
|
1570
|
-
- "Build a working memory system for OpenCode" (project-level goal, not task-level)
|
|
1571
|
-
|
|
1572
|
-
## progress block (2000 chars)
|
|
1573
|
-
**Purpose**: Checklist of done/in-progress/blocked items + key decisions
|
|
1574
|
-
|
|
1575
|
-
โ
**GOOD Examples**:
|
|
1576
|
-
- "โ
Found bug in applyDecay() line 856\\nโณ Testing fix with gamma=0.85\\nโ Need to verify edge case: score=0"
|
|
1577
|
-
- "โ
Phase 1-3 complete\\nโณ Phase 4 intervention testing\\nโ ๏ธ BLOCKED: Need promptAsync docs"
|
|
1578
|
-
|
|
1579
|
-
โ **BAD Examples**:
|
|
1580
|
-
- "Function sendPressureInterventionMessage() @ working-memory.ts:L1286-1354" (line numbers useless after edits)
|
|
1581
|
-
- "Commit 2f42f1b implemented promptAsync integration" (commit hash irrelevant)
|
|
1582
|
-
- "API: client.session.promptAsync({ path: {id}, body: {...} })" (API signature, not progress)
|
|
1583
|
-
|
|
1584
|
-
## context block (1500 chars)
|
|
1585
|
-
**Purpose**: Files you're CURRENTLY editing + key patterns/conventions
|
|
1586
|
-
|
|
1587
|
-
โ
**GOOD Examples**:
|
|
1588
|
-
- "Editing: .opencode/plugins/working-memory.ts (main plugin, 1706 lines)\\nRelated: WORKING_MEMORY.md, TEST_PHASE4.md"
|
|
1589
|
-
- "Key paths: .opencode/memory-core/ (persistent blocks), memory-working/ (session data)"
|
|
1590
|
-
- "Pattern: All async file ops use mkdir({recursive:true}) before writeFile"
|
|
1591
|
-
|
|
1592
|
-
โ **BAD Examples**:
|
|
1593
|
-
- "OpenCode SDK types: TextPartInput = { type: 'text', text: string, synthetic?: boolean }" (type definition)
|
|
1594
|
-
- "Function signature: async function loadCoreMemory(directory: string, sessionID: string): Promise<CoreMemory | null>" (function signature)
|
|
1595
|
-
- "Method client.session.promptAsync() returns 204 No Content" (API behavior, read docs instead)
|
|
1596
|
-
|
|
1597
|
-
## โ ๏ธ NEVER Store in Core Memory
|
|
1598
|
-
- API documentation (read source/docs when needed)
|
|
1599
|
-
- Type definitions from libraries (import them)
|
|
1600
|
-
- Function signatures (read source code)
|
|
1601
|
-
- Implementation details (belong in code comments)
|
|
1602
|
-
- Completed goals (clear them immediately)
|
|
1603
|
-
|
|
1604
|
-
## โ
Update Core Memory Immediately When
|
|
1605
|
-
- **Starting new task**: Clear old goal, set new specific goal
|
|
1606
|
-
- **Making progress**: Update progress checklist (keep concise)
|
|
1607
|
-
- **Switching files**: Update context with current working files
|
|
1608
|
-
- **Task completed**: Clear goal/progress, set next task
|
|
1609
|
-
- **Approaching char limit**: Compress or remove outdated info
|
|
1509
|
+
// Sub-agents are short-lived โ skip entire memory system.
|
|
1510
|
+
if (await isSubAgent(sessionID)) return;
|
|
1610
1511
|
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
output.system.push(coreMemoryGuidelines);
|
|
1615
|
-
|
|
1616
|
-
// Phase 4: Check for memory pressure and inject warning
|
|
1617
|
-
// Skip warning if model just changed (avoids false alarms with different limits)
|
|
1512
|
+
// Check for memory pressure and inject warning into system prompt.
|
|
1513
|
+
// Skip if model just changed (avoids false alarms with different limits).
|
|
1618
1514
|
const prevPressure = await loadModelPressureInfo(directory, sessionID);
|
|
1619
1515
|
const modelChanged = model && prevPressure && prevPressure.modelID !== model.id;
|
|
1620
1516
|
|
|
@@ -1625,7 +1521,7 @@ The Working Memory Plugin provides persistent core_memory blocks. **USE THEM COR
|
|
|
1625
1521
|
}
|
|
1626
1522
|
}
|
|
1627
1523
|
|
|
1628
|
-
//
|
|
1524
|
+
// Calculate current token usage from DB and update pressure info.
|
|
1629
1525
|
if (model) {
|
|
1630
1526
|
const totalTokens = await calculateTotalTokensFromDB(sessionID);
|
|
1631
1527
|
|
|
@@ -1640,14 +1536,12 @@ The Working Memory Plugin provides persistent core_memory blocks. **USE THEM COR
|
|
|
1640
1536
|
totalTokens
|
|
1641
1537
|
);
|
|
1642
1538
|
|
|
1643
|
-
// Save for next turn's warning injection
|
|
1539
|
+
// Save for next turn's warning injection.
|
|
1644
1540
|
await saveModelPressureInfo(directory, updatedPressure);
|
|
1645
1541
|
|
|
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
|
|
1542
|
+
// Send proactive intervention if pressure just crossed into HIGH.
|
|
1649
1543
|
if (updatedPressure.current.level === "high") {
|
|
1650
|
-
// Only send if pressure
|
|
1544
|
+
// Only send if pressure just escalated (avoid repeated spam).
|
|
1651
1545
|
const shouldSend = !prevPressure ||
|
|
1652
1546
|
prevPressure.current.level === "safe" ||
|
|
1653
1547
|
prevPressure.current.level === "moderate";
|
|
@@ -1658,7 +1552,7 @@ The Working Memory Plugin provides persistent core_memory blocks. **USE THEM COR
|
|
|
1658
1552
|
}
|
|
1659
1553
|
}
|
|
1660
1554
|
|
|
1661
|
-
//
|
|
1555
|
+
// Core memory
|
|
1662
1556
|
const coreMemory = await loadCoreMemory(directory, sessionID);
|
|
1663
1557
|
if (coreMemory) {
|
|
1664
1558
|
const hasContent =
|
|
@@ -1672,7 +1566,7 @@ The Working Memory Plugin provides persistent core_memory blocks. **USE THEM COR
|
|
|
1672
1566
|
}
|
|
1673
1567
|
}
|
|
1674
1568
|
|
|
1675
|
-
//
|
|
1569
|
+
// Working memory
|
|
1676
1570
|
const workingMemory = await loadWorkingMemory(directory, sessionID);
|
|
1677
1571
|
if (workingMemory && getWorkingMemoryItemCount(workingMemory) > 0) {
|
|
1678
1572
|
const workingPrompt = renderWorkingMemoryPrompt(workingMemory);
|
|
@@ -1682,15 +1576,14 @@ The Working Memory Plugin provides persistent core_memory blocks. **USE THEM COR
|
|
|
1682
1576
|
}
|
|
1683
1577
|
},
|
|
1684
1578
|
|
|
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
|
-
// ========================================================================
|
|
1579
|
+
// Cache tool outputs, auto-extract to working memory, and sweep cache periodically.
|
|
1689
1580
|
"tool.execute.after": async (hookInput, hookOutput) => {
|
|
1690
1581
|
const { sessionID, callID, tool: toolName, args } = hookInput;
|
|
1691
1582
|
const { output: toolOutput } = hookOutput;
|
|
1692
1583
|
|
|
1693
|
-
//
|
|
1584
|
+
// Sub-agents don't need working memory tracking.
|
|
1585
|
+
if (await isSubAgent(sessionID)) return;
|
|
1586
|
+
|
|
1694
1587
|
await cacheToolOutput(directory, {
|
|
1695
1588
|
callID,
|
|
1696
1589
|
sessionID,
|
|
@@ -1699,25 +1592,25 @@ The Working Memory Plugin provides persistent core_memory blocks. **USE THEM COR
|
|
|
1699
1592
|
timestamp: Date.now(),
|
|
1700
1593
|
});
|
|
1701
1594
|
|
|
1702
|
-
// Phase 3: Auto-extract to working memory
|
|
1703
1595
|
const extractedItems = extractFromToolOutput(toolName, toolOutput);
|
|
1704
1596
|
for (const item of extractedItems) {
|
|
1705
1597
|
await addToWorkingMemory(directory, sessionID, item);
|
|
1706
1598
|
}
|
|
1707
1599
|
|
|
1708
|
-
//
|
|
1600
|
+
// Sweep tool-output cache every N tool calls.
|
|
1709
1601
|
const memory = await loadWorkingMemory(directory, sessionID);
|
|
1710
1602
|
if (memory && memory.eventCounter % STORAGE_GOVERNANCE.sweepInterval === 0) {
|
|
1711
1603
|
await sweepToolOutputCache(directory, sessionID);
|
|
1712
1604
|
}
|
|
1713
1605
|
},
|
|
1714
1606
|
|
|
1715
|
-
//
|
|
1716
|
-
// Phase 2: Apply Smart Pruning to Messages (Pressure-Aware)
|
|
1717
|
-
// ========================================================================
|
|
1607
|
+
// Apply smart pruning to compacted tool outputs (pressure-aware).
|
|
1718
1608
|
"experimental.chat.messages.transform": async (hookInput, output) => {
|
|
1719
1609
|
const sessionID = output.messages[0]?.info?.sessionID || "";
|
|
1720
1610
|
|
|
1611
|
+
// Sub-agents don't need smart pruning.
|
|
1612
|
+
if (sessionID && await isSubAgent(sessionID)) return;
|
|
1613
|
+
|
|
1721
1614
|
// Load current pressure info to get pressure-aware pruning config
|
|
1722
1615
|
const currentPressure = await loadModelPressureInfo(directory, sessionID);
|
|
1723
1616
|
const pressureLevel = currentPressure?.current?.pressure || 0;
|
|
@@ -1754,11 +1647,32 @@ The Working Memory Plugin provides persistent core_memory blocks. **USE THEM COR
|
|
|
1754
1647
|
}
|
|
1755
1648
|
},
|
|
1756
1649
|
|
|
1757
|
-
//
|
|
1758
|
-
|
|
1759
|
-
|
|
1650
|
+
// Auto-capture [Decision: ...] markers from agent responses.
|
|
1651
|
+
"experimental.text.complete": async (hookInput, output) => {
|
|
1652
|
+
const { sessionID } = hookInput;
|
|
1653
|
+
if (!sessionID) return;
|
|
1654
|
+
|
|
1655
|
+
// Sub-agents are short-lived โ skip decision tracking.
|
|
1656
|
+
if (await isSubAgent(sessionID)) return;
|
|
1657
|
+
|
|
1658
|
+
// Extract all [Decision: ...] markers from the completed text.
|
|
1659
|
+
const matches = output.text.matchAll(/\[Decision:\s*([^\]]+)\]/gi);
|
|
1660
|
+
for (const match of matches) {
|
|
1661
|
+
const description = match[1].trim();
|
|
1662
|
+
if (!description) continue;
|
|
1663
|
+
|
|
1664
|
+
await addToWorkingMemory(directory, sessionID, {
|
|
1665
|
+
type: "decision",
|
|
1666
|
+
content: description,
|
|
1667
|
+
source: "auto:text",
|
|
1668
|
+
timestamp: Date.now(),
|
|
1669
|
+
mentions: 1,
|
|
1670
|
+
});
|
|
1671
|
+
}
|
|
1672
|
+
},
|
|
1673
|
+
|
|
1674
|
+
// Clean up all session artifacts on session deletion.
|
|
1760
1675
|
event: async ({ event }) => {
|
|
1761
|
-
// Listen for session.deleted events and cleanup all artifacts
|
|
1762
1676
|
if (event.type === "session.deleted") {
|
|
1763
1677
|
const sessionID = event.properties?.info?.id;
|
|
1764
1678
|
if (sessionID) {
|
|
@@ -1767,12 +1681,13 @@ The Working Memory Plugin provides persistent core_memory blocks. **USE THEM COR
|
|
|
1767
1681
|
}
|
|
1768
1682
|
},
|
|
1769
1683
|
|
|
1770
|
-
//
|
|
1771
|
-
// Phase 4: Preserve State Before Compaction
|
|
1772
|
-
// ========================================================================
|
|
1684
|
+
// Preserve working memory state before compaction.
|
|
1773
1685
|
"experimental.session.compacting": async (hookInput, output) => {
|
|
1774
1686
|
const { sessionID } = hookInput;
|
|
1775
1687
|
|
|
1688
|
+
// Sub-agents don't need compaction support.
|
|
1689
|
+
if (await isSubAgent(sessionID)) return;
|
|
1690
|
+
|
|
1776
1691
|
// Preserve only the most relevant working memory items
|
|
1777
1692
|
const preservedItems = await preserveRelevantItems(directory, sessionID, 0.5);
|
|
1778
1693
|
|
|
@@ -1808,7 +1723,7 @@ The Working Memory Plugin provides persistent core_memory blocks. **USE THEM COR
|
|
|
1808
1723
|
}
|
|
1809
1724
|
}
|
|
1810
1725
|
|
|
1811
|
-
//
|
|
1726
|
+
// Inject pending OpenCode todos into compaction context.
|
|
1812
1727
|
try {
|
|
1813
1728
|
const { execSync } = await import("child_process");
|
|
1814
1729
|
const dbPath = join(process.env.HOME || "~", ".local/share/opencode/opencode.db");
|
|
@@ -1849,9 +1764,7 @@ The Working Memory Plugin provides persistent core_memory blocks. **USE THEM COR
|
|
|
1849
1764
|
}
|
|
1850
1765
|
},
|
|
1851
1766
|
|
|
1852
|
-
// ========================================================================
|
|
1853
1767
|
// Tools
|
|
1854
|
-
// ========================================================================
|
|
1855
1768
|
tool: {
|
|
1856
1769
|
core_memory_update: tool({
|
|
1857
1770
|
description: `Update persistent core memory blocks that survive compaction.
|
|
@@ -1866,7 +1779,48 @@ Operations:
|
|
|
1866
1779
|
- append: Add content to the end of the block (automatically adds newline)
|
|
1867
1780
|
|
|
1868
1781
|
These blocks are ALWAYS visible to you in every message, even after compaction.
|
|
1869
|
-
Update them regularly to maintain continuity across long conversations
|
|
1782
|
+
Update them regularly to maintain continuity across long conversations.
|
|
1783
|
+
|
|
1784
|
+
---
|
|
1785
|
+
|
|
1786
|
+
## Usage Guidelines
|
|
1787
|
+
|
|
1788
|
+
### goal block (1000 chars)
|
|
1789
|
+
**Purpose**: ONE specific task you're working on RIGHT NOW
|
|
1790
|
+
|
|
1791
|
+
โ
GOOD: "Fix pruning bug where items with relevanceScore <0.01 are incorrectly excluded"
|
|
1792
|
+
โ
GOOD: "Add new tool: working_memory_search to query pool items by keyword"
|
|
1793
|
+
โ BAD: "Complete Phase 1-4 development and testing" (too broad, likely already done)
|
|
1794
|
+
โ BAD: "Build a working memory system for OpenCode" (project-level goal, not task-level)
|
|
1795
|
+
|
|
1796
|
+
### progress block (2000 chars)
|
|
1797
|
+
**Purpose**: Checklist of done/in-progress/blocked items + key decisions
|
|
1798
|
+
|
|
1799
|
+
โ
GOOD: "โ
Found bug in applyDecay()\\nโณ Testing fix with gamma=0.85\\nโ Need to verify edge case"
|
|
1800
|
+
โ
GOOD: "โ
Phase 1-3 complete\\nโณ Phase 4 intervention testing\\nโ ๏ธ BLOCKED: Need promptAsync docs"
|
|
1801
|
+
โ BAD: "Function foo() @ file.ts:L1286-1354" (line numbers useless after edits)
|
|
1802
|
+
โ BAD: "Commit 2f42f1b implemented X" (commit hash irrelevant)
|
|
1803
|
+
โ BAD: "API: client.session.promptAsync({ ... })" (API signatures belong in source)
|
|
1804
|
+
|
|
1805
|
+
### context block (1500 chars)
|
|
1806
|
+
**Purpose**: Files you're CURRENTLY editing + key patterns/conventions
|
|
1807
|
+
|
|
1808
|
+
โ
GOOD: "Editing: src/plugin.ts\\nKey paths: .opencode/memory-core/ (persistent blocks)"
|
|
1809
|
+
โ
GOOD: "Pattern: All async file ops use mkdir({recursive:true}) before writeFile"
|
|
1810
|
+
โ BAD: Type definitions, function signatures, API docs (read source/docs instead)
|
|
1811
|
+
|
|
1812
|
+
### NEVER store in core memory
|
|
1813
|
+
- API documentation (read source/docs when needed)
|
|
1814
|
+
- Type definitions from libraries (import them)
|
|
1815
|
+
- Function signatures (read source code)
|
|
1816
|
+
- Completed goals (clear them immediately)
|
|
1817
|
+
|
|
1818
|
+
### Update core memory immediately when
|
|
1819
|
+
- Starting new task: clear old goal, set new specific goal
|
|
1820
|
+
- Making progress: update progress checklist (keep concise)
|
|
1821
|
+
- Switching files: update context with current working files
|
|
1822
|
+
- Task completed: clear goal/progress, set next task
|
|
1823
|
+
- Approaching char limit: compress or remove outdated info`,
|
|
1870
1824
|
args: {
|
|
1871
1825
|
block: tool.schema.enum(["goal", "progress", "context"]).describe(
|
|
1872
1826
|
"Which memory block to update (goal/progress/context)"
|
package/package.json
CHANGED