Skip to content

Commit 20a6cfe

Browse files
committed
chore(release): prepare v1.5.0
1 parent 36b78ea commit 20a6cfe

9 files changed

Lines changed: 274 additions & 44 deletions

File tree

AGENTS.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ export type LongTermSource = "explicit" | "compaction" | "manual";
111111

112112
// ✅ USE: const assertions for limits
113113
export const LONG_TERM_LIMITS = {
114-
maxRenderedChars: 5200,
114+
maxRenderedChars: 3600,
115115
maxEntries: 28,
116116
} as const;
117117
```
@@ -140,8 +140,8 @@ const maxEntries = 28;
140140
async function loadWorkspaceMemory() { }
141141

142142
// ✅ REQUIRED: SCREAMING_SNAKE_CASE for constants
143-
const LONG_TERM_LIMITS = { maxRenderedChars: 5200, maxEntries: 28 };
144-
const HOT_STATE_LIMITS = { maxRenderedChars: 1200 };
143+
const LONG_TERM_LIMITS = { maxRenderedChars: 3600, maxEntries: 28 };
144+
const HOT_STATE_LIMITS = { maxRenderedChars: 700 };
145145

146146
// ✅ REQUIRED: PascalCase for types
147147
type WorkspaceMemoryStore = { ... };
@@ -236,7 +236,7 @@ export default {
236236
- **Location**: `~/.local/share/opencode-working-memory/workspaces/{workspaceKey}/workspace-memory.json`
237237
- **Workspace Key**: First 16 chars of `sha256(realpath(workspaceRoot))`
238238
- **Schema**: See `src/types.ts:WorkspaceMemoryStore`
239-
- **Limits**: 5200 chars, 28 entries max
239+
- **Limits**: 3600 chars, 28 entries max
240240

241241
### Session State Files
242242

@@ -299,9 +299,9 @@ Extracts workspace memory candidates from conversation, applies quality gate and
299299

300300
## Performance Considerations
301301

302-
- **Workspace memory budget**: 5200 chars injected into system prompt
303-
- **Session state budget**: 1200 chars injected into system prompt
304-
- **Total overhead**: ~1500-6000 chars per message (minimal)
302+
- **Workspace memory budget**: 3600 chars injected into system prompt
303+
- **Session state budget**: 700 chars injected into system prompt
304+
- **Total overhead**: typically well below configured maximums
305305
- **Storage footprint**: ~2-5 KB per workspace for memory, ~1-3 KB per session
306306

307307
## Contributing

CHANGELOG.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,38 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.5.0] - 2026-04-29
9+
10+
### Added
11+
12+
- Strength-based workspace memory retention using exponential decay instead of additive priority scoring.
13+
- Per-type rendered caps for workspace memory candidates: feedback 10, decision 10, project 8, and reference 6.
14+
- Safety-critical memory weighting and type-cap exemption so important entries survive type floods while still competing under the global rendered cap.
15+
- Dormant-workspace effective age: after 14 days without activity, additional dormant time counts at 0.25x for retention decay.
16+
- Reinforcement tracking for repeated memories, with same-session and one-hour guards to prevent accidental reinforcement spam.
17+
- Memory health diagnostics for stored vs rendered counts, type caps, global cap overflow, dormancy, retention monitoring, and strength-ranked top/weakest entries.
18+
- CLI smoke tests and regression fixtures covering retention decay, stale-prune removal, type caps, reinforcement, invalid timestamps, and diagnostics.
19+
20+
### Changed
21+
22+
- Workspace memory rendering now ranks entries by retention strength, not the previous priority/penalty model.
23+
- Confidence is retained for compatibility but no longer affects retention scoring.
24+
- Old or stale-marked memories are no longer hard-pruned; they remain stored and only fall out of rendered context through strength and cap competition.
25+
- Existing duplicate promotion and dedupe paths now reinforce the surviving memory instead of only absorbing the duplicate.
26+
- Health output now separates stored active memories from rendered candidates to make cap behavior easier to understand.
27+
- Default prompt budgets are lower after calibration against observed rendered output: workspace memory is 3600 characters and hot session state is 700 characters.
28+
29+
### Fixed
30+
31+
- Invalid `updatedAt` or `retentionClock` values no longer produce `NaN` retention strength or unstable sorting.
32+
- Dormant age calculation only discounts the dormant overlap since an entry was created, so new memories do not inherit old workspace dormancy.
33+
- Type max totals above the global cap are handled correctly: the global rendered limit still wins.
34+
35+
### Not Included Yet
36+
37+
- Delete tombstones and explicit `supersedes` chain enforcement remain deferred follow-up work.
38+
- Hot/warm/cold tiered storage remains a future v1.6 direction.
39+
840
## [1.4.0] - 2026-04-28
941

1042
### Added

README.md

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55

66
Automatic memory for OpenCode agents.
77

8-
OpenCode Working Memory helps your agent keep useful context across compactions and sessions: project decisions, preferences, important references, active files, and unresolved errors.
8+
Working memory is context that **remembers what matters, fades what changes, and stays out of the way.**
99

10-
It works automatically, without manual memory tools or extra LLM/API calls.
10+
OpenCode Working Memory preserves project decisions, preferences, and references across compactions and sessions, while keeping active files and unresolved errors fresh for the current session — with no manual tools or extra LLM/API calls.
1111

1212
## Why This Exists
1313

@@ -31,6 +31,7 @@ Use it when you want your agent to remember things like:
3131
- **Compaction-based extraction** — memory extraction piggybacks on OpenCode’s existing compaction flow.
3232
- **No manual tools** — memory is injected automatically into the system prompt.
3333
- **Quality guards** — filters noisy memories, temporary progress snapshots, stack traces, raw errors, and credentials.
34+
- **Retention decay** — keeps the strongest memories in prompt context while older or weaker memories fade out naturally; important and reinforced memories decay more slowly.
3435

3536
## Installation
3637

@@ -121,6 +122,27 @@ Memory types:
121122
- `decision` — important implementation or architecture decisions
122123
- `reference` — useful paths, commands, or configuration references
123124

125+
### Retention Decay
126+
127+
> **Memory should fade, so the agent can keep learning.**
128+
>
129+
> Important memories decay more slowly, but every memory must leave room for newer project reality.
130+
131+
Memories decay over time. The strongest stay visible in the prompt; weaker ones fade from context without being deleted.
132+
133+
```text
134+
strength
135+
136+
██ │╲____ reinforced: slower decline
137+
│ ╲______
138+
▒▒ │ ╲__ ordinary memory
139+
│ ╲
140+
├ ─ ─ ─ ─ ─ ─ ─ ─╲─ dynamic cap competition zone
141+
░░ │ ╲ easier for new memories to replace
142+
│ ↑ still stored, not deleted
143+
└──────────────────────────────→ time / sessions
144+
```
145+
124146
## Explicit Memory Triggers
125147

126148
You can explicitly ask the agent to remember durable facts.
@@ -167,13 +189,15 @@ It includes guards for:
167189

168190
- Credential redaction
169191
- Duplicate memory cleanup
170-
- Superseding older decisions with newer ones
171-
- Consolidation accounting so promoted, absorbed, superseded, and rejected memories are handled differently
192+
- Accounting for promoted, absorbed, superseded, and rejected memories
193+
- Strength-based retention so useful memories stay visible without hard age pruning
172194
- Filtering stack traces, git hashes, raw errors, and noisy path-heavy facts
173195
- Rejecting temporary project progress snapshots
174196

175197
The goal is to remember durable facts, not every detail.
176198

199+
**Good memory is selective memory.**
200+
177201
Historical cleanup is intentionally conservative: extraction-time filtering may reject more aggressively, but one-time migration cleanup only supersedes high-confidence garbage patterns. This protects existing durable memories written in declarative style, such as "API endpoint is X" or "Product branding is Y".
178202

179203
For local development cleanup, use:
@@ -191,21 +215,21 @@ OpenCode Working Memory works out of the box.
191215

192216
Default behavior:
193217

194-
- Workspace memory budget: 5200 characters
218+
- Workspace memory budget: 3600 characters (~900 tokens)
195219
- Workspace memory limit: 28 entries
196-
- Hot session state budget: 1200 characters
220+
- Hot session state budget: 700 characters (~175 tokens)
197221
- Active files shown: 8
198222
- Open errors shown: 3
199223

200224
See [Configuration](docs/configuration.md) for customization options.
201225

202-
## Ongoing Work
226+
## Roadmap
203227

204228
Current focus:
205229

206-
- Improve memory recording quality so only durable, useful facts are kept.
207-
- Strengthen deduplication and supersession so stale memories do not pile up.
208-
- Add better forgetting behavior for obsolete decisions, preferences, and project facts.
230+
- Add explicit delete tombstones so removed memories do not get re-extracted.
231+
- Enforce explicit `supersedes` chains for safer replacement of obsolete memories.
232+
- Explore tiered hot/warm/cold storage after the retention model has more real-world data.
209233

210234
## Documentation
211235

RELEASE_NOTES.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,106 @@
11
# Release Notes
22

3+
## 1.5.0 (2026-04-29)
4+
5+
### Retention Decay Model
6+
7+
This release changes workspace memory retention from hard stale pruning and additive priority scoring to a strength-based decay model.
8+
9+
Think of it like a forgetting curve: memories fade over time, but important, reinforced, and safety-critical memories decay slower. Weak entries fall out of rendered prompt context by cap competition, not hard deletion.
10+
11+
> **Memory should fade, so the agent can keep learning.**
12+
> Important memories decay slower, but every memory must leave room for newer project reality and avoid long-term memory pollution.
13+
14+
```text
15+
strength
16+
17+
██ │╲____ reinforced: slower decline
18+
│ ╲______
19+
▒▒ │ ╲__ ordinary memory
20+
│ ╲
21+
├ ─ ─ ─ ─ ─ ─ ─ ─╲─ dynamic cap competition zone
22+
░░ │ ╲ easier for new memories to replace
23+
│ ↑ still stored, not deleted
24+
└──────────────────────────────→ time / sessions
25+
```
26+
27+
### What Changed
28+
29+
- **Strength-based retention**: workspace memory now uses exponential decay: initial strength × age decay.
30+
- **Better initial strength**: type, source, user importance, and safety-critical status now determine how strong a memory starts.
31+
- **No confidence scoring**: confidence remains in stored data for compatibility, but it no longer affects retention ranking.
32+
- **Type caps**: rendered workspace memory now caps feedback, decisions, project facts, and references separately so one type cannot monopolize all 28 slots.
33+
- **Safety-critical protection**: safety-critical entries get stronger retention and are exempt from per-type caps, while still competing under the global rendered cap.
34+
- **Dormant-aware age**: after 14 inactive days, additional dormant workspace time counts at 0.25x so paused projects do not forget too aggressively.
35+
- **Reinforcement**: repeated matching memories reinforce the survivor and slow future decay, with same-session and one-hour guards to avoid accidental spam.
36+
- **No hard stale pruning**: old or stale-marked memories are no longer automatically dropped by age; they lose rendered space only through cap competition.
37+
- **Calibrated prompt budgets**: observed rendered output was typically under ~2000 characters for workspace memory and ~500 characters for hot session state, so defaults were reduced to 3600 and 700 characters to keep overhead lower while retaining buffer.
38+
- **Clearer health output**: `memory-diag health` now reports stored vs rendered counts, type caps, global cap overflow, dormancy, retention monitoring, and strength-ranked top/weakest entries.
39+
40+
### Why This Helps
41+
42+
- User preferences and explicit memories are less likely to disappear just because inferred project facts are newer.
43+
- Feedback, decisions, project facts, and references share prompt space more fairly.
44+
- Returning to an old workspace is less punishing because dormant time decays more slowly.
45+
- Maintainers can see why memories are rendered or capped instead of guessing from a single active-memory count.
46+
- Stale entries can fade out of prompt context without destructive cleanup.
47+
48+
### Diagnostics
49+
50+
Maintainers can inspect retention behavior with:
51+
52+
```bash
53+
bun scripts/memory-diag.ts health
54+
```
55+
56+
The health output now includes sections like:
57+
58+
```txt
59+
Stored active memories: 28
60+
Rendered candidates: 20
61+
62+
By type:
63+
feedback stored=17 rendered=10 typeCap=10
64+
decision stored=11 rendered=10 typeCap=10
65+
66+
Retention caps:
67+
type-capped entries: 8
68+
global-cap overflow: 0
69+
70+
Dormancy:
71+
dormant discount active: no
72+
73+
Retention monitoring:
74+
high_importance_ratio: 0.0% (alert > 30%)
75+
```
76+
77+
### Not Included Yet
78+
79+
- Delete tombstones are not implemented in this release.
80+
- Explicit `supersedes` chain enforcement is still deferred.
81+
- Hot/warm/cold tiered storage remains future work.
82+
83+
### Upgrade Notes
84+
85+
- No configuration changes required.
86+
- Existing workspace memory files remain compatible.
87+
- Existing entries without a `retentionClock` fall back safely to existing timestamps.
88+
- The OpenCode config entry stays the same:
89+
90+
```json
91+
{
92+
"plugin": ["opencode-working-memory"]
93+
}
94+
```
95+
96+
### Validation
97+
98+
- `npm run typecheck`
99+
- `npm test` — 237 tests passing
100+
- `bun scripts/memory-diag.ts health`
101+
102+
---
103+
3104
## 1.4.0 (2026-04-28)
4105

5106
### Memory Quality Cleanup

docs/architecture.md

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ OpenCode Working Memory implements a **three-layer memory architecture** designe
1010
│ • Persistent storage: ~/.local/share/opencode-working-... │
1111
│ • Types: feedback | project | decision | reference │
1212
│ • Sources: explicit | compaction | manual │
13-
│ • Limits: 5200 chars / 28 entries
13+
│ • Render limits: 3600 chars / 28 entries │
1414
│ • Survives: session reset, compaction (same workspace) │
1515
└─────────────────────────────────────────────────────────────┘
1616
@@ -48,8 +48,9 @@ Long-term memory that persists across sessions within the same workspace. Perfec
4848
{
4949
version: 1,
5050
workspace: { root: string, key: string },
51-
limits: { maxRenderedChars: 5200, maxEntries: 28 },
51+
limits: { maxRenderedChars: 3600, maxEntries: 28 },
5252
entries: LongTermMemoryEntry[],
53+
lastActivityAt?: string,
5354
updatedAt: string
5455
}
5556
```
@@ -90,18 +91,47 @@ Memory candidates:
9091
- Path-heavy facts (>50% paths)
9192
- Very short text (<20 chars)
9293

