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.
@@ -1,24 +1,5 @@
1
1
  import { evidenceSha256, sha256Value } from "../json.ts";
2
2
  import { ownValue } from "./canonical.ts";
3
- import {
4
- MAX_CONTEXT_ATTEMPT_RECORDS,
5
- MAX_PROJECTION_HASH_RECORDS,
6
- } from "./policy.ts";
7
-
8
- /**
9
- * Projection instrumentation.
10
- *
11
- * The iteration-1 ledger reported 19 "observed mutations", and reconstruction showed
12
- * only 7 of them were projection rewrites this runtime performed. The other 12 were
13
- * provider-side cache misses on projections that had only GROWN: message count up by
14
- * one, no message removed, system prompt and tool digests constant, and the request
15
- * still re-paid ~200k fresh tokens. One dial counting both makes a scheduling lever
16
- * look responsible for a cost no scheduling lever can remove.
17
- *
18
- * So the two are counted separately, and the append case is made PROVABLE from
19
- * artifacts: per-message digests of consecutive projections show exactly where, or
20
- * whether, the prefix diverged.
21
- */
22
3
 
23
4
  export type ProjectionChange = "identical" | "append" | "rewrite";
24
5
 
@@ -27,7 +8,6 @@ export interface ProjectionComparison {
27
8
  previousCount: number;
28
9
  nextCount: number;
29
10
  appendedCount: number;
30
- /** First index whose digest differs; null when the prefix is intact. */
31
11
  firstDivergentIndex: number | null;
32
12
  }
33
13
 
@@ -38,11 +18,6 @@ export function messageDigests(messages: readonly unknown[]): string[] {
38
18
  });
39
19
  }
40
20
 
