pi-fold 1.0.3 → 2.0.0

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.
@@ -53,7 +53,7 @@ import {
53
53
  MAX_FOLD_SPAN_CHARS,
54
54
  PEEK_DEFAULT_MAX_BYTES,
55
55
  PEEK_HEAD_SHARE,
56
- STATUS_DIET_SUGGESTIONS,
56
+ STATUS_DIET_INDEX_ROWS,
57
57
  zoneBytes,
58
58
  } from "./policy.ts";
59
59
  import type {
@@ -88,16 +88,7 @@ export function selectAutomaticChapter(
88
88
  maximumSourceRefs: number = snapshot.policy.maxFoldSourceRefs,
89
89
  claimed: ReadonlySet<string> = new Set<string>(),
90
90
  ): FoldCandidate | null {
91
- // Every structurally closed unit is a candidate. What keeps a chapter off the newest
92
- // material is `refsProtected` below -- the fresh byte tail and the agent's pins -- and
93
- // what keeps it off the running excursion's own evidence is the guard adjudicated at
94
- // the commit. There is no position filter here: a unit is composable or it is not.
95
91
  const units = chapterUnits(snapshot);
96
- // An automatic chapter is raw material, always. Letting a chapter swallow placeholders
97
- // once the root count reached `consolidateAfter` was the old approximation of
98
- // consolidation, and the count law replaces it outright: a placeholder gets a parent
99
- // when the count says it must, under a fold that says what it is, not as a side effect
100
- // of which chapter happened to be composable that pass (Shane 2026-08-10).
101
92
  const allowedChildren = NO_FOLD_KINDS;
102
93
  for (let unitIndex = 0; unitIndex < units.length; unitIndex += 1) {
103
94
  const first = units[unitIndex];
@@ -117,15 +108,7 @@ export function selectAutomaticChapter(
117
108
  if (claimed.size && refs.some((ref) => claimed.has(objectRefKey(ref)))) continue;
118
109
  if (refsProtected(refs, state, snapshot)) continue;
119
110
  const size = bytes(encodedFoldSource(snapshot, state, parts, "chapter"));
120
- // The chapter cap bounds what the rung COMPOSES, never what it may reclaim: a
121
- // single closed unit that alone exceeds it is still accepted (rep 19: six status
122
- // results, each above the cap, were exactly the mass nothing could fold).
123
111
  if (size > snapshot.policy.maxChapterChars && endIndex > unitIndex) break;
124
- // Bite-sized by construction. A multi-unit chapter never grows past the cap, so
125
- // the ladder cannot build the 60kB fold whose tail hid the answer (rep 6); a
126
- // SINGLE unit that alone exceeds it is still accepted, because there is no
127
- // interior boundary to stop at and refusing would leave the biggest span in the
128
- // window as the one thing nothing may reclaim.
129
112
  const biteSized = size <= MAX_FOLD_SPAN_CHARS || endIndex === unitIndex;
130
113
  if (size >= snapshot.policy.minChapterChars && biteSized) best = { kind: "chapter", parts, sourceRefs: refs };
131
114
  if (size > MAX_FOLD_SPAN_CHARS) break;
@@ -135,20 +118,6 @@ export function selectAutomaticChapter(
135
118
  return null;
136
119
  }
137
120
 
138
- /**
139
- * ONE automatic selection law for stale material.
140
- *
141
- * There is no chapter rung and no consolidation rung any more, each with its own
142
- * trigger: automation proposes the stalest eligible SPAN, and what the span contains
143
- * decides the fold's kind. Raw narrative folds as a chapter; a parent the root count
144
- * requires folds as a consolidation. Nothing here is pressure-scaled: the epoch's own
145
- * commit trigger decides WHEN, and this decides WHAT.
146
- *
147
- * One candidate, because that is what a single inline rung can apply. The count law
148
- * itself is not rationed this way: the commit epoch takes EVERY group the count requires
149
- * in one pass, and this is the leftover route by which the stalest of them can also ride
150
- * an epoch's own rewrite.
151
- */
152
121
  export function selectAutomaticStaleSpan(
153
122
  snapshot: ActiveContextSnapshot,
154
123
  state: ActiveContextState,
@@ -162,10 +131,6 @@ export function selectAutomaticStaleSpan(
162
131
  return at(folds) < at(chapter) ? folds : chapter;
163
132
  }
164
133
 
165
- /**
166
- * What the commit epoch proposes: a completed read-only tool batch if one is stale, and
167
- * otherwise the stalest span the one law composes. This is the whole automatic path.
168
- */
169
134
  export function selectAutomaticSpan(
170
135
  snapshot: ActiveContextSnapshot,
171
136
  state: ActiveContextState,
@@ -185,14 +150,6 @@ export type AutomaticRungSelection =
185
150
 
186
151
  export interface AutomaticRungSelectionOptions {
187
152
  toolOnly?: boolean;
188
- summarizerAvailable?: boolean;
189
- failedPreparationIds?: ReadonlySet<string>;
190
- /**
191
- * Evidence and folds already spoken for by something that is not yet a fold, i.e.
192
- * a pending epoch mark. Excluding them makes each eligible turn select NEW stale
193
- * content instead of re-selecting the batch a mark already covers, which is the
194
- * only way marks accumulate below the commit threshold.
195
- */
196
153
  claimed?: ReadonlySet<string>;
197
154
  claimedFoldIds?: ReadonlySet<string>;
198
155
  }
@@ -224,24 +181,12 @@ export function selectAutomaticRung(
224
181
  if (options.toolOnly || !Number.isFinite(ratio)) return null;
225
182
  const refold = selectAutomaticRefold(snapshot, state, claimedFoldIds);
226
183
  if (refold) return { kind: "refold", foldId: refold };
227
- // One law decides the span; only the CHAPTER kind still has a preparation path in
228
- // front of it, because only a chapter brief is worth a model call.
229
184
  const span = selectAutomaticStaleSpan(snapshot, state, claimed);
230
185
  if (span?.kind === "consolidation") return { kind: "consolidation", candidate: span };
231
186
  const chapter = span;
232
187
  if (!chapter) return null;
233
- const preparationFailed = options.failedPreparationIds?.has(
234
- automaticPreparationId(chapter, state),
235
- ) ?? false;
236
- if (ratio >= hardFenceRatio(snapshot)) {
237
- return !options.summarizerAvailable || preparationFailed
238
- ? { kind: "chapter", candidate: chapter }
239
- : { kind: "chapter-prepare", candidate: chapter };
240
- }
241
- if ((options.summarizerAvailable && ratio >= snapshot.policy.warmRatio) ||
242
- ((!options.summarizerAvailable || preparationFailed) && ratio >= snapshot.policy.prepareRatio)) {
243
- return { kind: "chapter-prepare", candidate: chapter };
244
- }
188
+ if (ratio >= hardFenceRatio(snapshot)) return { kind: "chapter", candidate: chapter };
189
+ if (ratio >= snapshot.policy.prepareRatio) return { kind: "chapter-prepare", candidate: chapter };
245
190
  return null;
246
191
  }
247
192
 
@@ -250,12 +195,10 @@ export function foldCandidatesDetail(
250
195
  state: ActiveContextState,
251
196
  ratio: number | null,
252
197
  options: {
253
- summarizerAvailable?: boolean;
254
198
  generation?: number;
255
199
  measurementFresh?: boolean;
256
200
  automaticFailure?: boolean;
257
201
  preparing?: boolean;
258
- failedPreparationIds?: ReadonlySet<string>;
259
202
  } = {},
260
203
  ): Record<string, unknown> {
261
204
  const measuredRatio = ratio !== null && Number.isFinite(ratio) ? ratio : Number.NaN;
@@ -264,10 +207,7 @@ export function foldCandidatesDetail(
264
207
  const staleSpan = selectAutomaticStaleSpan(snapshot, state);
265
208
  const consolidation = staleSpan?.kind === "consolidation" ? staleSpan : null;
266
209
  const chapter = staleSpan?.kind === "chapter" ? staleSpan : null;
267
- const selection = selectAutomaticRung(snapshot, state, measuredRatio, {
268
- summarizerAvailable: options.summarizerAvailable,
269
- failedPreparationIds: options.failedPreparationIds,
270
- });
210
+ const selection = selectAutomaticRung(snapshot, state, measuredRatio);
271
211
  let wouldFireNow: string | null = null;
272
212
  let blockedBy: string | null = null;
273
213
  if (options.measurementFresh === false) blockedBy = "measurement-stale";
@@ -296,9 +236,6 @@ export function foldCandidatesDetail(
296
236
  } : null,
297
237
  wouldFireNow,
298
238
  blockedBy,
299
- // The one position that still cuts anything, plus the count law read as the
300
- // arithmetic it is: the roots it counts, the width it divides by, and how many
301
- // parents that count owes right now.
302
239
  zones: {
303
240
  freshBoundary: snapshot.freshBoundary,
304
241
  budgetTokens: snapshot.budgetTokens,
@@ -369,25 +306,6 @@ export function encodedFoldSource(
369
306
  }));
370
307
  }
371
308
 
372
- /**
373
- * What a generator reads to brief a PARENT: its children at depth one.
374
- *
375
- * `encodedFoldSource` renders a parent's children through `renderFold`, which yields their
376
- * placeholders, so a parent's source was the children's briefs. Briefing that produces a
377
- * summary of summaries, and by the second rung the material itself is three descriptions
378
- * away. So each child is opened here instead: its own brief for orientation, then its
379
- * contents, with ITS children left as placeholders. Depth one is the whole rule. It bounds
380
- * the read at one level per rung whatever the ladder's height, and it is the same view an
381
- * agent gets from expanding the parent, so a brief written from it describes what expanding
382
- * would actually return.
383
- *
384
- * When the opened children do not fit, the LARGEST collapses back to brief-only first, and
385
- * again until the rest fit. Size is what decides, not position: dropping in span order
386
- * would spend the whole budget on an early giant and close every child behind it, while
387
- * taking the giant out first buys back the most room for the fewest subjects lost. A
388
- * collapsed child still appears, in span order, carrying its brief and saying it is
389
- * collapsed, so the generator can tell a child it read from a child it only heard about.
390
- */
391
309
  export function consolidationSourceText(
392
310
  snapshot: ActiveContextSnapshot,
393
311
  state: ActiveContextState,
@@ -417,8 +335,6 @@ export function consolidationSourceText(
417
335
  ? { collapsed: "brief only: this child did not fit expanded" }
418
336
  : { contents: entry.messages }),
419
337
  })));
420
- // Largest first, and only as many as the budget forces: each collapse is a subject the
421
- // brief must describe from one sentence instead of from its contents.
422
338
  const bySize = entries
423
339
  .filter((entry) => entry.foldId !== null)
424
340
  .map((entry) => ({ index: entry.index, size: bytes(entry.messages) }))
@@ -459,17 +375,6 @@ export function boundedOrientation(
459
375
  return { beforeRefs: before, beforeText, afterRefs: after.refs, afterText: after.text };
460
376
  }
461
377
 
462
- /**
463
- * What is wrong with a generated brief, said in words its author can act on, or null.
464
- *
465
- * Length is a criterion we set, so a brief that misses it is answered rather than cut.
466
- * Truncating a long brief silently loses whatever the generator put last, which on a group
467
- * is the last children it covered; the cure is to hand the complaint back and let the
468
- * generator spend the budget itself. Coverage cannot be checked the same way: no test tells
469
- * whether a sentence stands for a child, so coverage is instructed in the request and
470
- * defended by the deterministic index underneath, and what is checked here is the pair of
471
- * failures that were silent before.
472
- */
473
378
  export function briefContractComplaint(
474
379
  brief: string,
475
380
  maxBriefChars: number,
@@ -488,140 +393,13 @@ export function briefContractComplaint(
488
393
  return null;
489
394
  }
490
395
 
491
- /**
492
- * One brief from the generator, with one chance to cure a criterion it missed.
493
- *
494
- * The no-retry rule this lane was built with holds for what it was written about: a
495
- * generator that fails on a span fails on it again, and looping would spend the session's
496
- * calls proving it. A contract complaint is the other case. The request stated a limit, the
497
- * answer missed it, and the complaint names exactly what to change, so the second ask is
498
- * new information rather than the same ask twice. It is bounded at one, and only a
499
- * complaint earns it: transport faults, timeouts and attribution drift still fail on the
500
- * first answer. Both attempts sit inside the caller's timeout, so a brief costs no more
501
- * wall clock than it did.
502
- */
503
- export async function generatedBrief(input: {
504
- summarize: (request: Record<string, unknown>, ctx?: unknown) => Promise<Record<string, unknown>>;
505
- request: Record<string, unknown>;
506
- ctx?: unknown;
507
- maxBriefChars: number;
508
- toolName: string;
509
- }): Promise<{ brief: string; provenance: BriefProvenance; cured: boolean }> {
510
- let complaint: string | null = null;
511
- for (let attempt = 0; attempt < 2; attempt += 1) {
512
- const result = await input.summarize(
513
- complaint === null ? input.request : { ...input.request, cure: complaint },
514
- input.ctx,
515
- );
516
- const brief = typeof result?.brief === "string" ? result.brief.trim() : "";
517
- const provenance = generatorAttribution(result);
518
- complaint = briefContractComplaint(brief, input.maxBriefChars, input.toolName);
519
- if (complaint === null) return { brief, provenance, cured: attempt > 0 };
520
- }
521
- throw new Error(`Model context brief usefulness contract drift after one cure: ${complaint}`);
522
- }
523
-
524
- /**
525
- * Attribution is the harness's own wiring, not the generator's judgment, so it is never
526
- * handed back to be cured: a summarizer that cannot say which model wrote a brief is broken
527
- * in a way a second ask cannot mend. Shared by the single and batched paths so the two
528
- * cannot drift.
529
- */
530
- function generatorAttribution(result: Record<string, unknown> | null | undefined): BriefProvenance {
531
- const digest = result?.launchContractDigest;
532
- if (typeof result?.provider !== "string" || !result.provider ||
533
- typeof result?.model !== "string" || !result.model ||
534
- typeof result?.effort !== "string" || !result.effort || result.toolCalls !== 0 ||
535
- (digest !== undefined && (typeof digest !== "string" || !/^[a-f0-9]{64}$/.test(digest)))) {
536
- throw new Error("Model context brief attribution, zero-tool, or digest contract drift");
537
- }
538
- return {
539
- kind: "model",
540
- provider: result.provider,
541
- model: result.model,
542
- effort: result.effort,
543
- ...(typeof digest === "string" ? { launchContractDigest: digest } : {}),
544
- };
545
- }
546
-
547
- /**
548
- * Many briefs from one call, with one chance to cure the ones that missed.
549
- *
550
- * The lane used to send one request per fold, two at a time, which meant most folds never
551
- * reached a generator at all: the 2026-08-11 rep committed 87 folds and applied 14 model
552
- * briefs, the rest keeping the deterministic sentence because the queue was full. A commit
553
- * folds its spans together, so they are briefed together, and coverage stops competing with
554
- * throughput.
555
- *
556
- * The cure is per-span and the retry carries only the spans that failed, so one bad brief
557
- * does not make the model rewrite eleven good ones. A span still failing after its cure
558
- * keeps its deterministic brief and is named in the error, which is the same outcome a
559
- * single-span failure has always had; the difference is that its neighbours still land.
560
- */
561
- export async function generatedBriefs(input: {
562
- summarize: (request: Record<string, unknown>, ctx?: unknown) => Promise<Record<string, unknown>>;
563
- request: Record<string, unknown>;
564
- spans: Array<Record<string, unknown>>;
565
- ctx?: unknown;
566
- maxBriefChars: number;
567
- toolName: string;
568
- }): Promise<{
569
- briefs: Array<{ brief: string; provenance: BriefProvenance } | null>;
570
- cured: number;
571
- complaints: string[];
572
- }> {
573
- const settled = new Array<{ brief: string; provenance: BriefProvenance } | null>(input.spans.length)
574
- .fill(null);
575
- let pending = input.spans.map((span, index) => ({ span, index }));
576
- let cured = 0;
577
- let complaints: string[] = [];
578
- for (let attempt = 0; attempt < 2 && pending.length; attempt += 1) {
579
- const asked = pending;
580
- const result = await input.summarize({
581
- ...input.request,
582
- spans: asked.map((item) => item.span),
583
- ...(complaints.length
584
- ? {
585
- cure: asked.map((item, at) =>
586
- `Brief ${at + 1}: ${complaints[at] ?? "did not meet the stated criteria."}`).join(" "),
587
- }
588
- : {}),
589
- }, input.ctx);
590
- const provenance = generatorAttribution(result);
591
- const written = Array.isArray(result?.briefs) ? result.briefs : [];
592
- const failed: typeof pending = [];
593
- const next: string[] = [];
594
- for (let at = 0; at < asked.length; at += 1) {
595
- const brief = typeof written[at] === "string" ? (written[at] as string).trim() : "";
596
- const complaint = briefContractComplaint(brief, input.maxBriefChars, input.toolName);
597
- if (complaint === null) {
598
- settled[asked[at].index] = { brief, provenance };
599
- if (attempt > 0) cured += 1;
600
- continue;
601
- }
602
- failed.push(asked[at]);
603
- next.push(complaint);
604
- }
605
- pending = failed;
606
- complaints = next;
607
- }
608
- return { briefs: settled, cured, complaints };
609
- }
610
-
611
396
  export async function prepareFold(input: {
612
397
  candidate: FoldCandidate;
613
398
  snapshot: ActiveContextSnapshot;
614
399
  state: ActiveContextState;
615
400
  generation: number;
616
401
  brief?: string;
617
- /**
618
- * Provenance for a supplied brief. A full record round-trips a brief whose
619
- * generator already ran (a pending mark carrying a model brief); the string
620
- * forms stay the shorthand for the two model-free kinds.
621
- */
622
402
  briefProvenance?: "supplied" | "deterministic" | BriefProvenance;
623
- summarize?: (request: Record<string, unknown>, ctx?: unknown) => Promise<Record<string, unknown>>;
624
- onSummarizerFailure?: (error: unknown) => void;
625
403
  ctx?: unknown;
626
404
  signal?: AbortSignal;
627
405
  now?: () => number;
@@ -635,93 +413,26 @@ export async function prepareFold(input: {
635
413
  throw new Error("Fold source is not exact, stale, and unprotected");
636
414
  }
637
415
  const sourceText = encodedFoldSource(snapshot, state, candidate.parts, candidate.kind);
638
- const groupChildren = candidate.parts.filter((part) => part.kind === "fold").length;
639
- const readableSource = groupChildren
640
- ? consolidationSourceText(snapshot, state, candidate.parts)
641
- : sourceText;
642
416
  const orientation = boundedOrientation(snapshot, candidate.sourceRefs);
643
417
  const sourceSha256 = sha256Text(sourceText);
644
418
  const beforeSha256 = sha256Text(orientation.beforeText);
645
419
  const afterSha256 = sha256Text(orientation.afterText);
646
- const candidateId = sha256Value({
647
- kind: candidate.kind,
648
- parts: candidate.parts.map(normalizedPart),
649
- sourceSha256,
650
- beforeSha256,
651
- afterSha256,
652
- });
653
420
 
654
421
  let brief: string;
655
422
  let provenance: BriefProvenance;
656
423
  if (input.brief !== undefined) {
657
- if (!usefulBrief(input.brief, snapshot.policy.maxBriefChars, snapshot.toolName)) {
658
- throw new Error(`Supplied brief must be non-structural and at most ${snapshot.policy.maxBriefChars} characters`);
659
- }
424
+ const complaint = briefContractComplaint(
425
+ input.brief.trim(), snapshot.policy.maxBriefChars, snapshot.toolName);
426
+ if (complaint !== null) throw new Error(`Supplied brief rejected. ${complaint}`);
660
427
  brief = input.brief.trim();
661
428
  provenance = typeof input.briefProvenance === "object"
662
429
  ? clone(input.briefProvenance)
663
430
  : { kind: input.briefProvenance ?? "supplied" };
664
431
  } else {
665
- let modelBrief: { brief: string; provenance: BriefProvenance } | null = null;
666
- if (input.summarize) {
667
- const controller = new AbortController();
668
- const relayAbort = (): void => controller.abort();
669
- input.signal?.addEventListener("abort", relayAbort, { once: true });
670
- let timeout: ReturnType<typeof setTimeout> | undefined;
671
- try {
672
- if (bytes(readableSource) > snapshot.policy.maxSourceChars) {
673
- throw new Error("Fold source exceeds the bounded model-summary request");
674
- }
675
- const timed = new Promise<never>((_resolve, reject) => {
676
- timeout = setTimeout(() => {
677
- controller.abort();
678
- reject(new Error(`Context brief exceeded ${snapshot.policy.briefTimeoutMs}ms`));
679
- }, snapshot.policy.briefTimeoutMs);
680
- });
681
- const generated = await Promise.race([generatedBrief({
682
- summarize: input.summarize,
683
- request: {
684
- candidateId,
685
- sourceRefs: clone(candidate.sourceRefs),
686
- // What the generator READS, which for a group is its children opened one level
687
- // rather than the placeholders `sourceText` renders. The fold's own identity
688
- // stays `sourceSha256` above: what a span IS does not change with how it is read.
689
- sourceText: readableSource,
690
- sourceSha256: sha256Text(readableSource),
691
- children: groupChildren,
692
- beforeRefs: clone(orientation.beforeRefs),
693
- beforeText: orientation.beforeText,
694
- beforeSha256,
695
- afterRefs: clone(orientation.afterRefs),
696
- afterText: orientation.afterText,
697
- afterSha256,
698
- maxBriefChars: snapshot.policy.maxBriefChars,
699
- signal: controller.signal,
700
- },
701
- ctx: input.ctx,
702
- maxBriefChars: snapshot.policy.maxBriefChars,
703
- toolName: snapshot.toolName,
704
- }), timed]);
705
- modelBrief = { brief: generated.brief, provenance: generated.provenance };
706
- } catch (error) {
707
- if (input.signal?.aborted) throw error;
708
- input.onSummarizerFailure?.(error);
709
- } finally {
710
- clearTimeout(timeout);
711
- input.signal?.removeEventListener("abort", relayAbort);
712
- }
713
- }
714
- if (modelBrief) {
715
- brief = modelBrief.brief;
716
- provenance = modelBrief.provenance;
717
- } else {
718
- // By kind, not by assumption: a group that loses its generator falls back to the
719
- // index of its children, and only a chapter falls back to a chapter's brief.
720
- brief = candidate.kind === "consolidation"
721
- ? deterministicConsolidationBrief(candidate, state)
722
- : deterministicChapterCandidateBrief(snapshot, candidate);
723
- provenance = { kind: "deterministic" };
724
- }
432
+ brief = candidate.kind === "consolidation"
433
+ ? deterministicConsolidationBrief(candidate, state, snapshot.toolName, snapshot)
434
+ : deterministicChapterCandidateBrief(snapshot, candidate);
435
+ provenance = { kind: "deterministic" };
725
436
  }
726
437
 
727
438
  const id = foldIdFor(candidate.kind, candidate.parts);
@@ -910,9 +621,6 @@ export function commitPreparedFold(input: {
910
621
  if (error) throw new Error(`Prepared active-context fold discarded: ${error}`);
911
622
  const nested = stateWithNestedFold(input.state, input.prepared.fold);
912
623
  const next = clearPrepared({ ...nested, revision: input.state.revision + 1 });
913
- // A fold is not committed unless its provider projection preserves every
914
- // existing tool-call/output pair atomically. This keeps malformed provider
915
- // context non-constructible even if a future selector regresses.
916
624
  projectActiveContext(input.snapshot, next);
917
625
  return next;
918
626
  }
@@ -975,8 +683,6 @@ export function protectEvidence(
975
683
  });
976
684
  const byKey = new Map(state.protected.map((ref) => [objectRefKey(ref), ref]));
977
685
  for (const ref of refs) protect ? byKey.set(objectRefKey(ref), ref) : byKey.delete(objectRefKey(ref));
978
- // Atomic: the whole resolved set pins or nothing does. A partial pin that reports
979
- // success would leave part of the requested span silently reclaimable.
980
686
  if (byKey.size > MAX_ACTIVE_PROTECTED) {
981
687
  throw new Error(
982
688
  `Protecting ${ids.join(", ")} would hold ${byKey.size} refs, over the ${MAX_ACTIVE_PROTECTED}-ref cap; ` +
@@ -1105,13 +811,6 @@ export function assertProjectionPreservesToolLinkage(source: unknown[], projecte
1105
811
  }
1106
812
  }
1107
813
 
1108
- /**
1109
- * Peek mass the ladder cannot take: results whose evidence the agent protected
1110
- * outright. This is the number that made rep 7's starvation invisible. The mass sat
1111
- * raw in the window, the ROI trigger's eligibility arithmetic could never reach its
1112
- * threshold over it, and no accounting field said so while the window grew to 235k
1113
- * tokens.
1114
- */
1115
814
  export function pinnedPeekMass(
1116
815
  snapshot: ActiveContextSnapshot,
1117
816
  state: ActiveContextState,
@@ -1170,13 +869,6 @@ export function projectActiveContext(
1170
869
  index += 1;
1171
870
  }
1172
871
  }
1173
- // A peek result is APPEND-ONLY. It used to be rewritten in place once a model call
1174
- // had seen it, on the theory that the edit was tail-local; rep 22 disproved that.
1175
- // The result is only tail-local when it ARRIVES, and reclamation deliberately waits
1176
- // until a later assistant message exists, by which point the window has grown over
1177
- // it and the edit lands mid-prefix. Two peeks cost 100k fresh tokens that way. The
1178
- // duplicate bytes are still reclaimed, by the tool-fold rung at the next commit,
1179
- // which is the one moment a rewrite is already being paid for.
1180
872
  assertProjectionPreservesToolLinkage(snapshot.messages, output);
1181
873
  return output;
1182
874
  }
@@ -1205,9 +897,6 @@ export function foldStatusRow(fold: ActiveFold, state: ActiveContextState, snaps
1205
897
  sourceCount: allSourceIds.length,
1206
898
  sourceIdsTruncated: sourceIds.length < allSourceIds.length,
1207
899
  sourceSha256: fold.sourceSha256,
1208
- // A paged row without the brief is a chronological id and nothing else: the
1209
- // agent that paged the tree to find WHICH fold holds something got the one
1210
- // field that answers it withheld, while the tree detail carried it all along.
1211
900
  brief: foldBrief(fold, state),
1212
901
  sourceChars: fold.sourceChars,
1213
902
  actions: {
@@ -1221,7 +910,6 @@ export function foldStatusRow(fold: ActiveFold, state: ActiveContextState, snaps
1221
910
  };
1222
911
  }
1223
912
 
1224
- /** Every fold, nested ones included, in transcript order with each parent before its children. */
1225
913
  export function orderedFoldTree(state: ActiveContextState, snapshot: ActiveContextSnapshot): ActiveFold[] {
1226
914
  const byId = foldMap(state);
1227
915
  const ordered: ActiveFold[] = [];
@@ -1260,12 +948,6 @@ export function foldTreeDetail(
1260
948
  }));
1261
949
  }
1262
950
 
1263
- /**
1264
- * One compact row per collapsed fold: what is behind it, where its span starts and
1265
- * ends, and the brief that carries whatever key material the fold captured. This is
1266
- * the answer to "which fold has X" that agents were paying 56-83k chars per peek to
1267
- * reconstruct, and it costs a line.
1268
- */
1269
951
  export function foldIndexRow(
1270
952
  fold: ActiveFold,
1271
953
  state: ActiveContextState,
@@ -1294,7 +976,6 @@ export function foldIndexRow(
1294
976
  };
1295
977
  }
1296
978
 
1297
- /** Every mapped source id to the fold that now holds it, so a lookup never needs a read. */
1298
979
  export function foldSourceMap(
1299
980
  state: ActiveContextState,
1300
981
  snapshot: ActiveContextSnapshot,
@@ -1304,7 +985,6 @@ export function foldSourceMap(
1304
985
  for (const fold of orderedFoldTree(state, snapshot)) {
1305
986
  for (const ref of flattenFoldRefs(fold, state)) entries.push([ref.entryId, fold.id]);
1306
987
  }
1307
- // Deepest wins: the innermost fold is the one that actually holds the source.
1308
988
  const byEntry = new Map(entries);
1309
989
  const all = [...byEntry.entries()];
1310
990
  return { entries: all.slice(0, limit), total: all.length, truncated: all.length > limit };
@@ -1316,7 +996,7 @@ export function activeContextStatus(
1316
996
  offset = 0,
1317
997
  limit = 40,
1318
998
  maximumChapterSourceRefs = Number.MAX_SAFE_INTEGER,
1319
- statusOptions: { diet?: boolean; suggestions?: number } = {},
999
+ statusOptions: { diet?: boolean; indexRows?: number } = {},
1320
1000
  ): Record<string, unknown> {
1321
1001
  const roots = orderedRoots(state, snapshot).map((item) => item.fold.id);
1322
1002
  const ordered = orderedFoldTree(state, snapshot);
@@ -1339,17 +1019,13 @@ export function activeContextStatus(
1339
1019
  const eligibleEndpoints = eligibleSourceIds.length
1340
1020
  ? [...new Set([eligibleSourceIds[0], eligibleSourceIds.at(-1)!])]
1341
1021
  : [];
1342
- // The index that used to ride every status call grew from 12.6k to 86.5k chars in
1343
- // one measured session, injected on every request that asked for state. On the diet
1344
- // the default payload carries counts, the source map and the folds worth acting on;
1345
- // the full tree and the object list move behind an explicit paged query.
1346
1022
  const index = statusOptions.diet
1347
1023
  ? visibleCollapsedFolds(state, snapshot)
1348
1024
  .map((fold) => foldIndexRow(fold, state, snapshot))
1349
1025
  .sort((left, right) =>
1350
1026
  Number(right.reclaimableBytes) - Number(left.reclaimableBytes) ||
1351
1027
  String(left.id).localeCompare(String(right.id)))
1352
- .slice(0, statusOptions.suggestions ?? STATUS_DIET_SUGGESTIONS)
1028
+ .slice(0, statusOptions.indexRows ?? STATUS_DIET_INDEX_ROWS)
1353
1029
  : null;
1354
1030
  const sourceMap = statusOptions.diet ? foldSourceMap(state, snapshot) : null;
1355
1031
  return {
@@ -1401,20 +1077,8 @@ export function activeContextStatus(
1401
1077
  };
1402
1078
  }
1403
1079
 
1404
- /** Room reserved for the one-line continuation marker a truncated page carries. */
1405
1080
  const STATUS_CONTINUATION_RESERVE_BYTES = 400;
1406
1081
 
1407
- /**
1408
- * Enforce the hard status page cap on one fully assembled status payload.
1409
- *
1410
- * Measured 2026-08-07 (rep 19): one status answer serialized to 526KB, 6.2x the
1411
- * remaining headroom, and aborted the request. The paging affordance existed;
1412
- * nothing forced it. This function is the force: every listing in the payload is
1413
- * truncated at a unit boundary (whole rows, never mid-unit) until the serialized
1414
- * page fits, and a one-line continuation marker names the next offset. Trim order
1415
- * spends the diagnostics first and the listing the caller asked for last, and the
1416
- * requested listing always keeps at least one unit so paging always progresses.
1417
- */
1418
1082
  export function boundStatusPayload(
1419
1083
  payload: Record<string, unknown>,
1420
1084
  detail: string | null,
@@ -1426,9 +1090,6 @@ export function boundStatusPayload(
1426
1090
  const target = cap - STATUS_CONTINUATION_RESERVE_BYTES;
1427
1091
  const fits = (): boolean => size() <= target;
1428
1092
  const omitted: string[] = [];
1429
- // keepEnd keeps the NEWEST units of a time-ordered ledger; a paged listing keeps
1430
- // its head so the continuation offset is the next unread unit. Binary search for
1431
- // the largest keep that fits; a stage that cannot fit alone trims to its floor.
1432
1093
  const shrink = (owner: unknown, key: string, label: string, keepEnd = false, minKeep = 0): number => {
1433
1094
  if (!isPlainRecord(owner) || fits()) return 0;
1434
1095
  const original = ownValue(owner, key);
@@ -1452,16 +1113,22 @@ export function boundStatusPayload(
1452
1113
  };
1453
1114
  const automatic = ownValue(payload, "automatic");
1454
1115
  const instrumentation = isPlainRecord(automatic) ? ownValue(automatic, "instrumentation") : undefined;
1455
- const surfacing = isPlainRecord(automatic) ? ownValue(automatic, "surfacing") : undefined;
1116
+ const lastAction = isPlainRecord(automatic) ? ownValue(automatic, "lastAutomaticAction") : undefined;
1117
+ const lastEpoch = isPlainRecord(lastAction) ? ownValue(lastAction, "epoch") : undefined;
1456
1118
  shrink(instrumentation, "projectionRecords", "projection record(s)", true);
1457
1119
  shrink(instrumentation, "cacheObservations", "cache observation(s)", true);
1458
1120
  shrink(instrumentation, "events", "context event(s)", true);
1459
- shrink(surfacing, "log", "surfacing log row(s)", true);
1460
1121
  if (shrink(payload, "sourceMap", "source map row(s)") > 0) payload.sourceMapTruncated = true;
1461
1122
  shrink(payload, "topFolds", "top fold row(s)");
1123
+ // The last epoch's receipt lists grow with commit depth (a 0.20 minTarget epoch
1124
+ // applies dozens of marks). They are the commit's own receipt, so they outlast
1125
+ // every other diagnostic, but they still page away before any listing row the
1126
+ // caller actually asked for: on the detail pages, where none of the diagnostics
1127
+ // above exist, they are the mass that made one fold row miss the cap.
1128
+ const receiptRowsDropped =
1129
+ shrink(lastEpoch, "applied", "applied receipt row(s)", true) +
1130
+ shrink(lastEpoch, "absorbed", "absorbed receipt row(s)", true) > 0;
1462
1131
  const rowLabel = (key: string): string => key === "folds" ? "fold row(s)" : key === "objects" ? "object row(s)" : "tree row(s)";
1463
- // The listing the caller asked for goes last and never below one unit. Its
1464
- // machine-readable next offset moves back to the first unit the page dropped.
1465
1132
  let continueAt: number | null = null;
1466
1133
  if (detail === "folds" || detail === "objects") {
1467
1134
  const rideKey = detail === "folds" ? "objects" : "folds";
@@ -1478,9 +1145,17 @@ export function boundStatusPayload(
1478
1145
  else payload.nextOffset = continueAt;
1479
1146
  }
1480
1147
  if (!omitted.length) return payload;
1481
- const resume = continueAt === null
1482
- ? "the full lists stay reachable through the paged status details"
1148
+ // A dropped receipt row has no page: the epoch receipt is a record of the last
1149
+ // action, not a listing, and claiming it "stays reachable" pointed the agent at
1150
+ // a page that does not exist (2026-08-14 external review). The span-to-fold
1151
+ // mapping it summarizes stays complete in the folds and tree details, so the
1152
+ // continuation says exactly that instead.
1153
+ const pageable = continueAt === null
1154
+ ? "the fold, object and tree lists stay reachable through the paged status details"
1483
1155
  : `continue at ${JSON.stringify({ action: "status", detail, offset: continueAt })}`;
1156
+ const resume = receiptRowsDropped
1157
+ ? `${pageable}; the epoch receipt keeps only its newest rows and the trimmed rows have no page`
1158
+ : (continueAt === null ? "the full lists stay reachable through the paged status details" : pageable);
1484
1159
  payload.continuation =
1485
1160
  `Status pages are bounded to ${cap} bytes; omitted ${omitted.join(", ")}; ${resume}.`;
1486
1161
  return payload;
@@ -1559,7 +1234,6 @@ export interface FoldSourceRequest {
1559
1234
  projectEntry?: (entry: Record<string, unknown>) => unknown[];
1560
1235
  }
1561
1236
 
1562
- /** One exact message per ref identity and digest, read off the immutable session entries. */
1563
1237
  function exactSessionMessages(input: FoldSourceRequest): Map<string, unknown> {
1564
1238
  const projectEntry = input.projectEntry ?? sessionEntryMessages;
1565
1239
  const exact = new Map<string, unknown>();
@@ -1586,13 +1260,6 @@ function exactMessageFor(
1586
1260
  return clone(message);
1587
1261
  }
1588
1262
 
1589
- /**
1590
- * One fold's ENTIRE original source, at every depth.
1591
- *
1592
- * This is the rescue read: restoration and the recovery audit both need the bytes as the
1593
- * session first wrote them, with nothing standing in for anything. Retrieval inside a
1594
- * session is the other function, and it serves one level.
1595
- */
1596
1263
  export function recoverFoldMessages(input: FoldSourceRequest): unknown[] {
1597
1264
  const fold = input.state.folds.find((item) => item.id === input.foldId);
1598
1265
  if (!fold) throw new Error(`Unknown active-context fold ${input.foldId}`);
@@ -1600,7 +1267,6 @@ export function recoverFoldMessages(input: FoldSourceRequest): unknown[] {
1600
1267
  return flattenFoldRefs(fold, input.state).map((ref) => exactMessageFor(exact, ref, input.sessionId));
1601
1268
  }
1602
1269
 
1603
- /** A child fold as its parent's stored span holds it: the placeholder, not the bytes under it. */
1604
1270
  export function storedChildPlaceholder(child: ActiveFold, state: ActiveContextState): Record<string, unknown> {
1605
1271
  return {
1606
1272
  placeholder: "fold",
@@ -1614,18 +1280,6 @@ export function storedChildPlaceholder(child: ActiveFold, state: ActiveContextSt
1614
1280
  };
1615
1281
  }
1616
1282
 
1617
- /**
1618
- * One fold's stored span, AS STORED: one level.
1619
- *
1620
- * A fold's span is what it took in, and what it took in was already whatever the window
1621
- * held: raw entries, and placeholders for folds that were there first. So the honest read
1622
- * of a parent is that same span, children still placeheld. Reading it as the original
1623
- * bytes at every depth would hand back a whole session's transcript for one call and
1624
- * undo the nesting the fold performed.
1625
- *
1626
- * The verbatim floor is untouched by this. A leaf's stored span IS its original bytes, so
1627
- * the bytes are always exactly one peek away from any id the read hands back.
1628
- */
1629
1283
  export function foldStoredSpan(input: FoldSourceRequest): unknown[] {
1630
1284
  const fold = input.state.folds.find((item) => item.id === input.foldId);
1631
1285
  if (!fold) throw new Error(`Unknown active-context fold ${input.foldId}`);
@@ -1639,21 +1293,12 @@ export function foldStoredSpan(input: FoldSourceRequest): unknown[] {
1639
1293
  });
1640
1294
  }
1641
1295
 
1642
- /**
1643
- * Ephemeral point read of one fold's stored span. It returns the same SHA-256-verified
1644
- * span expansion restores, ONE level: raw entries verbatim, child folds still placeheld.
1645
- * It is bounded for one model call and changes no projection: the fold stays collapsed and
1646
- * the durable state is untouched. Any id in the result peeks in one hop, so the verbatim
1647
- * floor is a hop away at every depth rather than a payload nobody asked for.
1648
- */
1649
1296
  export function peekFoldSource(input: {
1650
1297
  foldId: string;
1651
1298
  state: ActiveContextState;
1652
1299
  entries: Array<Record<string, unknown>>;
1653
1300
  sessionId: string;
1654
- /** Explicit widening. Absent, a peek returns the bounded index view. */
1655
1301
  maximumBytes?: number;
1656
- /** Byte offset into the exact stored source; the narrowing half of admission control. */
1657
1302
  offset?: number;
1658
1303
  toolName?: string;
1659
1304
  projectEntry?: (entry: Record<string, unknown>) => unknown[];
@@ -1672,9 +1317,6 @@ export function peekFoldSource(input: {
1672
1317
  const offset = Math.min(Math.max(0, input.offset ?? 0), sourceBytes);
1673
1318
  const budget = input.maximumBytes ?? PEEK_DEFAULT_MAX_BYTES;
1674
1319
  const window = offset > 0 ? utf8Slice(source, offset) : source;
1675
- // Offset zero is the INDEX read and takes head AND tail, because chain keys and
1676
- // conclusions live at the end of a source and a head-only slice silently omits
1677
- // exactly that region. An explicit offset is paging and stays contiguous.
1678
1320
  const view = offset > 0
1679
1321
  ? { text: boundedUtf8(window, budget), omittedBytes: 0, contiguous: true }
1680
1322
  : boundedHeadTail(window, budget);
@@ -1685,8 +1327,6 @@ export function peekFoldSource(input: {
1685
1327
  : null;
1686
1328
  const truncated = view.omittedBytes > 0 || nextOffset !== null;
1687
1329
  const children = childFoldIds(fold);
1688
- // The index view: every nested fold's brief in FULL. A brief is the navigable unit,
1689
- // so truncating it is the one economy that costs the read its purpose.
1690
1330
  const index = descendantIndexRows(fold, input.state);
1691
1331
  return {
1692
1332
  version: 1,
@@ -1706,8 +1346,6 @@ export function peekFoldSource(input: {
1706
1346
  truncated,
1707
1347
  view: offset > 0 ? "slice" : (view.omittedBytes > 0 ? "index" : "complete"),
1708
1348
  ...(index.length ? { index } : {}),
1709
- // Narrower and WIDER reads that are always constructible, so a refusal or a
1710
- // truncation can name one.
1711
1349
  children,
1712
1350
  narrower: {
1713
1351
  ...(nextOffset === null ? {} : { slice: { action: "peek", id: fold.id, offset: nextOffset, bytes: returnedBytes } }),
@@ -1728,10 +1366,6 @@ export function peekFoldSource(input: {
1728
1366
  "placeheld. Peek a child id to read its own span; the fold stayed collapsed and no projection changed."
1729
1367
  : "Complete exact source; the fold stayed collapsed and no projection changed.",
1730
1368
  source: returned,
1731
- // Serialized AFTER the source on purpose: a reader that just consumed a bounded slice
1732
- // decides its next action at the end of the payload, and a notice buried before ten
1733
- // thousand source bytes is a notice unread (measured: a run concluded a staged chain
1734
- // was finished because the chain key lived in the unshown tail of a truncated peek).
1735
1369
  ...(truncated
1736
1370
  ? {
1737
1371
  truncationReminder: `STOP: ${view.omittedBytes || sourceBytes - returnedBytes} of ${sourceBytes} ` +
@@ -1746,16 +1380,10 @@ export function peekFoldSource(input: {
1746
1380
  };
1747
1381
  }
1748
1382
 
1749
- /** The marker a head+tail view puts where the omitted middle was. */
1750
1383
  export function headTailMarker(omittedBytes: number): string {
1751
1384
  return omittedBytes > 0 ? `\n... [${omittedBytes} exact source bytes omitted] ...\n` : "";
1752
1385
  }
1753
1386
 
1754
- /**
1755
- * A bounded view that keeps the head AND the tail. A head-only bound drops exactly the
1756
- * region a staged chain keeps its key in; the omitted size is stated where the reading
1757
- * ends so "there is more" is never an inference.
1758
- */
1759
1387
  export function boundedHeadTail(
1760
1388
  source: string,
1761
1389
  budget: number,
@@ -1776,7 +1404,6 @@ export function boundedHeadTail(
1776
1404
  };
1777
1405
  }
1778
1406
 
1779
- /** Every descendant fold, in transcript order, with its brief in full. */
1780
1407
  export function descendantIndexRows(
1781
1408
  fold: ActiveFold,
1782
1409
  state: ActiveContextState,