93-
### Consolidation and Deduplication
94+
### Consolidation, Deduplication, and Retention
9495

9596
Memories are deduplicated and consolidated with accounting:
9697

9798
1. Normalize exact text: lowercase, strip punctuation, collapse whitespace.
9899
2. Group project/reference entries by identity where possible.
99-
3. Group decisions and feedback by topic where possible.
100-
4. Keep the best surviving entry by source, confidence, type, and freshness rules.
100+
3. Keep decision and feedback entries on exact canonical matching to avoid broad semantic merges.
101+
4. Keep the best surviving entry by source, confidence, specificity, and freshness tie-breakers.
101102
5. Emit accounting events so pending memories can be classified as promoted, absorbed, superseded, or rejected.
102103

103104
This prevents absorbed or superseded pending memories from retrying forever while still preserving the active surviving memory.
104105

106+
Retention then decides which active memories are rendered into prompt context. It does not hard-delete old memories by age.
107+
108+
```typescript
109+
strength = initialStrength * 2 ** (-effectiveAgeDays / effectiveHalfLifeDays)
110+
```
111+
112+
Initial strength is based on memory type, source, optional user importance, and safety-critical status. Confidence remains stored for compatibility but is not part of retention scoring.
113+
114+
Rendered candidates are selected in this order:
115+
116+
1. Exclude `status: "superseded"` entries.
117+
2. Compute current retention strength.
118+
3. Sort by strength descending.
119+
4. Apply per-type caps, with safety-critical entries exempt from type caps.
120+
5. Keep the top 28 rendered entries under the workspace memory character budget.
121+
122+
Default type caps:
123+
124+
| Type | Rendered cap |
125+
|------|--------------|
126+
| `feedback` | 10 |
127+
| `decision` | 10 |
128+
| `project` | 8 |
129+
| `reference` | 6 |
130+
131+
The type-cap total is 34, intentionally above the global 28-entry cap. These are maximums, not quotas.
132+
133+
Dormant workspaces age more slowly: after 14 inactive days, additional dormant time counts at 0.25x for retention decay. Repeated duplicate memories reinforce the surviving entry and slow future decay, but same-session and under-one-hour repeats do not stack reinforcement.
134+
105135
### System Prompt Injection
106136

