pi-observational-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 +118 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,35 +1,100 @@
|
|
|
1
1
|
# pi-observational-memory
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Make Pi sessions feel endless.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Every session has a cliff. You're three hours in, the context window fills up, compaction runs, and suddenly the agent doesn't remember what you decided in hour one. You start repeating yourself. The session that was flowing now feels like a new conversation with an amnesiac.
|
|
6
|
+
|
|
7
|
+
pi-observational-memory pushes that cliff out far enough that you stop thinking about it. It replaces Pi's compaction summary with a two-tier memory system — **observations** (a timestamped, priority-tagged event log) and **reflections** (stable long-term facts) — so the agent carries forward *what* you decided, *when*, *why*, and what's already done. Not as prose that degrades with each compaction cycle, but as structured memory that stays sharp.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
<reflections>
|
|
11
|
+
- User works at Acme Corp, building "Acme Dashboard"
|
|
12
|
+
- Stack: Next.js 15, Supabase auth, server components with client-side hydration
|
|
13
|
+
- Hard constraint: ship by January 22nd 2026
|
|
14
|
+
</reflections>
|
|
15
|
+
|
|
16
|
+
<observations>
|
|
17
|
+
Date: 2026-01-15
|
|
18
|
+
- 🔴 14:30 User decided to switch from REST to GraphQL for the public API
|
|
19
|
+
- 🟡 14:32 Motivation: reduce over-fetching on mobile clients
|
|
20
|
+
- 🟢 14:35 Agent scaffolded GraphQL schema in src/schema.ts
|
|
21
|
+
- ✅ 14:50 GraphQL migration completed — user confirmed queries working
|
|
22
|
+
- 🔴 15:10 User wants rate limiting on all public endpoints
|
|
23
|
+
- 🟡 15:12 Prefers token bucket algorithm, 100 req/min per API key
|
|
24
|
+
</observations>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Hour six should feel like hour one. The agent knows who you are, what you've built together, and what's left to do.
|
|
28
|
+
|
|
29
|
+
Pi's built-in compaction handles most sessions well — it tracks file operations, manages split turns, and keeps recent messages intact. This extension is for the sessions where "most" isn't enough: long builds, multi-feature sprints, and the kind of deep work where breaking flow to start a new session costs you more than the tokens.
|
|
30
|
+
|
|
31
|
+
Inspired by [Mastra's Observational Memory](https://mastra.ai/blog/observational-memory) research (94.87% on LongMemEval). This is an independent implementation built for Pi's extension system and compaction model.
|
|
32
|
+
|
|
33
|
+
## Why this matters
|
|
34
|
+
|
|
35
|
+
Pi's default compaction summarizes old messages into prose and tracks which files were read and modified. This works well for short-to-medium sessions. But prose summaries are inherently lossy in ways that compound over time — the third compaction summarizes a summary of a summary, and specific decisions, timestamps, and completion states get flattened.
|
|
36
|
+
|
|
37
|
+
Observational memory uses a different format that's designed to survive repeated compaction cycles:
|
|
38
|
+
|
|
39
|
+
| What you get | Why it matters |
|
|
40
|
+
|---|---|
|
|
41
|
+
| 🔴 Priority tags | Agent knows what's important vs. what's noise |
|
|
42
|
+
| Timestamps | Temporal reasoning — agent knows *when* things happened |
|
|
43
|
+
| ✅ Completion markers | Agent won't redo finished work |
|
|
44
|
+
| State change tracking | "Switched from A to B" — no stale decisions |
|
|
45
|
+
| User quotes preserved | Your exact words survive compression |
|
|
46
|
+
| Reflections tier | Identity and constraints never get pruned |
|
|
6
47
|
|
|
7
48
|
## How it works
|
|
8
49
|
|
|
9
|
-
|
|
50
|
+
Two LLM passes intercept Pi's compaction to produce structured output instead of prose:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
Raw messages accumulate
|
|
54
|
+
│
|
|
55
|
+
▼ exceeds observationThreshold (default: 50k tokens)
|
|
56
|
+
┌─────────┐
|
|
57
|
+
│ Observer │──▶ Compresses messages into timestamped,
|
|
58
|
+
└─────────┘ priority-tagged observations. Append-only.
|
|
59
|
+
│
|
|
60
|
+
▼ observations exceed reflectionThreshold (default: 30k tokens)
|
|
61
|
+
┌───────────┐
|
|
62
|
+
│ Reflector │──▶ Promotes stable facts to reflections.
|
|
63
|
+
└───────────┘ Prunes only what it's certain is dead.
|
|
64
|
+
│
|
|
65
|
+
▼
|
|
66
|
+
┌──────────────────────────────┐
|
|
67
|
+
│ Agent context: │
|
|
68
|
+
│ 1. System prompt │
|
|
69
|
+
│ 2. Reflections (stable) │
|
|
70
|
+
│ 3. Observations (event log) │
|
|
71
|
+
│ 4. Recent raw messages │
|
|
72
|
+
└──────────────────────────────┘
|
|
73
|
+
```
|
|
10
74
|
|
|
11
|
-
|
|
12
|
-
2. **Reflector** — when observations grow past a threshold, promotes stable facts to reflections and prunes dead observations.
|
|
75
|
+
The observer is aggressive — it compresses everything into dense observations. The reflector is conservative — it only prunes what's clearly dead. Between reflector runs, no information is lost.
|
|
13
76
|
|
|
14
|
-
|
|
77
|
+
For the full technical breakdown — compaction lifecycle, state persistence, configuration interactions — see **[docs/how-it-works.md](docs/how-it-works.md)**.
|
|
15
78
|
|
|
16
79
|
## Install
|
|
17
80
|
|
|
18
|
-
Install from npm with pi:
|
|
19
|
-
|
|
20
81
|
```bash
|
|
21
82
|
pi install npm:pi-observational-memory
|
|
22
83
|
```
|
|
23
84
|
|
|
24
|
-
Or
|
|
85
|
+
Or from GitHub:
|
|
25
86
|
|
|
26
87
|
```bash
|
|
27
|
-
pi install
|
|
88
|
+
pi install git:github.com/elpapi42/pi-observational-memory
|
|
28
89
|
```
|
|
29
90
|
|
|
91
|
+
That's it. The extension hooks into Pi's compaction lifecycle automatically. No config file needed to start — defaults work well for most sessions.
|
|
92
|
+
|
|
30
93
|
## Configuration
|
|
31
94
|
|
|
32
|
-
|
|
95
|
+
### Extension settings
|
|
96
|
+
|
|
97
|
+
Create `~/.pi/agent/observational-memory.json` (or `.pi/observational-memory.json` per project):
|
|
33
98
|
|
|
34
99
|
```json
|
|
35
100
|
{
|
|
@@ -38,29 +103,60 @@ Create `~/.pi/agent/observational-memory.json`:
|
|
|
38
103
|
}
|
|
39
104
|
```
|
|
40
105
|
|
|
41
|
-
|
|
|
42
|
-
|
|
43
|
-
| `observationThreshold` | `
|
|
44
|
-
| `reflectionThreshold` | `
|
|
45
|
-
| `compactionModel` | session model | Optional
|
|
106
|
+
| Setting | Default | What it controls |
|
|
107
|
+
|---|---|---|
|
|
108
|
+
| `observationThreshold` | `50,000` tokens | How much raw conversation accumulates before the observer runs |
|
|
109
|
+
| `reflectionThreshold` | `30,000` tokens | How large observations grow before the reflector promotes and prunes |
|
|
110
|
+
| `compactionModel` | session model | Optional — use a cheaper model for observer/reflector passes |
|
|
46
111
|
|
|
47
|
-
###
|
|
112
|
+
### Using a cheaper model for compaction
|
|
113
|
+
|
|
114
|
+
The observer and reflector don't need the same capabilities as your coding agent. Offload them to something fast and cheap:
|
|
48
115
|
|
|
49
116
|
```json
|
|
50
117
|
{
|
|
51
|
-
"observationThreshold": 6000,
|
|
52
|
-
"reflectionThreshold": 1000,
|
|
53
118
|
"compactionModel": { "provider": "openrouter", "id": "google/gemma-4-31b-it" }
|
|
54
119
|
}
|
|
55
120
|
```
|
|
56
121
|
|
|
122
|
+
### Pi compaction settings
|
|
123
|
+
|
|
124
|
+
The extension works with Pi's built-in compaction settings in `~/.pi/agent/settings.json`:
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"compaction": {
|
|
129
|
+
"keepRecentTokens": 20000
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
| Setting | Default | What it controls |
|
|
135
|
+
|---|---|---|
|
|
136
|
+
| `keepRecentTokens` | `20,000` | Tokens of recent conversation kept verbatim (not summarized) |
|
|
137
|
+
| `reserveTokens` | `16,384` | Headroom for LLM response; Pi auto-compacts when context exceeds `window - reserveTokens` |
|
|
138
|
+
|
|
139
|
+
**How they interact:** Pi decides *when* to compact and *how many recent messages to keep raw*. The extension decides *how* to compact (observations + reflections instead of a flat summary) and *when to proactively trigger* compaction before the window fills. Both paths end up at the same `session_before_compact` hook.
|
|
140
|
+
|
|
57
141
|
## Commands
|
|
58
142
|
|
|
59
143
|
| Command | Description |
|
|
60
|
-
|
|
61
|
-
| `/om-status` |
|
|
144
|
+
|---|---|
|
|
145
|
+
| `/om-status` | Token counts, thresholds, and when the next observer/reflector pass will trigger |
|
|
62
146
|
| `/om-view` | Print current reflections and observations |
|
|
63
|
-
| `/om-view --full` | Same as above, plus raw kept messages |
|
|
147
|
+
| `/om-view --full` | Same as above, plus the raw kept messages |
|
|
148
|
+
|
|
149
|
+
## Design decisions
|
|
150
|
+
|
|
151
|
+
**Why observations are append-only.** Between reflector runs, nothing is lost. The observer only compresses new messages — it doesn't decide what to keep. This keeps the observer simple and predictable.
|
|
152
|
+
|
|
153
|
+
**Why the reflector is conservative.** It only prunes what it's certain is dead: completed tasks no longer referenced, superseded information, exact duplicates. Being old or low-priority is not a reason to prune. If in doubt, it keeps.
|
|
154
|
+
|
|
155
|
+
**Why user messages are captured near-verbatim.** When the context window shrinks, observations become the only record of what you said. Short messages are preserved exactly; long ones are summarized with key phrases quoted.
|
|
156
|
+
|
|
157
|
+
**Why state changes are explicit.** When you say "switching from A to B," the observation notes both the new state and what it replaces. This prevents the agent from acting on stale information after compaction.
|
|
158
|
+
|
|
159
|
+
**Why memory lives in the session.** State is stored in Pi's session entries as compaction `details` — no external database, no filesystem state, no separate sync. On session resume, the extension walks backward through entries to restore memory. If you use `/tree` to branch, each branch gets its own memory state.
|
|
64
160
|
|
|
65
161
|
## License
|
|
66
162
|
|
package/package.json
CHANGED