41
- /**
42
- * Compare two consecutive projections by per-message digest. A pure append leaves the
43
- * whole previous prefix intact, which is precisely the condition under which a
44
- * provider cache SHOULD hit; anything else is a rewrite this runtime caused.
45
- */
46
21
  export function compareProjections(
47
22
  previous: readonly string[] | null,
48
23
  next: readonly string[],
@@ -84,41 +59,14 @@ export interface ProjectionRecord extends ProjectionComparison {
84
59
  prefixSha256: string;
85
60
  }
86
61
 
87
- /** One provider response read against the projection that produced it. */
88
62
  export interface CacheObservation {
89
63
  ordinal: number;
90
64
  change: ProjectionChange;
91
65
  inputTokens: number;
92
66
  cacheReadTokens: number;
93
- /** A fresh re-read of a prefix that did not move: nothing here is ours to fix. */
94
67
  providerSideMiss: boolean;
95
68
  }
96
69
 
97
- /**
98
- * THE context-management event stream.
99
- *
100
- * One stream, one envelope, one naming convention. Every context event this runtime
101
- * produces -- tool attempts and their refusals, span corrections, gate lifecycle,
102
- * receipts, commits, absorptions, splits, recoveries, projections and provider usage --
103
- * lands here as a flat JSONL-style record, and every record is also appended as a
104
- * durable session entry so an analyst can reconstruct the whole timeline from session
105
- * artifacts alone.
106
- *
107
- * The envelope is fixed and self-describing:
108
- * v schema version of the envelope
109
- * seq monotonic per session, from 1, so records order without a clock
110
- * kind "context.<noun>", the only field a reader must know to dispatch
111
- * session_id the session these events belong to
112
- * ordinal transcript position, the deterministic clock this runtime reasons in
113
- * at wall-clock milliseconds, the join key against provider-side telemetry
114
- * revision durable state revision at emit time
115
- *
116
- * Everything after the envelope is a FLAT snake_case payload. Records are not shaped
117
- * around any metric we compute today: a reader that wants a number we never thought of
118
- * should be able to get it with a query rather than a code change. Nothing nests, and
119
- * anything one-to-many (a correction, an absorbed wedge, a created fold) is its own
120
- * record referencing the record it belongs to by `seq`.
121
- */
122
70
  export const CONTEXT_EVENT_SCHEMA_VERSION = 1;
123
71
 
124
72
  export interface ContextEvent {
@@ -133,63 +81,29 @@ export interface ContextEvent {
133
81
  }
134
82
 
135
83
  export type ContextEventKind =
136
- /** One context-management tool call, accepted or refused. */
137
84
  | "context.attempt"
138
- /** One auto-snap applied to a requested span, referencing its attempt. */
139
85
  | "context.correction"
140
- /** One protect or unprotect: durable pin bookkeeping; no window bytes move. */
141
86
  | "context.protect"
142
- /** The resolved serving budget, stated once at startup so a live run can prove it. */
143
87
  | "context.capacity"
144
- /** One receipt delivered into the window. */
145
88
  | "context.receipt"
146
- /** One post-commit rider composed and persisted: at most one per fold epoch. */
147
89
  | "context.rider"
148
- /** One pre-commit last-call exposure: the band-top prompt, with its telemetry. */
149
90
  | "context.lastcall"
150
- /** One gated round's outcome, attributed to the exposure it answers. */
151
91
  | "context.response"
152
- /** One surfacing suggestion issued: both channel scores, the divergence, the carrier. */
153
92
  | "context.suggestion"
154
- /** One surfacing suggestion graded: the acted/used/ignored join and its transition. */
155
93
  | "context.outcome"
156
- /** One append-once occupancy threshold notice delivered into the window. */
157
94
  | "context.notice"
158
- /** One commit epoch: the single mutation an epoch pays for. */
159
95
  | "context.commit"
160
- /** One fold created, by the ladder or by the agent. */
161
96
  | "context.fold"
162
- /**
163
- * One generator call for a brief: what it read, what it wrote, and what it cost.
164
- *
165
- * The generator runs outside the session, so its spend appears in no provider ledger
166
- * and its latency appears in no turn. Without this record a deployment cannot say what
167
- * briefing costs it, which is the question the async lane exists to answer.
168
- */
169
97
  | "context.brief"
170
- /** One boundary sliver absorbed into its later neighbour. */
171
98
  | "context.absorb"
172
- /** One oversized span split into sequential bite-sized folds. */
173
99
  | "context.split"
174
- /** One overflow rollback and the recovery that followed it. */
175
100
  | "context.recovery"
176
- /** One tree rollback: what left the branch, and whether the request replayed. */
101
+ | "context.suspend"
177
102
  | "context.rollback"
178
- /** One projection built, classified against the one before it. */
179
103
  | "context.projection"
180
- /** One provider response, which is where this stream joins provider usage. */
181
104
  | "context.usage"
182
- /** One handoff, compared byte-for-byte against the previous transmitted projection. */
183
105
  | "context.prefix";
184
106
 
185
- /**
186
- * Where the prefix first differs from the previous TRANSMITTED projection, in chars.
187
- *
188
- * A positional cache is keyed on a byte prefix, so the message-level classification is
189
- * not enough to attribute a miss: the analyst needs the position, and only the runtime
190
- * knows why a byte at that position moved. This is the cheap half -- both projections
191
- * are in hand at handoff -- and it is emission only. No analysis of it belongs here.
192
- */
193
107
  export function prefixDivergence(
194
108
  previous: string | null,
195
109
  next: string,
@@ -208,18 +122,14 @@ export function prefixDivergence(
208
122
 
209
123
  export interface InstrumentationLedger {
210
124
  projections: number;
211
- /** Projections this runtime actually rewrote. The scheduling dial. */
212
125
  rewrites: number;
213
126
  appends: number;
214
127
  identical: number;
215
- /** Responses that re-read an unchanged prefix from scratch. The provider's dial. */
216
128
  providerSideMisses: number;
217
129
  observedMisses: number;
218
130
  records: ProjectionRecord[];
219
131
  observations: CacheObservation[];
220
- /** The stream itself, oldest first. Bounded in memory; durable in full. */
221
132
  events: ContextEvent[];
222
- /** The monotonic sequence every record carries. */
223
133
  sequence: number;
224
134
  countsByKind: Record<string, number>;
225
135
  }
@@ -240,10 +150,6 @@ export function emptyLedger(): InstrumentationLedger {
240
150
  };
241
151
  }
242
152
 
243
- /**
244
- * Stamp the envelope and append. The in-memory ring is the status view and is bounded;
245
- * the durable session entry is the audit copy and is not.
246
- */
247
153
  export function recordContextEvent(
248
154
  ledger: InstrumentationLedger,
249
155
  kind: ContextEventKind,
@@ -263,19 +169,9 @@ export function recordContextEvent(
263
169
  };
264
170
  ledger.countsByKind[kind] = (ledger.countsByKind[kind] ?? 0) + 1;
265
171
  ledger.events.push(record);
266
- if (ledger.events.length > MAX_CONTEXT_ATTEMPT_RECORDS) {
267
- ledger.events = ledger.events.slice(ledger.events.length - MAX_CONTEXT_ATTEMPT_RECORDS);
268
- }
269
172
  return record;
270
173
  }
271
174
 
272
- function bounded<T>(values: T[], next: T): T[] {
273
- values.push(next);
274
- return values.length > MAX_PROJECTION_HASH_RECORDS
275
- ? values.slice(values.length - MAX_PROJECTION_HASH_RECORDS)
276
- : values;
277
- }
278
-
279
175
  export function recordProjection(
280
176
  ledger: InstrumentationLedger,
281
177
  comparison: ProjectionComparison,
@@ -285,29 +181,13 @@ export function recordProjection(
285
181
  if (comparison.change === "rewrite") ledger.rewrites += 1;
286
182
  else if (comparison.change === "append") ledger.appends += 1;
287
183
  else ledger.identical += 1;
288
- ledger.records = bounded(ledger.records, {
184
+ ledger.records.push({
289
185
  ...comparison,
290
186
  ordinal: ledger.projections,
291
- // The prefix digest, not the whole projection: it is what a positional cache keys
292
- // on, so two equal values are the claim that a cache hit was available.
293
187
  prefixSha256: sha256Value(digests.slice(0, comparison.previousCount || digests.length)),
294
188
  });
295
189
  }
296
190
 
297
- /**
298
- * Read one provider response against the projection that produced it. A miss on a
299
- * projection that only grew is provider-side by construction: no byte we control
300
- * moved, so no scheduling change could have prevented the re-read.
301
- *
302
- * A rewrite used to be charged to us unconditionally, and that hid the whole rep 21
303
- * finding: a rewrite that preserved 99.86% of the prompt still read back as ours, so
304
- * our own metrics reported a negligible divergence and a total cache loss and never
305
- * put the two side by side. Attribution now asks how much of the previous prompt the
306
- * rewrite actually kept. If we preserved nearly all of it and the provider still
307
- * served nothing, our divergence cannot account for the loss and the miss is theirs.
308
- * That is a claim about ATTRIBUTION, not about cost: a preserved-share miss is still
309
- * a full rebuild, and it is still worth not causing.
310
- */
311
191
  export const PROVIDER_SIDE_MISS_PRESERVED_SHARE = 0.99;
312
192
 
313
193
  export function observeCacheUsage(
@@ -334,11 +214,10 @@ export function observeCacheUsage(
334
214
  };
335
215
  if (missed) ledger.observedMisses += 1;
336
216
  if (observation.providerSideMiss) ledger.providerSideMisses += 1;
337
- ledger.observations = bounded(ledger.observations, observation);
217
+ ledger.observations.push(observation);
338
218
  return observation;
339
219
  }
340
220
 
341
- /** The compact ledger the evidence envelope carries; the record rings stay for audit. */
342
221
  export function ledgerSummary(ledger: InstrumentationLedger): Record<string, unknown> {
343
222
  return {
344
223
  projections: ledger.projections,
@@ -89,32 +89,12 @@ export function contextUsageRatio(value: unknown): number | null {
89
89
  return tokens / contextWindow;
90
90
  }
91
91
 
92
- /**
93
- * A measured WINDOW ratio, restated as occupancy of the serving budget.
94
- *
95
- * The provider reports tokens against its total window and the thermostat is declared
96
- * against the serving budget, so exactly one conversion stands between them and it is
97
- * written here rather than inferred at each reader. Before this build the trigger
98
- * divided by the budget while the freeing target and the backstop divided by the
99
- * window, which at a 383,616-token budget behind a 400,000-token window is a 4.5-point
100
- * disagreement, and 10 points at a 100,000-token fixture.
101
- */
102
92
  export function budgetOccupancy(snapshot: ActiveContextSnapshot, windowRatio: number | null): number | null {
103
93
  if (typeof windowRatio !== "number" || !Number.isFinite(windowRatio)) return null;
104
94
  if (!(snapshot.budgetTokens > 0) || !(snapshot.contextWindow > 0)) return null;
105
95
  return windowRatio * snapshot.contextWindow / snapshot.budgetTokens;
106
96
  }
107
97
 
108
- /**
109
- * The fence, as a ratio of the window the measured ratio is denominated against.
110
- *
111
- * A measured ratio is tokens over `contextWindow`, so "the request reached its serving
112
- * budget" is exactly `ratio >= budgetTokens / contextWindow`. When the snapshot carries
113
- * a budget the runtime uses it verbatim: a deployment that DECLARED its input budget has
114
- * already netted out its output reservation, and re-subtracting one here would fence it
115
- * roughly four points early. Only the ctx fallback, where nothing but a raw provider
116
- * window is known, still estimates the reservation.
117
- */
118
98
  export function hardFenceRatio(value?: unknown, ctx?: any): number {
119
99
  const direct = ownValue(value, "contextWindow");
120
100
  const reported = ownValue(ctx?.getContextUsage?.(), "contextWindow");
@@ -134,45 +114,16 @@ export function hardFenceRatio(value?: unknown, ctx?: any): number {
134
114
  return (contextWindow - reserve) / contextWindow;
135
115
  }
136
116
 
137
- /**
138
- * What the runtime is actually allowed to send, in tokens.
139
- *
140
- * The per-request max-input DESCRIPTOR a provider advertises assumes a full output
141
- * reservation, so it understates the real ceiling by whatever the deployment did not
142
- * reserve. Measured 2026-08-06 on openai-codex gpt-5.x: a run aborted at ~297k
143
- * projected tokens against a 272k descriptor, in the same session where the provider
144
- * had just accepted 339,689 tokens. Serving admits roughly the TOTAL window minus the
145
- * output reservation actually in force, which at a 16,384 reservation is ~384k.
146
- *
147
- * Every number here is introspectable in status on purpose: a fence nobody can audit
148
- * is how a 25 percent error survived four instrumented runs.
149
- */
150
117
  export interface CapacityAccounting {
151
- /** "truthful" once the deployment declares its own serving budget. */
152
118
  mode: "descriptor" | "truthful";
153
- /** The denominator every pressure ratio and fence is computed against. */
154
119
  window: number;
155
120
  outputReservation: number;
156
- /** The token ceiling: window minus the output reservation. */
157
121
  budgetTokens: number;
158
122
  usedTokens: number | null;
159
123
  headroomTokens: number | null;
160
- /** What the provider descriptor claimed, kept so the gap stays visible. */
161
124
  descriptorWindow: number | null;
162
125
  }
163
126
 
164
- /**
165
- * ONE denominator, and no guess in front of it.
166
- *
167
- * `truthful` means the deployment stated the budget it may actually fill, already net of
168
- * whatever output reservation it holds back, so the value passes through untouched and
169
- * the reservation this runtime reports is zero because it withheld nothing. Only the
170
- * descriptor fallback still subtracts, because a per-request max-input descriptor is a
171
- * total-window claim and the reservation inside it has to be estimated. That estimate was
172
- * the defect the reshape removed: the runtime subtracted a fixed 16,384 tokens capped at
173
- * a tenth of the window while the documentation claimed it subtracted the reservation
174
- * actually in force.
175
- */
176
127
  export function capacityAccounting(input: {
177
128
  window: number;
178
129
  truthful: boolean;
@@ -352,8 +303,6 @@ export function persistenceProjection(state: ActiveContextState, snapshot: Activ
352
303
  leases: Object.fromEntries(Object.entries(state.leases)
353
304
  .filter(([id]) => retained.has(id))),
354
305
  };
355
- // A pending mark is a promise about evidence that is still in the branch. Once its
356
- // source or its named fold leaves, the mark is unfulfillable and must not survive.
357
306
  const survivingMarks = (state.pendingMarks ?? []).filter((mark) => mark.mark === "refold"
358
307
  ? retained.has(mark.id)
359
308
  : mark.parts.every((part) => part.kind === "raw"
@@ -362,8 +311,6 @@ export function persistenceProjection(state: ActiveContextState, snapshot: Activ
362
311
  if (survivingMarks.length) projected.pendingMarks = clone(survivingMarks);
363
312
  else delete projected.pendingMarks;
364
313
  delete projected.pinnedPeeks;
365
- // A brief correction names a fold; once that fold is gone the correction has
366
- // nothing to present, exactly like a mark whose source left the branch.
367
314
  const survivingBriefs = Object.entries(state.briefs ?? {}).filter(([id]) => retained.has(id));
368
315
  if (survivingBriefs.length) projected.briefs = Object.fromEntries(clone(survivingBriefs));
369
316
  else delete projected.briefs;
@@ -402,11 +349,6 @@ export function toolRefsProtected(
402
349
  });
403
350
  }
404
351
 
405
- /**
406
- * Mass the agent's pins hold out of the ladder's reach: explicitly protected evidence
407
- * outside the fresh tail. A pin's cost must be a number the agent sees on receipts,
408
- * never a silent commit shortfall.
409
- */
410
352
  export function protectedStaleMass(
411
353
  snapshot: ActiveContextSnapshot,
412
354
  state: ActiveContextState,
@@ -424,12 +366,6 @@ export function protectedStaleMass(
424
366
  return { bytes: total, refs };
425
367
  }
426
368
 
427
- /**
428
- * Every byte protect currently holds raw, fresh or stale: the mass the pinned-share
429
- * cap is measured against. protectedStaleMass narrows to what a commit could not
430
- * reclaim TODAY; the cap has to see the whole promise, because a pin inside the
431
- * fresh tail outlives the tail.
432
- */
433
369
  export function explicitProtectedMass(
434
370
  snapshot: ActiveContextSnapshot,
435
371
  state: ActiveContextState,
@@ -452,16 +388,9 @@ export interface AdmissionVerdict {
452
388
  requestedTokens: number;
453
389
  headroomTokens: number | null;
454
390
  budgetTokens: number;
455
- /** Bytes of the target that WOULD fit; 0 when even the minimum slice will not. */
456
391
  affordableBytes: number;
457
392
  }
458
393
 
459
- /**
460
- * Preflight one read against the exact stored size of its target and the truthful
461
- * headroom. A read that has not been measured yet is ADMITTED and says so: refusing
462
- * on absent data would stall a session for a number we never had, and the absence is
463
- * reported rather than papered over.
464
- */
465
394
  export function admissionVerdict(input: {
466
395
  requestedBytes: number;
467
396
  capacity: CapacityAccounting;