107137
Workspace memory is injected at the top of every message:
@@ -241,7 +271,7 @@ Applies quality gate, redaction, migration, consolidation accounting, deduplicat
241271
- `session.compacted`: Promote session decisions to workspace memory
242272
- `session.deleted`: Clean up session state files
243273

244-
Promotion uses accounting results from workspace memory normalization. Pending memories that are kept are promoted; duplicate memories are absorbed; obsolete same-topic memories are superseded; stale or over-capacity compaction memories are rejected.
274+
Promotion uses accounting results from workspace memory normalization. Pending memories that are kept are promoted; duplicate memories are absorbed; exact decision replacements can be superseded; over-capacity compaction memories are rejected. Stale-marked memories are not hard-pruned by age; they lose rendered space through retention strength and cap competition.
245275

246276
## Quality Guarantees
247277

@@ -319,14 +349,14 @@ const workspaceKey = sha256(realpath(workspaceRoot)).slice(0, 16)
319349

320350
| Layer | Max Chars | Max Entries |
321351
|-------|-----------|-------------|
322-
| Workspace Memory | 5200 | 28 |
323-
| Hot Session State | 1200 | 8 files, 3 errors |
352+
| Workspace Memory | 3600 | 28 |
353+
| Hot Session State | 700 | 8 files, 3 errors |
324354

325355
### Injection Overhead
326356

327-
- Workspace memory: ~200-500 chars per message
328-
- Hot session state: ~200-400 chars per message
329-
- Total: ~400-900 chars per message (minimal)
357+
- Workspace memory: usually under ~2000 chars in observed rendered output
358+
- Hot session state: usually under ~500 chars in observed rendered output
359+
- Total: typically well below the configured maximums
330360

331361
### Storage Footprint
332362

0 commit comments

Comments
 (0)