pi-fold 1.0.4 → 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.
- package/README.md +34 -8
- package/extensions/active-context.ts +245 -2355
- package/extensions/evidence.js +0 -10
- package/extensions/index.js +10 -30
- package/extensions/json.ts +3 -29
- package/extensions/lib/canonical.ts +0 -8
- package/extensions/lib/curation.ts +1 -258
- package/extensions/lib/folding.ts +33 -406
- package/extensions/lib/instrumentation.ts +3 -124
- package/extensions/lib/measurement.ts +0 -71
- package/extensions/lib/persistence.ts +216 -197
- package/extensions/lib/policy.ts +29 -558
- package/extensions/lib/rollback.ts +0 -53
- package/extensions/lib/scheduling.ts +36 -367
- package/extensions/lib/selection.ts +206 -312
- package/extensions/lib/tool-surface.ts +5 -18
- package/extensions/lib/transcript.ts +32 -108
- package/package.json +1 -1
- package/extensions/lib/surfacing.ts +0 -604
- package/extensions/summarizer.js +0 -291
|
@@ -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
|
-
|
|
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
|
|
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
|
|
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;
|