pi-mega-compact 0.12.2 → 0.12.4

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.
@@ -194,7 +194,12 @@ export function handlePerf(req, res, ctx) {
194
194
  minutes = 30;
195
195
  minutes = Math.min(minutes, 1440); // cap at 24h
196
196
  const sinceTs = Date.now() - minutes * 60_000;
197
- const rows = readPerfSamples(stateDir, sinceTs); // guardrails-allow PREVENT-PI-004: local SQLite read (loopback dashboard)
197
+ let rows = readPerfSamples(stateDir, sinceTs); // guardrails-allow PREVENT-PI-004: local SQLite read (loopback dashboard)
198
+ // Fallback: if the window is empty (stale data from a previous
199
+ // session), show the most recent 200 samples regardless of age.
200
+ if (rows.length === 0) {
201
+ rows = readPerfSamples(stateDir, 0).slice(-200);
202
+ }
198
203
  const byKind = new Map();
199
204
  for (const r of rows) {
200
205
  let arr = byKind.get(r.kind);
@@ -51,7 +51,21 @@ export function buildDashboardSnapshot(ctx) {
51
51
  tierPct: ctx.config.tierPct,
52
52
  effectiveThresholdPct: ctx.config.tierPct != null ? ctx.config.tierPct * 100 : null,
53
53
  },
54
- store: ctx.st,
54
+ store: {
55
+ checkpointCount: ctx.st.checkpointCount,
56
+ totalTokenEstimate: ctx.st.totalTokenEstimate,
57
+ originalTokens: ctx.st.originalTokens,
58
+ tokensSaved: ctx.st.tokensSaved,
59
+ injectedCount: ctx.st.injectedCount,
60
+ dedupHitRate: ctx.st.dedupHitRate,
61
+ // Session-scoped dedup stats from runtime counters, NOT the
62
+ // cumulative meta table (which is repo-wide and belongs in the
63
+ // repo card below). rt.dedupSkips/dedupAttempts reset on session
64
+ // restart so the session card shows only this session's dedup.
65
+ storageDedupRate: ctx.rt.dedupAttempts > 0 ? ctx.rt.dedupSkips / ctx.rt.dedupAttempts : 0,
66
+ dedupAttempts: ctx.rt.dedupAttempts,
67
+ dedupCollapsed: ctx.rt.dedupSkips,
68
+ },
55
69
  crew: {
56
70
  activeAgents: ctx.activeAgents,
57
71
  currentTurn: ctx.currentTurn,
@@ -76,13 +90,17 @@ export function buildDashboardSnapshot(ctx) {
76
90
  integrity: ctx.di,
77
91
  cacheHits: {
78
92
  session: ctx.rt.dedupSkips + ctx.rt.recallInjections,
79
- total: ctx.st.dedupCollapsed + ctx.st.injectedCount,
93
+ // Total = cumulative dedup collapses (meta table) + session
94
+ // recall injections. The meta table is the authoritative
95
+ // cumulative counter for dedup; recall injections are tracked
96
+ // per-session in rt and there's no cumulative counter yet.
97
+ total: ctx.repo.dedupCollapsed,
80
98
  sessionTokensSaved: ctx.rt.cacheHitTokens,
81
- totalTokensSaved: ctx.st.dedupCollapsed > 0 ? ctx.st.dedupCollapsed * 100 : 0,
99
+ totalTokensSaved: ctx.repo.dedupCollapsed > 0 ? ctx.repo.dedupCollapsed * 100 : 0,
82
100
  },
83
101
  compacts: {
84
102
  session: ctx.rt.compactCount,
85
- total: ctx.st.checkpointCount,
103
+ total: ctx.repo.checkpointCount,
86
104
  },
87
105
  timeSaved: {
88
106
  compact: {
@@ -91,7 +109,7 @@ export function buildDashboardSnapshot(ctx) {
91
109
  },
92
110
  cacheHit: {
93
111
  sessionSec: ctx.rt.cacheHitTokens / 1000,
94
- totalSec: (ctx.st.dedupCollapsed * 100) / 1000,
112
+ totalSec: (ctx.repo.dedupCollapsed * 100) / 1000,
95
113
  },
96
114
  },
97
115
  model: ctx.currentModel
@@ -48,10 +48,11 @@ export function buildCheckpointNodes(db, sessionId, nodes, edges) {
48
48
  let prevId = null;
49
49
  for (const row of rows) {
50
50
  const id = String(row.id);
51
+ const rowSessionId = sessionId || String(row.session_id ?? "");
51
52
  const summary = String(row.summary ?? "");
52
53
  const node = {
53
54
  id,
54
- sessionId,
55
+ sessionId: rowSessionId,
55
56
  label: id,
56
57
  summaryTruncated: summary.length > 200 ? summary.slice(0, 197) + "..." : summary,
57
58
  tokenEstimate: Number(row.token_estimate ?? 0),
@@ -107,14 +108,14 @@ export function buildTurnNodes(db, sessionId, nodes, edges) {
107
108
  const existingIds = new Set(nodes.map((n) => n.id));
108
109
  const rows = sessionId
109
110
  ? db
110
- .prepare(`SELECT turn_index, role, pressure_band, ctx_tokens, ctx_percent,
111
+ .prepare(`SELECT turn_index, session_id, role, pressure_band, ctx_tokens, ctx_percent,
111
112
  epoch_id, ended_at
112
113
  FROM turns
113
114
  WHERE session_id = ?
114
115
  ORDER BY turn_index ASC`)
115
116
  .all(sessionId)
116
117
  : db
117
- .prepare(`SELECT turn_index, role, pressure_band, ctx_tokens, ctx_percent,
118
+ .prepare(`SELECT turn_index, session_id, role, pressure_band, ctx_tokens, ctx_percent,
118
119
  epoch_id, ended_at
119
120
  FROM turns
120
121
  ORDER BY turn_index ASC`)
@@ -124,7 +125,8 @@ export function buildTurnNodes(db, sessionId, nodes, edges) {
124
125
  let prevId = null;
125
126
  for (const row of rows) {
126
127
  const ti = Number(row.turn_index);
127
- const nodeId = `turn:${sessionId}:${ti}`;
128
+ const rowSessionId = sessionId || String(row.session_id ?? "");
129
+ const nodeId = `turn:${rowSessionId}:${ti}`;
128
130
  if (existingIds.has(nodeId))
129
131
  continue;
130
132
  existingIds.add(nodeId);
@@ -132,7 +134,7 @@ export function buildTurnNodes(db, sessionId, nodes, edges) {
132
134
  const endedAt = row.ended_at ? Number(row.ended_at) : Date.now();
133
135
  const node = {
134
136
  id: nodeId,
135
- sessionId,
137
+ sessionId: rowSessionId,
136
138
  label: `Turn ${ti}`,
137
139
  summaryTruncated: `Turn ${ti}: ${role}${row.epoch_id ? ` (epoch: ${String(row.epoch_id)})` : ""}`,
138
140
  tokenEstimate: row.ctx_tokens ? Number(row.ctx_tokens) : 0,
@@ -234,7 +234,12 @@ export function handlePerf(
234
234
  if (!Number.isFinite(minutes) || minutes <= 0) minutes = 30;
235
235
  minutes = Math.min(minutes, 1440); // cap at 24h
236
236
  const sinceTs = Date.now() - minutes * 60_000;
237
- const rows = readPerfSamples(stateDir, sinceTs); // guardrails-allow PREVENT-PI-004: local SQLite read (loopback dashboard)
237
+ let rows = readPerfSamples(stateDir, sinceTs); // guardrails-allow PREVENT-PI-004: local SQLite read (loopback dashboard)
238
+ // Fallback: if the window is empty (stale data from a previous
239
+ // session), show the most recent 200 samples regardless of age.
240
+ if (rows.length === 0) {
241
+ rows = readPerfSamples(stateDir, 0).slice(-200);
242
+ }
238
243
  const byKind = new Map<string, number[]>();
239
244
  for (const r of rows) {
240
245
  let arr = byKind.get(r.kind);
@@ -110,7 +110,21 @@ export function buildDashboardSnapshot(ctx: SnapshotBuildContext): DashboardSnap
110
110
  tierPct: ctx.config.tierPct,
111
111
  effectiveThresholdPct: ctx.config.tierPct != null ? ctx.config.tierPct * 100 : null,
112
112
  },
113
- store: ctx.st,
113
+ store: {
114
+ checkpointCount: ctx.st.checkpointCount,
115
+ totalTokenEstimate: ctx.st.totalTokenEstimate,
116
+ originalTokens: ctx.st.originalTokens,
117
+ tokensSaved: ctx.st.tokensSaved,
118
+ injectedCount: ctx.st.injectedCount,
119
+ dedupHitRate: ctx.st.dedupHitRate,
120
+ // Session-scoped dedup stats from runtime counters, NOT the
121
+ // cumulative meta table (which is repo-wide and belongs in the
122
+ // repo card below). rt.dedupSkips/dedupAttempts reset on session
123
+ // restart so the session card shows only this session's dedup.
124
+ storageDedupRate: ctx.rt.dedupAttempts > 0 ? ctx.rt.dedupSkips / ctx.rt.dedupAttempts : 0,
125
+ dedupAttempts: ctx.rt.dedupAttempts,
126
+ dedupCollapsed: ctx.rt.dedupSkips,
127
+ },
114
128
  crew: {
115
129
  activeAgents: ctx.activeAgents,
116
130
  currentTurn: ctx.currentTurn,
@@ -135,13 +149,17 @@ export function buildDashboardSnapshot(ctx: SnapshotBuildContext): DashboardSnap
135
149
  integrity: ctx.di,
136
150
  cacheHits: {
137
151
  session: ctx.rt.dedupSkips + ctx.rt.recallInjections,
138
- total: ctx.st.dedupCollapsed + ctx.st.injectedCount,
152
+ // Total = cumulative dedup collapses (meta table) + session
153
+ // recall injections. The meta table is the authoritative
154
+ // cumulative counter for dedup; recall injections are tracked
155
+ // per-session in rt and there's no cumulative counter yet.
156
+ total: ctx.repo.dedupCollapsed,
139
157
  sessionTokensSaved: ctx.rt.cacheHitTokens,
140
- totalTokensSaved: ctx.st.dedupCollapsed > 0 ? ctx.st.dedupCollapsed * 100 : 0,
158
+ totalTokensSaved: ctx.repo.dedupCollapsed > 0 ? ctx.repo.dedupCollapsed * 100 : 0,
141
159
  },
142
160
  compacts: {
143
161
  session: ctx.rt.compactCount,
144
- total: ctx.st.checkpointCount,
162
+ total: ctx.repo.checkpointCount,
145
163
  },
146
164
  timeSaved: {
147
165
  compact: {
@@ -150,7 +168,7 @@ export function buildDashboardSnapshot(ctx: SnapshotBuildContext): DashboardSnap
150
168
  },
151
169
  cacheHit: {
152
170
  sessionSec: ctx.rt.cacheHitTokens / 1000,
153
- totalSec: (ctx.st.dedupCollapsed * 100) / 1000,
171
+ totalSec: (ctx.repo.dedupCollapsed * 100) / 1000,
154
172
  },
155
173
  },
156
174
  model: ctx.currentModel
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-mega-compact",
3
- "version": "0.12.2",
3
+ "version": "0.12.4",
4
4
  "description": "Layered, local, vector-backed context compressor for pi — supersede/collapse/cluster compaction with deduped inline recall.",
5
5
  "type": "module",
6
6
  "license": "BSD-3-Clause",
@@ -77,10 +77,11 @@ export function buildCheckpointNodes(
77
77
  let prevId: string | null = null;
78
78
  for (const row of rows) {
79
79
  const id = String(row.id);
80
+ const rowSessionId = sessionId || String(row.session_id ?? "");
80
81
  const summary = String(row.summary ?? "");
81
82
  const node: MemoryGraphNode = {
82
83
  id,
83
- sessionId,
84
+ sessionId: rowSessionId,
84
85
  label: id,
85
86
  summaryTruncated: summary.length > 200 ? summary.slice(0, 197) + "..." : summary,
86
87
  tokenEstimate: Number(row.token_estimate ?? 0),
@@ -149,7 +150,7 @@ export function buildTurnNodes(
149
150
  const rows = sessionId
150
151
  ? (db
151
152
  .prepare(
152
- `SELECT turn_index, role, pressure_band, ctx_tokens, ctx_percent,
153
+ `SELECT turn_index, session_id, role, pressure_band, ctx_tokens, ctx_percent,
153
154
  epoch_id, ended_at
154
155
  FROM turns
155
156
  WHERE session_id = ?
@@ -158,7 +159,7 @@ export function buildTurnNodes(
158
159
  .all(sessionId) as Array<Record<string, unknown>>)
159
160
  : (db
160
161
  .prepare(
161
- `SELECT turn_index, role, pressure_band, ctx_tokens, ctx_percent,
162
+ `SELECT turn_index, session_id, role, pressure_band, ctx_tokens, ctx_percent,
162
163
  epoch_id, ended_at
163
164
  FROM turns
164
165
  ORDER BY turn_index ASC`,
@@ -170,7 +171,8 @@ export function buildTurnNodes(
170
171
  let prevId: string | null = null;
171
172
  for (const row of rows) {
172
173
  const ti = Number(row.turn_index);
173
- const nodeId = `turn:${sessionId}:${ti}`;
174
+ const rowSessionId = sessionId || String(row.session_id ?? "");
175
+ const nodeId = `turn:${rowSessionId}:${ti}`;
174
176
  if (existingIds.has(nodeId)) continue;
175
177
  existingIds.add(nodeId);
176
178
 
@@ -179,7 +181,7 @@ export function buildTurnNodes(
179
181
 
180
182
  const node: MemoryGraphNode = {
181
183
  id: nodeId,
182
- sessionId,
184
+ sessionId: rowSessionId,
183
185
  label: `Turn ${ti}`,
184
186
  summaryTruncated: `Turn ${ti}: ${role}${row.epoch_id ? ` (epoch: ${String(row.epoch_id)})` : ""}`,
185
187
  tokenEstimate: row.ctx_tokens ? Number(row.ctx_tokens) : 0,