pi-observational-memory 2.4.1 → 2.4.3
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 +30 -2
- package/package.json +6 -5
- package/src/commands/status.ts +4 -3
- package/src/commands/view.ts +5 -3
- package/src/compaction.ts +448 -35
- package/src/config.ts +56 -0
- package/src/debug-log.ts +53 -0
- package/src/hooks/compaction-hook.ts +197 -8
- package/src/hooks/compaction-trigger.ts +24 -1
- package/src/hooks/observer-trigger.ts +70 -37
- package/src/model-budget.ts +9 -0
- package/src/observer.ts +24 -6
- package/src/progress.ts +155 -0
- package/src/prompts.ts +23 -22
package/README.md
CHANGED
|
@@ -76,7 +76,7 @@ flowchart TD
|
|
|
76
76
|
Conv([Conversation accumulates])
|
|
77
77
|
Obs[Observer<br/>async, fire-and-forget<br/>compresses each chunk into timestamped,<br/>relevance-tagged observations<br/>stored as silent tree entries]
|
|
78
78
|
Comp[Compaction<br/>extension-owned; merges accumulated<br/>observations with prior compaction state]
|
|
79
|
-
RP[Reflector + Pruner<br/>Reflector runs focused passes<br/>to crystallize durable patterns<br/>Pruner drops observations by id<br/>across up to
|
|
79
|
+
RP[Reflector + Pruner<br/>Reflector runs two focused passes<br/>to crystallize durable patterns<br/>Pruner drops observations by id<br/>across up to 2 passes]
|
|
80
80
|
Sum[Summary mechanically assembled<br/>## Reflections<br/> [id] durable insight<br/>## Observations<br/> [id] YYYY-MM-DD HH:MM relevance ...<br/>Becomes the compactionSummary<br/>the agent sees on the next turn]
|
|
81
81
|
|
|
82
82
|
Conv -->|every ~1k raw tokens since last bound| Obs
|
|
@@ -126,7 +126,31 @@ To run the background memory work (observer, reflector, pruner) on a cheaper / f
|
|
|
126
126
|
}
|
|
127
127
|
```
|
|
128
128
|
|
|
129
|
-
|
|
129
|
+
Nested agent-loop turns default to `16` for each memory role. To tune them (useful for controlling cost on large observation pools):
|
|
130
|
+
|
|
131
|
+
```json
|
|
132
|
+
{
|
|
133
|
+
"observational-memory": {
|
|
134
|
+
"observerMaxTurnsPerRun": 8,
|
|
135
|
+
"reflectorMaxTurnsPerPass": 12,
|
|
136
|
+
"prunerMaxTurnsPerPass": 12
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
A turn is one assistant/model response cycle inside Pi's nested agent loop. These caps are checked after a turn finishes; they are not hard mid-stream interrupts and are not literal tool-call counters.
|
|
142
|
+
|
|
143
|
+
For memory model calls, you can also tune thinking effort with one unified setting. Valid values are `off`, `minimal`, `low`, `medium`, `high`, and `xhigh`; the default is `low`.
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"observational-memory": {
|
|
148
|
+
"thinkingLevel": "low"
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
The settings most worth knowing:
|
|
130
154
|
|
|
131
155
|
| Setting | Default | What it controls |
|
|
132
156
|
|---|---|---|
|
|
@@ -135,6 +159,10 @@ The six settings most worth knowing:
|
|
|
135
159
|
| `reflectionThresholdTokens` | `30,000` | The observation pool size at which reflector + pruner engage |
|
|
136
160
|
| `passive` | `false` | Disables proactive observation and extension-triggered compaction while keeping manual/Pi compaction and commands active |
|
|
137
161
|
| `compactionModel` | session model | Which model runs the observer / reflector / pruner — point at a cheaper one to save cost |
|
|
162
|
+
| `observerMaxTurnsPerRun` | `16` | Assistant-turn cap for each observer run |
|
|
163
|
+
| `reflectorMaxTurnsPerPass` | `16` | Assistant-turn cap for each reflector pass |
|
|
164
|
+
| `prunerMaxTurnsPerPass` | `16` | Assistant-turn cap for each pruner pass |
|
|
165
|
+
| `thinkingLevel` | `low` | Thinking effort for observer, reflector, and pruner calls; `off` omits Pi's reasoning option |
|
|
138
166
|
| `compaction.keepRecentTokens` | `20,000` | How much recent conversation Pi keeps verbatim post-compaction (Pi setting; structural to the extension) |
|
|
139
167
|
|
|
140
168
|
For shell/session-level control, `PI_OBSERVATIONAL_MEMORY_PASSIVE` overrides global and project settings. Use `1`, `true`, `yes`, or `on` to enable passive mode; use `0`, `false`, `no`, or `off` to force it off.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-observational-memory",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.3",
|
|
4
4
|
"description": "Observational memory extension for pi — cache-friendly tiered compaction with observations and reflections.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -40,11 +40,12 @@
|
|
|
40
40
|
"@mariozechner/pi-tui": "*"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@mariozechner/pi-agent-core": "^0.
|
|
44
|
-
"@mariozechner/pi-ai": "^0.
|
|
45
|
-
"@mariozechner/pi-coding-agent": "^0.
|
|
46
|
-
"@mariozechner/pi-tui": "^0.
|
|
43
|
+
"@mariozechner/pi-agent-core": "^0.73.0",
|
|
44
|
+
"@mariozechner/pi-ai": "^0.73.0",
|
|
45
|
+
"@mariozechner/pi-coding-agent": "^0.73.0",
|
|
46
|
+
"@mariozechner/pi-tui": "^0.73.0",
|
|
47
47
|
"@types/node": "^22.0.0",
|
|
48
|
+
"typebox": "^1.1.38",
|
|
48
49
|
"typescript": "^5.6.0",
|
|
49
50
|
"vitest": "^4.1.5"
|
|
50
51
|
}
|
package/src/commands/status.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
rawTokensSinceLastBound,
|
|
6
6
|
rawTokensSinceLastCompaction,
|
|
7
7
|
} from "../branch.js";
|
|
8
|
+
import { observationPoolTokens as estimateObservationPoolTokens } from "../compaction.js";
|
|
8
9
|
import { countByRelevance, formatRelevanceHistogram } from "../relevance.js";
|
|
9
10
|
import type { Runtime } from "../runtime.js";
|
|
10
11
|
import { estimateStringTokens } from "../tokens.js";
|
|
@@ -21,12 +22,12 @@ export function registerStatusCommand(pi: ExtensionAPI, runtime: Runtime): void
|
|
|
21
22
|
|
|
22
23
|
const { reflections: committedRefs, committedObs, pendingObs } = getMemoryState(entries);
|
|
23
24
|
const committedRefItems = committedRefs as MemoryReflection[];
|
|
24
|
-
const committedObsTokens = committedObs
|
|
25
|
+
const committedObsTokens = estimateObservationPoolTokens(committedObs);
|
|
25
26
|
const committedObsCount = committedObs.length;
|
|
26
27
|
const committedRefsTokens = committedRefItems.reduce((s, r) => s + estimateStringTokens(reflectionContent(r)), 0);
|
|
27
28
|
const committedRefsCount = committedRefItems.length;
|
|
28
29
|
|
|
29
|
-
const pendingObsTokens = pendingObs
|
|
30
|
+
const pendingObsTokens = estimateObservationPoolTokens(pendingObs);
|
|
30
31
|
const pendingObsCount = pendingObs.length;
|
|
31
32
|
|
|
32
33
|
const relevanceHistogram = countByRelevance([...committedObs, ...pendingObs]);
|
|
@@ -41,7 +42,7 @@ export function registerStatusCommand(pi: ExtensionAPI, runtime: Runtime): void
|
|
|
41
42
|
// at compaction entry. We over-count by the tail slice and can't predict the gap obs here.
|
|
42
43
|
// Precise version would simulate the new firstKeptEntryId by walking back keepRecentTokens from
|
|
43
44
|
// the branch tail and split pending into pre-tail vs tail-covering.
|
|
44
|
-
const observationPoolTokens =
|
|
45
|
+
const observationPoolTokens = estimateObservationPoolTokens([...committedObs, ...pendingObs]);
|
|
45
46
|
const obsPct = Math.min(100, Math.round((sinceBound / obsThreshold) * 100));
|
|
46
47
|
const compPct = Math.min(100, Math.round((sinceCompaction / compThreshold) * 100));
|
|
47
48
|
const refPct = Math.min(100, Math.round((observationPoolTokens / refThreshold) * 100));
|
package/src/commands/view.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
2
|
import { getMemoryState } from "../branch.js";
|
|
3
|
+
import { observationPoolTokens as estimateObservationPoolTokens } from "../compaction.js";
|
|
3
4
|
import { countByRelevance, formatRelevanceHistogram } from "../relevance.js";
|
|
4
5
|
import type { Runtime } from "../runtime.js";
|
|
5
6
|
import { estimateStringTokens } from "../tokens.js";
|
|
@@ -17,14 +18,15 @@ export function registerViewCommand(pi: ExtensionAPI, runtime: Runtime): void {
|
|
|
17
18
|
const committedRefTokens = committedRefItems.reduce((s, r) => s + estimateStringTokens(reflectionContent(r)), 0);
|
|
18
19
|
const committedRefCount = committedRefItems.length;
|
|
19
20
|
|
|
20
|
-
const committedObsTokens = committedObs
|
|
21
|
+
const committedObsTokens = estimateObservationPoolTokens(committedObs);
|
|
21
22
|
const committedObsCount = committedObs.length;
|
|
22
23
|
|
|
23
|
-
const pendingObsTokens = pendingObs
|
|
24
|
+
const pendingObsTokens = estimateObservationPoolTokens(pendingObs);
|
|
24
25
|
const pendingObsCount = pendingObs.length;
|
|
25
26
|
|
|
26
27
|
const totalObsCount = committedObsCount + pendingObsCount;
|
|
27
|
-
const
|
|
28
|
+
const totalObsTokens = estimateObservationPoolTokens([...committedObs, ...pendingObs]);
|
|
29
|
+
const totalTokens = committedRefTokens + totalObsTokens;
|
|
28
30
|
const relevanceHistogram = countByRelevance([...committedObs, ...pendingObs]);
|
|
29
31
|
|
|
30
32
|
const plural = (n: number, singular: string, plural: string) => (n === 1 ? singular : plural);
|