pi-fold 4.2.5 → 4.2.6

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.
@@ -1157,6 +1157,18 @@ export function registerActiveContext(pi: any, options: {
1157
1157
  }
1158
1158
  };
1159
1159
 
1160
+ // WHAT PI'S CONTEXT EVENT WOULD HAND OVER, BEFORE ONE HAS ARRIVED (2026-10-05). Pi's
1161
+ // ExtensionRunner.emitContext filters role "system" before any handler sees a message;
1162
+ // buildSessionContext does not. A `/fold` before the first context event cut folds from
1163
+ // the unfiltered view in session 01a10161: spans that crossed a system-prompt change
1164
+ // looked broken, and that commit erased 739 of 920 folds.
1165
+ const piContextMessages = (ctx: any): unknown[] | null => {
1166
+ const messages = ctx.sessionManager.buildSessionContext?.()?.messages;
1167
+ return Array.isArray(messages)
1168
+ ? messages.filter((message: any) => message?.role !== "system")
1169
+ : null;
1170
+ };
1171
+
1160
1172
  const snapshotForEvent = (ctx: any, messages: unknown[]): ActiveContextSnapshot =>
1161
1173
  timedDerivation(() => mapActiveContext({
1162
1174
  sessionId: ctx.sessionManager.getSessionId(),
@@ -1352,7 +1364,7 @@ export function registerActiveContext(pi: any, options: {
1352
1364
  persistence.deltasSinceCheckpoint = restoredPersistence?.deltasSinceCheckpoint ?? 0;
1353
1365
  persistence.persistedFoldRecords = restoredPersistence?.records ?? new Map<string, FoldRecordEntry>();
1354
1366
  persistence.persistedStateSha256 = restoredPersistence?.stateSha256 ?? semanticStateSha256(durableRestored);
1355
- const restoredMessages = ctx.sessionManager.buildSessionContext?.()?.messages;
1367
+ const restoredMessages = piContextMessages(ctx);
1356
1368
  measurements.lastProviderMeasurement = latestProviderContextMeasurement(
1357
1369
  Array.isArray(restoredMessages) ? restoredMessages : [],
1358
1370
  budgetWindowFor(ctx),
@@ -3897,6 +3909,26 @@ export function registerActiveContext(pi: any, options: {
3897
3909
  try { updateStatus(ctx); } catch { }
3898
3910
  return undefined;
3899
3911
  }
3912
+ // PI CHECKS BEFORE THE FIRST PROMPT, BEFORE ANY CONTEXT EVENT (2026-10-05). After a
3913
+ // context_edit (Pi's own retry recovery writes one per failed attempt) Pi stops trusting
3914
+ // the last provider count and estimates the whole raw transcript, folds unseen: 134% on
3915
+ // session 01a10834, 260% on 01a10161. This used to throw for want of a snapshot, suspend
3916
+ // folding and let native compaction run, which ran Pi out of memory on a 272MB session.
3917
+ // The context event that follows projects and commits on the projection itself, so the
3918
+ // boundary is cancelled and folding stays on.
3919
+ const currentSnapshot = lifecycle.latestSnapshot;
3920
+ if (!currentSnapshot || currentSnapshot.sessionId !== ctx.sessionManager.getSessionId()) {
3921
+ nativeCompaction.lastThresholdDecision = {
3922
+ handled: true,
3923
+ retry: false,
3924
+ reason: `${contextBrand(brandNoun)} blocked stock automatic compaction before the first context event; ` +
3925
+ "the projection is measured when the request is built",
3926
+ compactionReason: reason,
3927
+ nativeCompactionCompleted: false,
3928
+ };
3929
+ try { updateStatus(ctx); } catch { }
3930
+ return { cancel: true };
3931
+ }
3900
3932
  let handoff: Record<string, unknown> | null = null;
3901
3933
  try {
3902
3934
  const snapshot = authoritativeSnapshotFor(ctx);
@@ -4794,7 +4826,7 @@ export function registerActiveContext(pi: any, options: {
4794
4826
  if (!persistence.state) persistence.state = emptyActiveContextState(ctx.sessionManager.getSessionId());
4795
4827
  const snapshot = lifecycle.latestSnapshot
4796
4828
  ? authoritativeSnapshotFor(ctx)
4797
- : snapshotForEvent(ctx, ctx.sessionManager.buildSessionContext().messages);
4829
+ : snapshotForEvent(ctx, piContextMessages(ctx) ?? []);
4798
4830
  if (!lifecycle.latestSnapshot) lifecycle.latestSnapshot = snapshot;
4799
4831
  const divider = args.indexOf(" -- ");
4800
4832
  const selector = (divider >= 0 ? args.slice(0, divider) : args).trim();
@@ -4904,7 +4936,7 @@ export function registerActiveContext(pi: any, options: {
4904
4936
  }
4905
4937
  const snapshot = lifecycle.latestSnapshot
4906
4938
  ? authoritativeSnapshotFor(ctx)
4907
- : snapshotForEvent(ctx, ctx.sessionManager.buildSessionContext().messages);
4939
+ : snapshotForEvent(ctx, piContextMessages(ctx) ?? []);
4908
4940
  if (!lifecycle.latestSnapshot) lifecycle.latestSnapshot = snapshot;
4909
4941
  if (!persistence.state) throw new Error("The context window is empty; there is nothing to show yet");
4910
4942
 
@@ -69,8 +69,15 @@ export function contentDrifted(snapshot: ActiveContextSnapshot, ref: EvidenceRef
69
69
  return item !== null && item.ref!.sha256 !== ref.sha256;
70
70
  }
71
71
 
72
+ // PI NEVER SHOWS AN EXTENSION ITS SYSTEM MESSAGES (2026-10-05). The context event filters
73
+ // role "system" (ExtensionRunner.emitContext), so a system-prompt-change entry has no
74
+ // place in the window and cannot break a span. Folds cut from a view that did include them
75
+ // (a `/fold` before the first context event, session 01a10161) carry such refs; they are
76
+ // skipped here rather than unseating the fold and, at the next commit, its whole subtree.
72
77
  export function refsInOrder(snapshot: ActiveContextSnapshot, refs: EvidenceRef[]): number[] | null {
73
- const indices = refs.map((ref) => exactMapped(snapshot, ref)?.index ?? -1);
78
+ const placed = refs.filter((ref) => ref.role !== "system");
79
+ if (!placed.length) return null;
80
+ const indices = placed.map((ref) => exactMapped(snapshot, ref)?.index ?? -1);
74
81
  if (indices.some((index) => index < 0) || new Set(indices).size !== indices.length) return null;
75
82
  for (let index = 1; index < indices.length; index += 1) if (indices[index] !== indices[index - 1] + 1) return null;
76
83
  return indices;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-fold",
3
- "version": "4.2.5",
3
+ "version": "4.2.6",
4
4
  "description": "Lossless context folding for the Pi coding agent: only the oldest context changes; the fresh window stays untouched.",
5
5
  "keywords": [
6
6
  "pi-package",