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
|
@@ -2,11 +2,9 @@ import {
|
|
|
2
2
|
denseOwnArrayValues,
|
|
3
3
|
evidenceSha256,
|
|
4
4
|
objectRefKey,
|
|
5
|
-
sha256Text,
|
|
6
5
|
sha256Value,
|
|
7
6
|
stableStringify,
|
|
8
7
|
} from "./json.ts";
|
|
9
|
-
import type { EvidenceRef } from "./json.ts";
|
|
10
8
|
import {
|
|
11
9
|
bytes,
|
|
12
10
|
clone,
|
|
@@ -18,18 +16,15 @@ import {
|
|
|
18
16
|
import {
|
|
19
17
|
activeContextStatus,
|
|
20
18
|
automaticPreparationId,
|
|
21
|
-
boundedOrientation,
|
|
22
19
|
boundStatusPayload,
|
|
20
|
+
briefContractComplaint,
|
|
23
21
|
commitPreparedFold,
|
|
24
|
-
consolidationSourceText,
|
|
25
22
|
descendantIds,
|
|
26
23
|
encodedFoldSource,
|
|
27
|
-
generatedBriefs,
|
|
28
24
|
foldCandidatesDetail,
|
|
29
25
|
foldTreeDetail,
|
|
30
26
|
peekFoldSource,
|
|
31
27
|
prepareFold,
|
|
32
|
-
preparedFoldError,
|
|
33
28
|
projectActiveContext,
|
|
34
29
|
projectionSlateCandidates,
|
|
35
30
|
protectEvidence,
|
|
@@ -52,6 +47,7 @@ import {
|
|
|
52
47
|
latestProviderContextMeasurement,
|
|
53
48
|
orderedRoots,
|
|
54
49
|
parseNativeCompactionCompletion,
|
|
50
|
+
exactMapped,
|
|
55
51
|
parseNativeCompactionDecision,
|
|
56
52
|
parseProviderContextMeasurementReceipt,
|
|
57
53
|
persistenceProjection,
|
|
@@ -103,31 +99,19 @@ import {
|
|
|
103
99
|
COMMIT_RECLAIM_FLOOR_SHARE,
|
|
104
100
|
CONTEXT_RECEIPT_BLOCK_BYTES,
|
|
105
101
|
DEFAULT_CONTEXT_WINDOW,
|
|
106
|
-
EPOCH_CONSOLIDATION_ROUNDS,
|
|
107
|
-
MAX_THRESHOLD_NOTICES,
|
|
108
|
-
THRESHOLD_NOTICE_SHARES,
|
|
109
102
|
resolveGuidance,
|
|
110
103
|
assertThresholdsServable,
|
|
111
104
|
resolveThresholds,
|
|
112
105
|
servingBudgetTokens,
|
|
113
106
|
ESTIMATED_BYTES_PER_TOKEN,
|
|
114
107
|
entryTypeNamespace,
|
|
115
|
-
MAX_BRIEF_BATCH_SPANS,
|
|
116
|
-
MAX_BRIEF_UPGRADE_QUEUE,
|
|
117
|
-
MAX_BRIEF_UPGRADES_IN_FLIGHT,
|
|
118
108
|
MAX_FOLD_SPAN_CHARS,
|
|
119
109
|
MAX_PINNED_SHARE,
|
|
120
110
|
MAX_WEDGE_ABSORB_TOKENS,
|
|
121
|
-
OVERFLOW_RECOVERY_MAX_ATTEMPTS,
|
|
122
111
|
PEEK_DEFAULT_MAX_BYTES,
|
|
123
112
|
PEEK_MIN_SLICE_BYTES,
|
|
124
113
|
PEEK_READ_ONLY_CONTEXT_ACTIONS,
|
|
125
114
|
AUTO_FOLD_BLACKLIST_DEFAULT,
|
|
126
|
-
SURFACING_BRIEF_HIT,
|
|
127
|
-
SURFACING_CONTENT_HIT,
|
|
128
|
-
SURFACING_DIVERGENCE_MARGIN,
|
|
129
|
-
SURFACING_OUTCOME_WINDOW_ORDINALS,
|
|
130
|
-
SURFACING_SLATE_SIZE,
|
|
131
115
|
USER_RESCUE_MAX_SOURCE_CHARS,
|
|
132
116
|
} from "./lib/policy.ts";
|
|
133
117
|
import type {
|
|
@@ -136,7 +120,6 @@ import type {
|
|
|
136
120
|
ActiveContextState,
|
|
137
121
|
ActiveContextThresholds,
|
|
138
122
|
ActiveContextToolAction,
|
|
139
|
-
BriefProvenance,
|
|
140
123
|
FoldCandidate,
|
|
141
124
|
FoldKind,
|
|
142
125
|
FoldRecordEntry,
|
|
@@ -148,16 +131,12 @@ import {
|
|
|
148
131
|
import {
|
|
149
132
|
contextReceipt,
|
|
150
133
|
contextRiderText,
|
|
151
|
-
curationSignals,
|
|
152
|
-
lastCallText,
|
|
153
134
|
markAwarenessText,
|
|
154
135
|
receiptBlockText,
|
|
155
|
-
thresholdNoticeText,
|
|
156
136
|
withReceipt,
|
|
157
137
|
} from "./lib/curation.ts";
|
|
158
138
|
import type {
|
|
159
139
|
ContextReceipt,
|
|
160
|
-
CurationSignals,
|
|
161
140
|
} from "./lib/curation.ts";
|
|
162
141
|
import {
|
|
163
142
|
addPendingMark,
|
|
@@ -165,7 +144,6 @@ import {
|
|
|
165
144
|
commitPendingMarks,
|
|
166
145
|
consolidationMarks,
|
|
167
146
|
ephemeralPeekMarks,
|
|
168
|
-
epochCommitDue,
|
|
169
147
|
estimatedTokens,
|
|
170
148
|
foldMarkFor,
|
|
171
149
|
ladderSelectionMark,
|
|
@@ -184,10 +162,7 @@ import {
|
|
|
184
162
|
withPendingMarks,
|
|
185
163
|
} from "./lib/scheduling.ts";
|
|
186
164
|
import {
|
|
187
|
-
automaticToolBrief,
|
|
188
165
|
candidateSpanChars,
|
|
189
|
-
deterministicChapterCandidateBrief,
|
|
190
|
-
deterministicConsolidationBrief,
|
|
191
166
|
manualFoldCandidate,
|
|
192
167
|
peekedSourceFoldIds,
|
|
193
168
|
selectAutomaticToolBatch,
|
|
@@ -196,15 +171,6 @@ import {
|
|
|
196
171
|
splitCandidateBySize,
|
|
197
172
|
} from "./lib/selection.ts";
|
|
198
173
|
import type { SpanCorrection } from "./lib/selection.ts";
|
|
199
|
-
import {
|
|
200
|
-
issueSurfacing,
|
|
201
|
-
noteSurfacingAction,
|
|
202
|
-
resolveSurfacing,
|
|
203
|
-
selectSurfacingSlate,
|
|
204
|
-
surfacingLedger,
|
|
205
|
-
surfacingSilenced,
|
|
206
|
-
surfacingSlateText,
|
|
207
|
-
} from "./lib/surfacing.ts";
|
|
208
174
|
import {
|
|
209
175
|
compareProjections,
|
|
210
176
|
emptyLedger,
|
|
@@ -234,17 +200,8 @@ import {
|
|
|
234
200
|
unansweredToolCalls,
|
|
235
201
|
} from "./lib/rollback.ts";
|
|
236
202
|
|
|
237
|
-
/**
|
|
238
|
-
* How a projection's token reading was reached.
|
|
239
|
-
*
|
|
240
|
-
* "anchored": the provider's own count for the projection this one extends, plus a rate
|
|
241
|
-
* estimate of the bytes appended after it. "rewritten": the current projection does not
|
|
242
|
-
* begin with the measured one, so there is nothing to add a delta to and the whole
|
|
243
|
-
* projection is estimated. "unmeasured": no accepted provider count yet.
|
|
244
|
-
*/
|
|
245
203
|
export type ProjectionReadingBasis = "anchored" | "rewritten" | "unmeasured";
|
|
246
204
|
|
|
247
|
-
// Test seam only; the package API is registerPiFold because package.json exports block deep imports.
|
|
248
205
|
export * from "./lib/canonical.ts";
|
|
249
206
|
export * from "./lib/curation.ts";
|
|
250
207
|
export * from "./lib/folding.ts";
|
|
@@ -255,13 +212,8 @@ export * from "./lib/policy.ts";
|
|
|
255
212
|
export * from "./lib/rollback.ts";
|
|
256
213
|
export * from "./lib/scheduling.ts";
|
|
257
214
|
export * from "./lib/selection.ts";
|
|
258
|
-
export * from "./lib/surfacing.ts";
|
|
259
215
|
export * from "./lib/transcript.ts";
|
|
260
216
|
|
|
261
|
-
/**
|
|
262
|
-
* The batched fold request: several {span, brief} pairs in one call, with the single
|
|
263
|
-
* `ids` form still accepted because it is the shape a status action hands back.
|
|
264
|
-
*/
|
|
265
217
|
export interface BatchedMarkRequest {
|
|
266
218
|
ids: string[];
|
|
267
219
|
brief?: string;
|
|
@@ -295,37 +247,14 @@ export function batchedMarkRequests(params: Record<string, unknown>): BatchedMar
|
|
|
295
247
|
}
|
|
296
248
|
|
|
297
249
|
export function registerActiveContext(pi: any, options: {
|
|
298
|
-
/**
|
|
299
|
-
* INTERNAL brief-generator seam, not a public option: `registerPiFold` refuses the
|
|
300
|
-
* name and builds the generator from `summarizer`. It stays a parameter because it
|
|
301
|
-
* is the runtime's only brief-generator interface.
|
|
302
|
-
*/
|
|
303
|
-
summarizeContextSpan?: (request: Record<string, unknown>, ctx: unknown) => Promise<Record<string, unknown>>;
|
|
304
|
-
/**
|
|
305
|
-
* INTERNAL deployment-identity seam, not public options: the package entry refuses all
|
|
306
|
-
* five by name and the shipped identity is the one in `policy.ts`. They survive here
|
|
307
|
-
* because the neutrality gate has to register a synthetic brand to prove none of it
|
|
308
|
-
* reaches the defaults, and the experiment harness registers the pi-fold identity
|
|
309
|
-
* explicitly so its sealed runs keep their entry types.
|
|
310
|
-
*/
|
|
311
250
|
toolName?: string;
|
|
312
251
|
toolLabel?: string;
|
|
313
252
|
brandNoun?: string;
|
|
314
253
|
entryTypePrefix?: string;
|
|
315
254
|
commandNames?: { status?: string; fold?: string };
|
|
316
|
-
/**
|
|
317
|
-
* The auto-fold EXCEPTION list, empty by default: every completed tool batch folds
|
|
318
|
-
* unmarked, and this names the tools whose results must stay raw.
|
|
319
|
-
*/
|
|
320
255
|
blacklistAutoFoldTools?: ReadonlySet<string>;
|
|
321
|
-
/** The serving budget itself, ALREADY NET of the deployment's output reservation. */
|
|
322
256
|
providerInputBudget?: number;
|
|
323
|
-
/**
|
|
324
|
-
* The thermostat, set whole or not at all. USER policy: no agent action reads it back
|
|
325
|
-
* as a mutable surface, and status reports the values without offering to change them.
|
|
326
|
-
*/
|
|
327
257
|
thresholds?: ActiveContextThresholds;
|
|
328
|
-
/** Both guidance surfaces, set together, defaulting on. */
|
|
329
258
|
guidance?: Partial<ActiveContextGuidance>;
|
|
330
259
|
}): {
|
|
331
260
|
projectionCandidates: (ctx: any) => Array<Record<string, unknown>>;
|
|
@@ -335,11 +264,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
335
264
|
const brandNoun = options.brandNoun ?? DEFAULT_ACTIVE_CONTEXT_BRAND_NOUN;
|
|
336
265
|
const entryTypePrefix = options.entryTypePrefix ?? DEFAULT_ACTIVE_CONTEXT_ENTRY_TYPE_PREFIX;
|
|
337
266
|
const blacklistAutoFoldTools = options.blacklistAutoFoldTools ?? AUTO_FOLD_BLACKLIST_DEFAULT;
|
|
338
|
-
// Deleted options are REFUSED by name, never ignored. A deployment still passing one
|
|
339
|
-
// believes it asked for something, and silence would hand it the opposite behavior:
|
|
340
|
-
// `foldScheduling` chose between epoch and the deleted immediate scheduler,
|
|
341
|
-
// `foldPeekResults` opted peek results out of the foldable classification, and
|
|
342
|
-
// `toolActions` narrowed the action surface. All three are unconditional now.
|
|
343
267
|
for (const removed of ["foldScheduling", "foldPeekResults", "toolActions"]) {
|
|
344
268
|
if (Object.hasOwn(options, removed)) {
|
|
345
269
|
throw new Error(`${removed} is no longer an option: epoch scheduling, peek foldability ` +
|
|
@@ -350,24 +274,12 @@ export function registerActiveContext(pi: any, options: {
|
|
|
350
274
|
throw new Error("blockingTools is no longer an option: a tool call never causes a rewrite of " +
|
|
351
275
|
"its own, and the commit epoch is the only path that mutates the projection");
|
|
352
276
|
}
|
|
353
|
-
// The per-request surfacing channel. `setProjectionProvider` was a second delivery
|
|
354
|
-
// path for the value this call already returns, and `setSuggestionSourceRegistrar`
|
|
355
|
-
// handed out registration into a carrier that no longer exists: the slate rode a
|
|
356
|
-
// per-request ephemeral tail, which moved the prefix on every single pass. The
|
|
357
|
-
// selector survives and returns at the commit boundary; the channel does not.
|
|
358
277
|
for (const removed of ["setProjectionProvider", "setSuggestionSourceRegistrar"]) {
|
|
359
278
|
if (Object.hasOwn(options, removed)) {
|
|
360
279
|
throw new Error(`${removed} is no longer an option: projection candidates are returned by ` +
|
|
361
280
|
"registration, and external suggestion sources have no carrier to render into");
|
|
362
281
|
}
|
|
363
282
|
}
|
|
364
|
-
// The renamed options are refused by their OLD names, each pointing at the new one.
|
|
365
|
-
// `readOnlyTools` and then `autoFoldableTools` were both ALLOW-lists, and the list runs
|
|
366
|
-
// the other way now, so neither may be forwarded silently: an allow-list read as a
|
|
367
|
-
// blacklist bars exactly the tools it meant to permit. `commandPrefix` derived two
|
|
368
|
-
// command names from a stem the full-name override then overrode anyway;
|
|
369
|
-
// `providerTotalWindow` was a gross window the runtime netted down with a GUESSED
|
|
370
|
-
// reservation, and the guess is what the reshape deleted.
|
|
371
283
|
for (const inverted of ["readOnlyTools", "autoFoldableTools"]) {
|
|
372
284
|
if (Object.hasOwn(options, inverted)) {
|
|
373
285
|
throw new Error(`${inverted} is now blacklistAutoFoldTools, and the sense is INVERTED: ` +
|
|
@@ -388,9 +300,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
388
300
|
(!Number.isSafeInteger(options.providerInputBudget) || options.providerInputBudget <= 0)) {
|
|
389
301
|
throw new Error("providerInputBudget must be a positive integer");
|
|
390
302
|
}
|
|
391
|
-
// The deployment's own fact, and the only capacity knob: declaring it makes every
|
|
392
|
-
// ratio, fence and budget truthful, and leaving it out falls back to the provider
|
|
393
|
-
// descriptor and SAYS "descriptor" in the capacity accounting.
|
|
394
303
|
const providerInputBudget = options.providerInputBudget ?? null;
|
|
395
304
|
const readOnlyContextActions = PEEK_READ_ONLY_CONTEXT_ACTIONS;
|
|
396
305
|
if (!toolName || !toolLabel || !brandNoun || !entryTypePrefix ||
|
|
@@ -406,8 +315,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
406
315
|
commandNames.status === commandNames.fold) {
|
|
407
316
|
throw new Error("Active-context command names must be distinct kebab-case strings");
|
|
408
317
|
}
|
|
409
|
-
// The thermostat, validated atomically before anything else reads a threshold, and
|
|
410
|
-
// then checked against the budget this deployment can actually serve.
|
|
411
318
|
const thresholds = resolveThresholds(options.thresholds);
|
|
412
319
|
assertThresholdsServable(thresholds, providerInputBudget ?? servingBudgetTokens(DEFAULT_CONTEXT_WINDOW));
|
|
413
320
|
const guidance = resolveGuidance(options.guidance);
|
|
@@ -415,11 +322,8 @@ export function registerActiveContext(pi: any, options: {
|
|
|
415
322
|
const foldRecordEntryType = `${entryTypePrefix}-fold-record`;
|
|
416
323
|
const milestoneProjectionType = `${entryTypePrefix}-milestone`;
|
|
417
324
|
const advisoryProjectionType = `${entryTypePrefix}-advisory`;
|
|
418
|
-
const surfacingProjectionType = `${entryTypePrefix}-surfacing`;
|
|
419
325
|
const receiptProjectionType = `${entryTypePrefix}-receipts`;
|
|
420
326
|
const riderProjectionType = `${entryTypePrefix}-rider`;
|
|
421
|
-
const lastCallProjectionType = `${entryTypePrefix}-lastcall`;
|
|
422
|
-
const noticeProjectionType = `${entryTypePrefix}-notice`;
|
|
423
327
|
const curationProjectionType = `${entryTypePrefix}-curation`;
|
|
424
328
|
const entryNamespace = entryTypeNamespace(entryTypePrefix);
|
|
425
329
|
const providerMeasurementEntryType = `${entryNamespace}-provider-context-measurement`;
|
|
@@ -439,8 +343,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
439
343
|
persistenceDisposition: "none" | "record-only" | "state-committed";
|
|
440
344
|
};
|
|
441
345
|
|
|
442
|
-
// Owns in-memory/durable state and wire/record bookkeeping; persistenceQueue serializes it.
|
|
443
|
-
// Once the state event succeeds, replay owns this exact state; RAM may not roll behind it.
|
|
444
346
|
const persistence = {
|
|
445
347
|
state: null as ActiveContextState | null,
|
|
446
348
|
persisted: null as ActiveContextState | null,
|
|
@@ -450,7 +352,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
450
352
|
persistenceQueue: Promise.resolve<void>(undefined),
|
|
451
353
|
};
|
|
452
354
|
|
|
453
|
-
// Owns preparation/ladder rollback, selection, and suspension; actionQueue serializes it.
|
|
454
355
|
const ladder = {
|
|
455
356
|
pendingManual: false,
|
|
456
357
|
preparing: null as { id: string; controller: AbortController; promise: Promise<void> } | null,
|
|
@@ -461,102 +362,21 @@ export function registerActiveContext(pi: any, options: {
|
|
|
461
362
|
lastSelectionSourceIds: [] as string[],
|
|
462
363
|
pendingContextNote: null as string | null,
|
|
463
364
|
lastAutomaticAction: null as Record<string, unknown> | null,
|
|
464
|
-
/** What the last over-budget projection cost and whether the reduction saved it. */
|
|
465
365
|
overBudgetReduction: null as Record<string, unknown> | null,
|
|
466
366
|
automaticFailure: null as AutomaticFailureState | null,
|
|
467
|
-
failedPreparations: new Set<string>(),
|
|
468
367
|
actionQueue: Promise.resolve<unknown>(undefined),
|
|
469
368
|
};
|
|
470
369
|
|
|
471
|
-
/**
|
|
472
|
-
* THE BRIEF UPGRADE LANE.
|
|
473
|
-
*
|
|
474
|
-
* A fold brief is model-written from this campaign forward; the deterministic brief is
|
|
475
|
-
* what a failure leaves behind. The automatic ladder cannot wait for a generator: the
|
|
476
|
-
* commit path is synchronous with a measured projection and a fold deferred until a
|
|
477
|
-
* provider answers is a fold that does not happen. So the ladder commits deterministic,
|
|
478
|
-
* a generator runs between boundaries, and the brief it writes lands as an UPGRADE
|
|
479
|
-
* riding the next commit: the same law wedge absorption and the surfacing slate follow,
|
|
480
|
-
* mutations only inside a rewrite the session already paid for.
|
|
481
|
-
*
|
|
482
|
-
* Everything here is process-local. An upgrade lost to a reload is a brief that stays
|
|
483
|
-
* deterministic, which is exactly the failure the fallback exists for, and the state
|
|
484
|
-
* wire keeps one shape instead of carrying work in progress.
|
|
485
|
-
*/
|
|
486
|
-
const upgrades = {
|
|
487
|
-
/**
|
|
488
|
-
* BATCHES of folds committed deterministic, each holding the exact source its briefs
|
|
489
|
-
* will cover. One batch is one generator call: a commit folds its spans together, so
|
|
490
|
-
* they are briefed together, and the spans of one batch orient each other. Sending them
|
|
491
|
-
* one at a time is what left the 2026-08-11 rep with 14 model briefs across 87 folds.
|
|
492
|
-
*/
|
|
493
|
-
queue: [] as Array<{
|
|
494
|
-
members: Array<{ foldId: string; sourceSha256: string }>;
|
|
495
|
-
request: Record<string, unknown>;
|
|
496
|
-
}>,
|
|
497
|
-
/**
|
|
498
|
-
* Generator calls in flight, keyed by every fold each one briefs, bounded by a constant
|
|
499
|
-
* and cancel-safe on session change or shutdown. Keyed rather than counted because
|
|
500
|
-
* the queue has to know a fold is already being briefed.
|
|
501
|
-
*/
|
|
502
|
-
running: new Map<string, { controller: AbortController; promise: Promise<void> }>(),
|
|
503
|
-
/** Written briefs waiting for a boundary. They never apply anywhere else. */
|
|
504
|
-
ready: [] as Array<{ foldId: string; sourceSha256: string; brief: string; provenance: BriefProvenance }>,
|
|
505
|
-
/** A generator that failed a fold does not get it back: one attempt, then deterministic stands. */
|
|
506
|
-
failed: new Set<string>(),
|
|
507
|
-
/**
|
|
508
|
-
* Parents whose turn has not come: a group is read at depth one, so a child still
|
|
509
|
-
* waiting on its own brief would be read through the sentence that brief is about to
|
|
510
|
-
* replace. They are retried at every boundary and never dropped, because the wait ends
|
|
511
|
-
* the moment the lane drains and a parent that was skipped once is a parent no
|
|
512
|
-
* generator ever writes.
|
|
513
|
-
*/
|
|
514
|
-
deferred: new Set<string>(),
|
|
515
|
-
/** Failures since the last commit record carried them, so none of this is silent. */
|
|
516
|
-
failures: 0,
|
|
517
|
-
/**
|
|
518
|
-
* Briefs that took a second ask before they met the contract. Counted because the cure
|
|
519
|
-
* is the mechanism that lets the brief cap be a real limit rather than a truncation
|
|
520
|
-
* point, so how often it fires is how we learn whether the cap is set anywhere near
|
|
521
|
-
* right (Shane 2026-08-11).
|
|
522
|
-
*/
|
|
523
|
-
cures: 0,
|
|
524
|
-
lastError: null as string | null,
|
|
525
|
-
};
|
|
526
|
-
|
|
527
|
-
// Owns provider receipts, anchors, and usage; providerMeasurementQueue serializes receipt writes.
|
|
528
370
|
const measurements = {
|
|
529
371
|
latestRatio: null as number | null,
|
|
530
372
|
lastProviderMeasurement: null as ProviderContextMeasurement | null,
|
|
531
|
-
/** What the host/model descriptor last claimed, kept so the gap stays auditable. */
|
|
532
373
|
descriptorWindow: null as number | null,
|
|
533
|
-
/** Serialized size of the projection this process last handed the host. */
|
|
534
374
|
lastProjectedChars: null as number | null,
|
|
535
|
-
/**
|
|
536
|
-
* Ground truth for the transmission fence: recent pairings of a projection's size
|
|
537
|
-
* with the token count the provider reported for it. A fixed bytes-per-token
|
|
538
|
-
* constant is a guess about a tokenizer, and it is wrong by different amounts in
|
|
539
|
-
* different sessions -- measured 2026-08-06, 4.7 chars/token in rep11 and 7.0 in
|
|
540
|
-
* rep12 for the SAME workload. It also DRIFTS within one session: rep13 moved
|
|
541
|
-
* between 5.34 and 6.01 over its last twenty requests. Only the recent window
|
|
542
|
-
* counts, and the fence reads it pessimistically.
|
|
543
|
-
*/
|
|
544
375
|
projectionCalibrations: [] as Array<{ chars: number; tokens: number }>,
|
|
545
|
-
/** What the fence estimated for the projection it last handed the host. */
|
|
546
376
|
lastProjectedEstimate: null as number | null,
|
|
547
|
-
/** Whether that estimate used a measured ratio rather than the bootstrap constant. */
|
|
548
377
|
lastProjectedEstimateCalibrated: false,
|
|
549
|
-
/** How that reading was reached, so an audit can tell an anchor from a fallback. */
|
|
550
378
|
lastProjectedEstimateBasis: "unmeasured" as ProjectionReadingBasis,
|
|
551
|
-
/** That projection's own size at one rate, the baseline a reduction is measured from. */
|
|
552
379
|
lastProjectedSizeTokens: null as number | null,
|
|
553
|
-
/**
|
|
554
|
-
* The provider's own count for the last projection this process TRANSMITTED, held
|
|
555
|
-
* with that projection's serialized text so a later projection can be tested against
|
|
556
|
-
* it by construction. `head` is the serialization minus its closing bracket, which is
|
|
557
|
-
* what makes "the same projection with more appended after it" a byte-exact prefix
|
|
558
|
-
* test rather than a size comparison.
|
|
559
|
-
*/
|
|
560
380
|
projectionAnchor: null as {
|
|
561
381
|
tokens: number;
|
|
562
382
|
chars: number;
|
|
@@ -565,11 +385,8 @@ export function registerActiveContext(pi: any, options: {
|
|
|
565
385
|
sessionId: string;
|
|
566
386
|
generation: number;
|
|
567
387
|
} | null,
|
|
568
|
-
/** Signed estimator error against recent measurements, as a share of measured tokens. */
|
|
569
388
|
estimatorErrors: [] as number[],
|
|
570
|
-
/** Growth in measured tokens between consecutive requests. */
|
|
571
389
|
inflowSteps: [] as number[],
|
|
572
|
-
/** Growth steps the OUTPUT WALL reads. Separate from `inflowSteps` on purpose: see noteWallInflow. */
|
|
573
390
|
wallInflowSteps: [] as number[],
|
|
574
391
|
providerMeasurementQueue: Promise.resolve<void>(undefined),
|
|
575
392
|
providerMeasurementReceipts: new Set<string>(),
|
|
@@ -578,142 +395,79 @@ export function registerActiveContext(pi: any, options: {
|
|
|
578
395
|
providerMeasurementAnchorByMessageSha: new Map<string, ProviderMeasurementAnchor>(),
|
|
579
396
|
};
|
|
580
397
|
|
|
581
|
-
// Owns the projection/cache ledger. Nothing here is durable: it is telemetry about
|
|
582
|
-
// this process's own projections, rebuilt from scratch on every load.
|
|
583
398
|
const instrumentation = {
|
|
584
399
|
ledger: emptyLedger(),
|
|
585
400
|
previousDigests: null as string[] | null,
|
|
586
|
-
/** The previous TRANSMITTED projection, for the byte-level prefix comparison. */
|
|
587
401
|
previousText: null as string | null,
|
|
588
402
|
lastChange: "append" as ProjectionChange,
|
|
589
|
-
/** Share of the PREVIOUS prompt the last projection kept, for miss attribution. */
|
|
590
403
|
lastPreservedShare: null as number | null,
|
|
591
|
-
/** Events emitted since the last handoff, which is the attribution candidate set. */
|
|
592
404
|
sinceHandoff: [] as Array<{ seq: number; kind: string }>,
|
|
593
|
-
/** Structural mutations emitted since the last handoff. The per-generation budget. */
|
|
594
405
|
mutationsSinceHandoff: 0,
|
|
595
|
-
/** Provider responses this session, so event SPACING is readable from the stream. */
|
|
596
406
|
requests: 0,
|
|
597
407
|
lastMutationRequest: 0,
|
|
598
408
|
lastMutationTokens: null as number | null,
|
|
409
|
+
// WHAT THE SESSION SPENDS DERIVING ITSELF, cumulative for the process.
|
|
410
|
+
//
|
|
411
|
+
// Every lifecycle event remaps the whole branch, so the cost of a turn is not the
|
|
412
|
+
// cost of one derivation: it is however many the handlers happen to run. Measuring
|
|
413
|
+
// it at the two entry points and carrying the running totals on the projection means
|
|
414
|
+
// a consumer differences consecutive projections for the per-request cost and reads
|
|
415
|
+
// the last one for the exact session total. That is a SUM. The alternative, taking
|
|
416
|
+
// one terminal derivation and multiplying by the request count, treats every early
|
|
417
|
+
// request as though the session were already at its final size, which is invalid
|
|
418
|
+
// precisely when growth is the thing under study.
|
|
419
|
+
//
|
|
420
|
+
// These are process counters, not state. A rollback does not return the CPU, so they
|
|
421
|
+
// do not roll back either.
|
|
422
|
+
derivations: 0,
|
|
423
|
+
deriveMs: 0,
|
|
424
|
+
derivationHits: 0,
|
|
599
425
|
};
|
|
600
|
-
/**
|
|
601
|
-
* MECHANISM 1. The frozen surface.
|
|
602
|
-
*
|
|
603
|
-
* Between fold events the projection is byte-frozen: the only change a context action
|
|
604
|
-
* may cause is the append of its own tool result at the tail. Rep 17 measured every
|
|
605
|
-
* single context attempt landing as a projection REWRITE -- status 0.60-0.90 prefix
|
|
606
|
-
* identity, peek 0.96, the held gate down to 0.013 -- because the surface answered
|
|
607
|
-
* each one by re-rendering blocks the agent had already paid to cache. Provider prefix
|
|
608
|
-
* caches are positional, so each of those refreshes was a full or partial cache kill.
|
|
609
|
-
*
|
|
610
|
-
* `body` is the last rebuilt fold projection and `projection` is that body plus the
|
|
611
|
-
* carriers appended to it. While the rebuilt body still starts with `body` verbatim,
|
|
612
|
-
* nothing structural moved, so the previous projection is reused WHOLE and only the
|
|
613
|
-
* newly arrived raw messages are appended after it. A carrier whose key is already in
|
|
614
|
-
* the window is never re-rendered; a fold event, a reveal or an overflow recovery
|
|
615
|
-
* diverges the body and takes the sanctioned rewrite.
|
|
616
|
-
*/
|
|
617
426
|
const freeze = {
|
|
618
427
|
body: null as unknown[] | null,
|
|
619
428
|
bodyText: null as string | null,
|
|
620
429
|
projection: null as unknown[] | null,
|
|
621
430
|
keys: new Set<string>(),
|
|
622
|
-
/** True only while assembling a pass that reused the frozen projection. */
|
|
623
431
|
active: false,
|
|
624
432
|
};
|
|
625
|
-
/**
|
|
626
|
-
* A carrier may be built exactly once per freeze window. This is checked BEFORE the
|
|
627
|
-
* builder runs, because building a milestone spends its delivery budget and building
|
|
628
|
-
* a reminder advances the reminder cursor: a carrier that cannot land must not pay.
|
|
629
|
-
*/
|
|
630
433
|
const carrierAdmitted = (key: string): boolean => {
|
|
631
434
|
if (freeze.active && freeze.keys.has(key)) return false;
|
|
632
435
|
freeze.keys.add(key);
|
|
633
436
|
return true;
|
|
634
437
|
};
|
|
635
438
|
|
|
636
|
-
// Owns the guided-curation gate, the receipt ring, the context-event stream, and the
|
|
637
|
-
// overflow recovery lane. Nothing here is durable session STATE: it describes what
|
|
638
|
-
// this process did to the live window, and the durable copy is the appended
|
|
639
|
-
// instrumentation entry, which is what an external adjudicator reads.
|
|
640
439
|
const curation = {
|
|
641
440
|
receipts: [] as ContextReceipt[],
|
|
642
|
-
/** Every context-management tool call this session. */
|
|
643
441
|
contextCalls: 0,
|
|
644
|
-
/** The last measurement the commit trigger took, reported by status. */
|
|
645
|
-
lastSignals: null as CurationSignals | null,
|
|
646
|
-
/**
|
|
647
|
-
* COMMIT REOPEN HYSTERESIS. The eligible foldable share measured just after the last
|
|
648
|
-
* triggered commit, or null when the trigger is free to fire. Measured 2026-08-07
|
|
649
|
-
* (rep 17): occupancy plateaued at 0.85-0.92 of the truthful budget for the final
|
|
650
|
-
* third of the run, and the trigger fired ten times over a window whose foldable mass
|
|
651
|
-
* had not changed. A plateau is not an event. The trigger re-arms only once occupancy
|
|
652
|
-
* falls back under it and crosses again, or once a reclaim floor of eligible mass is
|
|
653
|
-
* NEW. This outlived the announcement it was built beside: it bounds how often the
|
|
654
|
-
* runtime rewrites the projection, which is the whole cost model.
|
|
655
|
-
*/
|
|
656
|
-
reopenBaselineShare: null as number | null,
|
|
657
|
-
/** Whether the output wall's crossing has already spent its one forced commit. */
|
|
658
442
|
wallEpisodeOpen: false,
|
|
659
|
-
/**
|
|
660
|
-
* Where the armed last-call actually LANDED: the exposure it rendered and the
|
|
661
|
-
* ordinal of the pass that rendered it. In-memory on purpose: after a reload the
|
|
662
|
-
* carrier re-renders on the first projection, this re-arms, and the commit waits
|
|
663
|
-
* one more round rather than firing on a prompt nobody saw.
|
|
664
|
-
*/
|
|
665
|
-
lastCallDelivery: null as { exposure: number; ordinal: number } | null,
|
|
666
|
-
/** Recovery attempts spent on the CURRENT inflow; reset by any accepted request. */
|
|
667
443
|
recoveryAttempts: 0,
|
|
668
|
-
/** A provider rejection observed but not yet recovered from. */
|
|
669
444
|
pendingRejection: null as { status: number; ordinal: number } | null,
|
|
670
445
|
lastRecovery: null as Record<string, unknown> | null,
|
|
671
446
|
instrumentationQueue: Promise.resolve<void>(undefined),
|
|
672
447
|
};
|
|
673
448
|
|
|
674
|
-
/**
|
|
675
|
-
* The tree-rollback lane.
|
|
676
|
-
*
|
|
677
|
-
* Armed once per session against the pi surfaces it calls, because every one of them
|
|
678
|
-
* is an internal the extension contract does not promise. Disarmed, the lane is OFF
|
|
679
|
-
* and says so on the record: an overflow still emits `context.rollback` with
|
|
680
|
-
* `armed: false` and the reason, the user is notified, and the session behaves
|
|
681
|
-
* exactly as it did before this build -- the projection-budget fence aborts an
|
|
682
|
-
* over-budget request rather than half-performing a recovery.
|
|
683
|
-
*/
|
|
684
449
|
const rollback = {
|
|
685
450
|
probes: null as ReturnType<typeof probeRollbackSurfaces> | null,
|
|
686
451
|
armed: false,
|
|
687
|
-
/** pi's own overflow classifier, resolved from the host; null when unreachable. */
|
|
688
452
|
classifier: null as ((message: unknown, contextWindow: number) => boolean) | null,
|
|
689
453
|
classifierSource: null as string | null,
|
|
690
|
-
/** Ordinals of episodes handled, so the one-shot and the ledger agree. */
|
|
691
454
|
attempts: 0,
|
|
692
455
|
last: null as Record<string, unknown> | null,
|
|
693
|
-
/** An overflow seen at message_end and not yet claimed by the compaction event. */
|
|
694
456
|
pendingOverflow: null as { at: number; entryId: string | null } | null,
|
|
695
457
|
};
|
|
696
458
|
|
|
697
|
-
// Owns advisory arming and hard-fence delivery; durable effects use persistenceQueue.
|
|
698
459
|
const advisory = {
|
|
699
460
|
hardFenceNoticeKey: null as string | null,
|
|
700
461
|
hardFenceReleaseSessionId: null as string | null,
|
|
701
462
|
hardFenceReleasedProjectionKeys: new Set<string>(),
|
|
702
463
|
};
|
|
703
464
|
|
|
704
|
-
// Owns native-compaction decisions and completion retry state; nativeReceiptQueue serializes it.
|
|
705
465
|
const nativeCompaction = {
|
|
706
466
|
lastThresholdDecision: null as Record<string, unknown> | null,
|
|
707
467
|
pendingNativeReceipt: null as NativeCompactionCompletionReceipt | null,
|
|
708
468
|
nativeReceiptQueue: Promise.resolve<void>(undefined),
|
|
709
469
|
};
|
|
710
470
|
|
|
711
|
-
// Owns session generation, snapshots/reloads, and per-turn blocking-tool harvest state.
|
|
712
|
-
// Pi normally requests context serially, but retries, reloads, and host
|
|
713
|
-
// integrations can overlap callbacks. Serialize the entire authority →
|
|
714
|
-
// preparation → commit → projection transaction so a follower cannot
|
|
715
|
-
// observe a published measurement before the leader's durable receipt or
|
|
716
|
-
// return raw final-rung context while the leader is preparing a brief.
|
|
717
471
|
const lifecycle = {
|
|
718
472
|
generation: 0,
|
|
719
473
|
shuttingDown: false,
|
|
@@ -750,16 +504,34 @@ export function registerActiveContext(pi: any, options: {
|
|
|
750
504
|
lifecycle.latestSnapshot ? markOrdinal(lifecycle.latestSnapshot) : 0;
|
|
751
505
|
|
|
752
506
|
/**
|
|
753
|
-
*
|
|
754
|
-
*
|
|
755
|
-
*
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
*
|
|
759
|
-
*
|
|
760
|
-
*
|
|
507
|
+
* ONE DERIVATION PER DISTINCT BRANCH, WITHIN ONE PASS.
|
|
508
|
+
*
|
|
509
|
+
* A lifecycle pass asks for the authoritative snapshot repeatedly: the fence asks, the
|
|
510
|
+
* ladder asks, the accounting asks, the status surface asks. Instrumented on a fixture,
|
|
511
|
+
* a single request cost up to TEN derivations, and a derivation remaps the entire
|
|
512
|
+
* branch. On sol-20260813-paired rep 2 one of those cost 18.5 seconds.
|
|
513
|
+
*
|
|
514
|
+
* The memo is deliberately the SMALLEST one that is airtight rather than the largest
|
|
515
|
+
* one that would pay. It lives for exactly one pass and is dropped at the start of the
|
|
516
|
+
* next, so nothing it holds can outlive the moment it was true. Within a pass the only
|
|
517
|
+
* way the branch changes is by APPENDING, which moves a length, so keying on the two
|
|
518
|
+
* lengths and the window catches every change that can occur. A cache that had to
|
|
519
|
+
* reason about entries being replaced underneath it would be the kind of guard this
|
|
520
|
+
* project does not keep; this one cannot face that question.
|
|
761
521
|
*/
|
|
762
|
-
const
|
|
522
|
+
const derivationMemo = {
|
|
523
|
+
key: null as string | null,
|
|
524
|
+
snapshot: null as ActiveContextSnapshot | null,
|
|
525
|
+
};
|
|
526
|
+
const dropDerivationMemo = (): void => {
|
|
527
|
+
derivationMemo.key = null;
|
|
528
|
+
derivationMemo.snapshot = null;
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
const beginMutationPass = (): void => {
|
|
532
|
+
instrumentation.mutationsSinceHandoff = 0;
|
|
533
|
+
dropDerivationMemo();
|
|
534
|
+
};
|
|
763
535
|
|
|
764
536
|
const emit = (kind: ContextEventKind, payload: Record<string, unknown> = {}): ContextEvent => {
|
|
765
537
|
const record = recordContextEvent(instrumentation.ledger, kind, {
|
|
@@ -771,114 +543,12 @@ export function registerActiveContext(pi: any, options: {
|
|
|
771
543
|
instrumentation.sinceHandoff.push({ seq: record.seq, kind });
|
|
772
544
|
const operation = curation.instrumentationQueue.then(async () => {
|
|
773
545
|
try { await pi.appendEntry(contextEventEntryType, record); }
|
|
774
|
-
catch {
|
|
546
|
+
catch { }
|
|
775
547
|
});
|
|
776
548
|
curation.instrumentationQueue = operation.then(() => undefined, () => undefined);
|
|
777
549
|
return record;
|
|
778
550
|
};
|
|
779
551
|
|
|
780
|
-
/**
|
|
781
|
-
* The generator, wrapped once so every brief it writes is on the record.
|
|
782
|
-
*
|
|
783
|
-
* Wrapping HERE rather than instrumenting the two call sites is deliberate. The inline
|
|
784
|
-
* path in `prepareFold` and the async upgrade lane both receive this function, so one
|
|
785
|
-
* wrapper covers both, no signature changes and no way to add a third caller that
|
|
786
|
-
* quietly escapes the ledger. It also covers a deployment's OWN summarizer, which is
|
|
787
|
-
* the case a call-site patch would have missed.
|
|
788
|
-
*
|
|
789
|
-
* A generator call is invisible everywhere else: it runs outside the session, so it
|
|
790
|
-
* lands in no provider ledger, and it runs off the turn, so it lands in no turn timing.
|
|
791
|
-
* Duration, queue wait and usage are therefore recorded here or nowhere.
|
|
792
|
-
*
|
|
793
|
-
* A failure is emitted and rethrown unchanged. This observes; it decides nothing, and
|
|
794
|
-
* the fallback to the deterministic brief stays exactly where it was.
|
|
795
|
-
*/
|
|
796
|
-
const observedSummarize = options.summarizeContextSpan
|
|
797
|
-
? async (request: Record<string, unknown>, summarizerCtx: unknown): Promise<Record<string, unknown>> => {
|
|
798
|
-
const startedAt = Date.now();
|
|
799
|
-
const queuedAt = typeof request.queuedAtMs === "number" ? request.queuedAtMs : null;
|
|
800
|
-
const spans = Array.isArray(request.spans)
|
|
801
|
-
? request.spans as Array<Record<string, unknown>>
|
|
802
|
-
: null;
|
|
803
|
-
const sourceText = typeof request.sourceText === "string" ? request.sourceText : "";
|
|
804
|
-
// A span is a CONSOLIDATION when it is a group of folds, and an automatic fold
|
|
805
|
-
// otherwise. Both kinds are counted and both kinds' source is measured, because a
|
|
806
|
-
// call can carry a mix and the honest way to attribute its tokens across kinds is by
|
|
807
|
-
// the share of source each kind contributed, never by pretending the call was one
|
|
808
|
-
// kind (Shane 2026-08-11: track cures and tokens by fold type).
|
|
809
|
-
const groupSpan = (span: Record<string, unknown>): boolean =>
|
|
810
|
-
Number.isInteger(span.children) && (span.children as number) > 1;
|
|
811
|
-
const charsOf = (span: Record<string, unknown>): number =>
|
|
812
|
-
typeof span.sourceText === "string" ? span.sourceText.length : 0;
|
|
813
|
-
const kindCounts = spans
|
|
814
|
-
? {
|
|
815
|
-
spans: spans.length,
|
|
816
|
-
group_spans: spans.filter(groupSpan).length,
|
|
817
|
-
leaf_spans: spans.filter((span) => !groupSpan(span)).length,
|
|
818
|
-
group_source_chars: spans.filter(groupSpan).reduce((sum, span) => sum + charsOf(span), 0),
|
|
819
|
-
leaf_source_chars: spans.filter((span) => !groupSpan(span))
|
|
820
|
-
.reduce((sum, span) => sum + charsOf(span), 0),
|
|
821
|
-
fold_ids: spans.map((span) => typeof span.candidateId === "string" ? span.candidateId : ""),
|
|
822
|
-
}
|
|
823
|
-
: {
|
|
824
|
-
spans: 1,
|
|
825
|
-
group_spans: groupSpan(request) ? 1 : 0,
|
|
826
|
-
leaf_spans: groupSpan(request) ? 0 : 1,
|
|
827
|
-
group_source_chars: groupSpan(request) ? sourceText.length : 0,
|
|
828
|
-
leaf_source_chars: groupSpan(request) ? 0 : sourceText.length,
|
|
829
|
-
};
|
|
830
|
-
const base = {
|
|
831
|
-
fold_id: typeof request.candidateId === "string" ? request.candidateId : "",
|
|
832
|
-
source_chars: spans ? spans.reduce((sum, span) => sum + charsOf(span), 0) : sourceText.length,
|
|
833
|
-
source_sha256: typeof request.sourceSha256 === "string" ? request.sourceSha256 : sha256Text(sourceText),
|
|
834
|
-
...kindCounts,
|
|
835
|
-
// The second ask, not the first. How often this fires is how we learn whether the
|
|
836
|
-
// brief cap is anywhere near right, so it is a field rather than an inference.
|
|
837
|
-
cure: typeof request.cure === "string" && request.cure.length > 0,
|
|
838
|
-
...(queuedAt === null ? {} : { queued_ms: Math.max(0, startedAt - queuedAt) }),
|
|
839
|
-
};
|
|
840
|
-
try {
|
|
841
|
-
const result = await options.summarizeContextSpan!(request, summarizerCtx);
|
|
842
|
-
const written = Array.isArray(result?.briefs)
|
|
843
|
-
? (result.briefs as unknown[]).map((item) => typeof item === "string" ? item.trim() : "")
|
|
844
|
-
: [typeof result?.brief === "string" ? result.brief.trim() : ""];
|
|
845
|
-
emit("context.brief", {
|
|
846
|
-
...base,
|
|
847
|
-
outcome: "ok",
|
|
848
|
-
duration_ms: Date.now() - startedAt,
|
|
849
|
-
provider: typeof result?.provider === "string" ? result.provider : "",
|
|
850
|
-
model: typeof result?.model === "string" ? result.model : "",
|
|
851
|
-
effort: typeof result?.effort === "string" ? result.effort : "",
|
|
852
|
-
brief_chars: written.reduce((sum, item) => sum + item.length, 0),
|
|
853
|
-
// Per span, so a batch where one brief came back empty is visible as that rather
|
|
854
|
-
// than as a slightly short total.
|
|
855
|
-
brief_chars_each: written.map((item) => item.length),
|
|
856
|
-
brief_sha256: written.length === 1 && written[0] ? sha256Text(written[0]) : "",
|
|
857
|
-
// Absent when the provider reported none. Never defaulted: a call whose cost we
|
|
858
|
-
// do not know and a call that cost nothing are different facts.
|
|
859
|
-
usage: result?.usage && typeof result.usage === "object" ? clone(result.usage) : null,
|
|
860
|
-
});
|
|
861
|
-
return result;
|
|
862
|
-
} catch (error) {
|
|
863
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
864
|
-
emit("context.brief", {
|
|
865
|
-
...base,
|
|
866
|
-
// A timeout is not a refusal and not a provider fault, and telling them apart is
|
|
867
|
-
// the difference between raising the bound and changing the generator.
|
|
868
|
-
outcome: /exceeded \d+ms/.test(message) ? "timeout" : "error",
|
|
869
|
-
duration_ms: Date.now() - startedAt,
|
|
870
|
-
error: boundReceiptText(message, 240, "brief generator"),
|
|
871
|
-
});
|
|
872
|
-
throw error;
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
: undefined;
|
|
876
|
-
|
|
877
|
-
/**
|
|
878
|
-
* Actions of ours that move bytes at or before a prefix position. Receipts, gate
|
|
879
|
-
* notices and suggestions are appended after the whole projection, so they can only
|
|
880
|
-
* move the tail and are never a prefix cause.
|
|
881
|
-
*/
|
|
882
552
|
const PREFIX_MUTATING_KINDS: ReadonlySet<string> = new Set([
|
|
883
553
|
"context.commit", "context.fold", "context.absorb", "context.split", "context.recovery",
|
|
884
554
|
]);
|
|
@@ -901,7 +571,7 @@ export function registerActiveContext(pi: any, options: {
|
|
|
901
571
|
};
|
|
902
572
|
|
|
903
573
|
const safeNotify = (ctx: any, message: string, level: "info" | "warning" | "error"): void => {
|
|
904
|
-
try { ctx.ui?.notify?.(message, level); } catch {
|
|
574
|
+
try { ctx.ui?.notify?.(message, level); } catch { }
|
|
905
575
|
};
|
|
906
576
|
|
|
907
577
|
const contextSessionMatches = (ctx: any, sessionId: string): boolean => {
|
|
@@ -922,34 +592,14 @@ export function registerActiveContext(pi: any, options: {
|
|
|
922
592
|
: " · provider usage unmeasured";
|
|
923
593
|
const suspended = ladder.automaticFailure ? " · automatic suspended" : "";
|
|
924
594
|
ctx.ui?.setStatus?.(entryTypePrefix, `${toolName} folds: ${roots}${prepared}${usage}${suspended}`);
|
|
925
|
-
} catch {
|
|
595
|
+
} catch { }
|
|
926
596
|
};
|
|
927
597
|
|
|
928
|
-
/**
|
|
929
|
-
* The number every ratio, fence and budget is computed against. Declared, it is the
|
|
930
|
-
* deployment's own serving budget, already net of whatever output reservation it holds
|
|
931
|
-
* back; undeclared, it is the per-request max-input descriptor, which bakes in a full
|
|
932
|
-
* output reservation the deployment never asked for and so aborts requests inside real
|
|
933
|
-
* headroom. The descriptor is still read, and still reported, so the gap stays
|
|
934
|
-
* auditable rather than assumed.
|
|
935
|
-
*/
|
|
936
598
|
const budgetWindowFor = (ctx: any): number | null => {
|
|
937
|
-
// Remembered so the ctx-free callers (trigger, gate, reminders, advisory) report the
|
|
938
|
-
// same descriptor gap the fence does instead of re-reading a second source.
|
|
939
599
|
measurements.descriptorWindow = contextWindowFor(ctx);
|
|
940
600
|
return providerInputBudget ?? measurements.descriptorWindow;
|
|
941
601
|
};
|
|
942
602
|
|
|
943
|
-
/**
|
|
944
|
-
* THE serving budget. One resolution, one formula, one value.
|
|
945
|
-
*
|
|
946
|
-
* Every consumer -- the curation trigger, the last-call gate, the sparse reminders,
|
|
947
|
-
* the transmission fence, the projection estimator and every instrumentation record --
|
|
948
|
-
* reads this. Measured 2026-08-06 (rep 15): the trigger and gate ran a whole run
|
|
949
|
-
* against a 272,000-token per-request DESCRIPTOR (budget 255,616) while the fence used
|
|
950
|
-
* the truthful 383,616, because the same arithmetic lived in three places. A second
|
|
951
|
-
* copy of this formula is the defect; there is now only one.
|
|
952
|
-
*/
|
|
953
603
|
const servingCapacity = (window: number | null): ReturnType<typeof capacityAccounting> =>
|
|
954
604
|
capacityAccounting({
|
|
955
605
|
window: window ?? lifecycle.latestSnapshot?.contextWindow ?? DEFAULT_CONTEXT_WINDOW,
|
|
@@ -961,7 +611,23 @@ export function registerActiveContext(pi: any, options: {
|
|
|
961
611
|
const currentCapacity = (ctx: any): ReturnType<typeof capacityAccounting> =>
|
|
962
612
|
servingCapacity(budgetWindowFor(ctx));
|
|
963
613
|
|
|
964
|
-
|
|
614
|
+
/**
|
|
615
|
+
* The one place a derivation is timed. Both entry points route through it, so the
|
|
616
|
+
* counters cannot drift from the work: a new call site that forgets to time itself
|
|
617
|
+
* would have to call `mapActiveContext` directly, which nothing outside these two
|
|
618
|
+
* functions does.
|
|
619
|
+
*/
|
|
620
|
+
const timedDerivation = (derive: () => ActiveContextSnapshot): ActiveContextSnapshot => {
|
|
621
|
+
const started = Date.now();
|
|
622
|
+
try { return derive(); }
|
|
623
|
+
finally {
|
|
624
|
+
instrumentation.derivations += 1;
|
|
625
|
+
instrumentation.deriveMs += Date.now() - started;
|
|
626
|
+
}
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
const snapshotForEvent = (ctx: any, messages: unknown[]): ActiveContextSnapshot =>
|
|
630
|
+
timedDerivation(() => mapActiveContext({
|
|
965
631
|
sessionId: ctx.sessionManager.getSessionId(),
|
|
966
632
|
eventMessages: messages,
|
|
967
633
|
contextEntries: ctx.sessionManager.buildContextEntries(),
|
|
@@ -973,27 +639,37 @@ export function registerActiveContext(pi: any, options: {
|
|
|
973
639
|
contextWindow: budgetWindowFor(ctx) ?? undefined,
|
|
974
640
|
netBudget: providerInputBudget !== null,
|
|
975
641
|
thresholds,
|
|
976
|
-
});
|
|
642
|
+
}));
|
|
977
643
|
|
|
978
644
|
const authoritativeSnapshotFor = (ctx: any): ActiveContextSnapshot => {
|
|
979
645
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
980
646
|
if (!lifecycle.latestSnapshot || lifecycle.latestSnapshot.sessionId !== sessionId) {
|
|
981
647
|
throw new Error("A current same-session Pi context event is required");
|
|
982
648
|
}
|
|
983
|
-
|
|
649
|
+
const entries = ctx.sessionManager.buildContextEntries();
|
|
650
|
+
const window = budgetWindowFor(ctx) ?? undefined;
|
|
651
|
+
const key = `${sessionId}:${entries.length}:${lifecycle.latestSnapshot.messages.length}:${window}`;
|
|
652
|
+
if (derivationMemo.snapshot && derivationMemo.key === key) {
|
|
653
|
+
instrumentation.derivationHits += 1;
|
|
654
|
+
return derivationMemo.snapshot;
|
|
655
|
+
}
|
|
656
|
+
const derived = timedDerivation(() => mapActiveContext({
|
|
984
657
|
sessionId,
|
|
985
658
|
eventMessages: lifecycle.latestSnapshot.messages,
|
|
986
|
-
contextEntries:
|
|
659
|
+
contextEntries: entries,
|
|
987
660
|
policy: lifecycle.latestSnapshot.policy,
|
|
988
661
|
toolName,
|
|
989
662
|
brandNoun,
|
|
990
663
|
entryTypePrefix,
|
|
991
664
|
blacklistAutoFoldTools,
|
|
992
665
|
readOnlyContextActions,
|
|
993
|
-
contextWindow:
|
|
666
|
+
contextWindow: window,
|
|
994
667
|
netBudget: providerInputBudget !== null,
|
|
995
668
|
thresholds,
|
|
996
|
-
});
|
|
669
|
+
}));
|
|
670
|
+
derivationMemo.key = key;
|
|
671
|
+
derivationMemo.snapshot = derived;
|
|
672
|
+
return derived;
|
|
997
673
|
};
|
|
998
674
|
|
|
999
675
|
const providerMeasurementBranchIndex = (
|
|
@@ -1036,7 +712,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
1036
712
|
lifecycle.generation += 1;
|
|
1037
713
|
lifecycle.shuttingDown = false;
|
|
1038
714
|
cancelPreparation();
|
|
1039
|
-
cancelBriefUpgrades();
|
|
1040
715
|
lifecycle.latestSnapshot = null;
|
|
1041
716
|
lifecycle.latestSnapshotError = null;
|
|
1042
717
|
measurements.latestRatio = null;
|
|
@@ -1064,15 +739,12 @@ export function registerActiveContext(pi: any, options: {
|
|
|
1064
739
|
instrumentation.mutationsSinceHandoff = 0;
|
|
1065
740
|
curation.receipts = [];
|
|
1066
741
|
curation.contextCalls = 0;
|
|
1067
|
-
curation.lastSignals = null;
|
|
1068
|
-
curation.reopenBaselineShare = null;
|
|
1069
742
|
curation.recoveryAttempts = 0;
|
|
1070
743
|
curation.pendingRejection = null;
|
|
1071
744
|
curation.lastRecovery = null;
|
|
1072
745
|
ladder.lastAutomaticAction = null;
|
|
1073
746
|
ladder.automaticFailure = null;
|
|
1074
747
|
advisory.hardFenceNoticeKey = null;
|
|
1075
|
-
ladder.failedPreparations.clear();
|
|
1076
748
|
measurements.providerMeasurementReceipts.clear();
|
|
1077
749
|
measurements.providerMeasurementRevisionByMessageSha.clear();
|
|
1078
750
|
measurements.providerMeasurementByMessageSha.clear();
|
|
@@ -1159,43 +831,29 @@ export function registerActiveContext(pi: any, options: {
|
|
|
1159
831
|
const operation = persistence.persistenceQueue.then(async () => {
|
|
1160
832
|
if (!persistence.state || !persistence.persisted) return;
|
|
1161
833
|
let next = clone(persistence.state);
|
|
1162
|
-
// Which folds this write is TRYING to add, read before the projection can drop them.
|
|
1163
834
|
const persistedFoldIds = new Set(persistence.persisted.folds.map((fold) => fold.id));
|
|
1164
835
|
const arrivingFoldIds = next.folds.filter((fold) => !persistedFoldIds.has(fold.id)).map((fold) => fold.id);
|
|
1165
836
|
const foldsInMemory = next.folds.length;
|
|
837
|
+
let projectionSnapshot: ActiveContextSnapshot | null = null;
|
|
1166
838
|
if (ctx && lifecycle.latestSnapshot?.sessionId === next.sessionId) {
|
|
1167
|
-
|
|
839
|
+
projectionSnapshot = authoritativeSnapshotFor(ctx);
|
|
840
|
+
next = persistenceProjection(next, projectionSnapshot);
|
|
1168
841
|
}
|
|
1169
842
|
next.folds = normalizeFoldsForPersistedRecords(next.folds, persistence.persistedFoldRecords);
|
|
1170
843
|
if (sameStateProjection(next, persistence.persisted)) {
|
|
1171
|
-
// A COMMIT THAT VANISHES IS NOT A NO-OP.
|
|
1172
|
-
//
|
|
1173
|
-
// `persistenceProjection` keeps only folds whose refs still resolve against the
|
|
1174
|
-
// snapshot, which is correct when evidence has genuinely left the branch. But when
|
|
1175
|
-
// a write ARRIVES with new folds and projects down to something byte-equal to what
|
|
1176
|
-
// is already durable, every one of those folds was dropped, and returning quietly
|
|
1177
|
-
// here also resets `persistence.state` BACKWARDS to the persisted revision. The
|
|
1178
|
-
// marks that produced them go back to pending, the same commit is attempted again
|
|
1179
|
-
// on the next boundary, and it vanishes again.
|
|
1180
|
-
//
|
|
1181
|
-
// Measured, sol-20260812 rep 3: a band-top commit ran at revision 143 and folded
|
|
1182
|
-
// 579,489 source chars into 85,463 of placeholder. Durable state never passed 134.
|
|
1183
|
-
// None of its 11 folds reached a record, no receipt was written, occupancy climbed
|
|
1184
|
-
// 237,682 -> 253,423 against a 251,520 budget unopposed, and the next request was
|
|
1185
|
-
// refused. Nothing was reported, because a silent return is not a failure. Rep 2
|
|
1186
|
-
// ran 15 commits on the same plan with no gap at all, so this is rare rather than
|
|
1187
|
-
// systemic, which is exactly why it has to announce itself the first time.
|
|
1188
|
-
//
|
|
1189
|
-
// Raising it routes through the automatic-failure path, which restores the state
|
|
1190
|
-
// this call was about to discard, suspends automatic folding and tells the user.
|
|
1191
|
-
// Suspending is the safe direction: continuing to compute folds that are thrown
|
|
1192
|
-
// away is what turned one dropped commit into a dead session.
|
|
1193
844
|
if (arrivingFoldIds.length) {
|
|
845
|
+
const lost = clone(persistence.state).folds.find((fold) => fold.id === arrivingFoldIds[0]);
|
|
846
|
+
const lostRefs = lost ? flattenFoldRefs(lost, persistence.state) : [];
|
|
847
|
+
const unresolved = projectionSnapshot
|
|
848
|
+
? lostRefs.filter((ref) => !exactMapped(projectionSnapshot!, ref)).length
|
|
849
|
+
: null;
|
|
1194
850
|
throw new Error(
|
|
1195
851
|
`Active-context commit discarded at persistence: ${arrivingFoldIds.length} arriving fold(s) ` +
|
|
1196
852
|
`did not survive the persistence projection (${foldsInMemory} in memory, ` +
|
|
1197
853
|
`${next.folds.length} durable) at revision ${persistence.state.revision}; ` +
|
|
1198
|
-
`first ${arrivingFoldIds[0]}
|
|
854
|
+
`first ${arrivingFoldIds[0]} with ${unresolved ?? "unknown"} of ${lostRefs.length} refs ` +
|
|
855
|
+
`unmapped against a ${projectionSnapshot?.mapped.length ?? 0}-message, ` +
|
|
856
|
+
`${projectionSnapshot?.branchObjects.length ?? 0}-object projection snapshot`,
|
|
1199
857
|
);
|
|
1200
858
|
}
|
|
1201
859
|
persistence.state = clone(persistence.persisted);
|
|
@@ -1295,7 +953,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
1295
953
|
occurredAt: Date.now(),
|
|
1296
954
|
}, sessionId);
|
|
1297
955
|
await pi.appendEntry(providerMeasurementEntryType, receipt);
|
|
1298
|
-
// The append is authoritative even if lifecycle attribution changes immediately after it.
|
|
1299
956
|
measurements.providerMeasurementReceipts.add(receiptKey);
|
|
1300
957
|
measurements.providerMeasurementRevisionByMessageSha.set(measurement.messageSha256, revision);
|
|
1301
958
|
measurements.providerMeasurementByMessageSha.set(measurement.messageSha256, receipt);
|
|
@@ -1510,10 +1167,7 @@ export function registerActiveContext(pi: any, options: {
|
|
|
1510
1167
|
let selection: Record<string, unknown> = { kind: lifecyclePhase };
|
|
1511
1168
|
try {
|
|
1512
1169
|
if (persistence.state && snapshot && ratio !== null) {
|
|
1513
|
-
const selected = selectAutomaticRung(snapshot, persistence.state, ratio
|
|
1514
|
-
summarizerAvailable: Boolean(options.summarizeContextSpan),
|
|
1515
|
-
failedPreparationIds: ladder.failedPreparations,
|
|
1516
|
-
});
|
|
1170
|
+
const selected = selectAutomaticRung(snapshot, persistence.state, ratio);
|
|
1517
1171
|
if (selected?.kind === "refold") {
|
|
1518
1172
|
selection = { kind: "refold", foldId: selected.foldId };
|
|
1519
1173
|
} else if (selected && "candidate" in selected) {
|
|
@@ -1558,6 +1212,25 @@ export function registerActiveContext(pi: any, options: {
|
|
|
1558
1212
|
ladder.boundaryFailure = message;
|
|
1559
1213
|
cancelPreparation();
|
|
1560
1214
|
if (persistence.state?.prepared) persistence.state = clearPrepared(persistence.state);
|
|
1215
|
+
const firstFailure = !ladder.automaticFailure;
|
|
1216
|
+
if (firstFailure || ladder.automaticFailure!.message !== message) {
|
|
1217
|
+
emit("context.suspend", {
|
|
1218
|
+
phase,
|
|
1219
|
+
error: message,
|
|
1220
|
+
error_name: error instanceof Error ? error.name : typeof error,
|
|
1221
|
+
disposition: firstFailure ? persistenceDisposition : ladder.automaticFailure!.persistenceDisposition,
|
|
1222
|
+
outcome: "suspended",
|
|
1223
|
+
repeat: !firstFailure,
|
|
1224
|
+
suppressed_callbacks: ladder.automaticFailure?.suppressedCallbacks ?? 0,
|
|
1225
|
+
state_revision: persistence.state?.revision ?? null,
|
|
1226
|
+
durable_revision: persistence.persisted?.revision ?? null,
|
|
1227
|
+
folds_in_memory: persistence.state?.folds.length ?? null,
|
|
1228
|
+
folds_durable: persistence.persisted?.folds.length ?? null,
|
|
1229
|
+
fold_records_durable: persistence.persistedFoldRecords.size,
|
|
1230
|
+
pending_marks: persistence.state?.pendingMarks?.length ?? 0,
|
|
1231
|
+
key: firstFailure ? key : ladder.automaticFailure!.key,
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1561
1234
|
if (ladder.automaticFailure) {
|
|
1562
1235
|
ladder.automaticFailure.suppressedCallbacks = Math.min(
|
|
1563
1236
|
Number.MAX_SAFE_INTEGER,
|
|
@@ -1587,12 +1260,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
1587
1260
|
const measuredRevision = measurements.lastProviderMeasurement
|
|
1588
1261
|
? measurements.providerMeasurementRevisionByMessageSha.get(measurements.lastProviderMeasurement.messageSha256)
|
|
1589
1262
|
: undefined;
|
|
1590
|
-
// A durable projection topology after the measured response gets exactly
|
|
1591
|
-
// one provider attempt so its fold can be measured. Concurrent
|
|
1592
|
-
// callbacks, retries, and same-session reloads may not repeatedly release
|
|
1593
|
-
// that unmeasured projection. Non-structural state persistence does not
|
|
1594
|
-
// spend this release. A failed automatic transaction never gets this
|
|
1595
|
-
// escape, even if a record/state append preceded its projection failure.
|
|
1596
1263
|
if (allowUnmeasuredRevisionRelease && !ladder.automaticFailure &&
|
|
1597
1264
|
measuredRevision !== undefined && measurements.lastProviderMeasurement &&
|
|
1598
1265
|
!durableProviderMeasurementMatches(measurements.lastProviderMeasurement)) {
|
|
@@ -1625,10 +1292,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
1625
1292
|
"error",
|
|
1626
1293
|
);
|
|
1627
1294
|
}
|
|
1628
|
-
// Stock Pi exposes abort() in every extension event context. Calling it
|
|
1629
|
-
// here aborts the signal passed to the provider stream after this context
|
|
1630
|
-
// transform returns, so exact raw Pi messages remain canonical but are not
|
|
1631
|
-
// transmitted as an overflowing request.
|
|
1632
1295
|
if (typeof ctx.abort !== "function") {
|
|
1633
1296
|
throw new Error(`Pi hard-fence abort capability is unavailable at ratio ${measurements.latestRatio}`);
|
|
1634
1297
|
}
|
|
@@ -1636,40 +1299,18 @@ export function registerActiveContext(pi: any, options: {
|
|
|
1636
1299
|
return true;
|
|
1637
1300
|
};
|
|
1638
1301
|
|
|
1639
|
-
/**
|
|
1640
|
-
* Bytes per token, measured rather than assumed.
|
|
1641
|
-
*
|
|
1642
|
-
* `ESTIMATED_BYTES_PER_TOKEN` is a constant over RAW text, and the fence weighs a
|
|
1643
|
-
* SERIALIZED projection: roles, ids, custom types and JSON escaping all count toward
|
|
1644
|
-
* the bytes and none of them reach the provider's tokenizer the same way. Measured
|
|
1645
|
-
* 2026-08-06 on one workload: rep11 ran at 4.7 serialized chars per measured token
|
|
1646
|
-
* and rep12 at 7.0. Against the fixed 4 that is a 76% over-estimate in rep12, which
|
|
1647
|
-
* is exactly how a session whose real window was 49% full (187,805 tokens of a
|
|
1648
|
-
* 400,000 window, ratio 0.47) had its request judged over a 383,616-token budget,
|
|
1649
|
-
* reduced, judged over again, and finally ABORTED at stage 37 of 64.
|
|
1650
|
-
*
|
|
1651
|
-
* So the ratio is calibrated per session against ground truth: the size of a
|
|
1652
|
-
* projection this process handed the host, paired with the token count the provider
|
|
1653
|
-
* reported for it. Tiny early requests are ignored -- a 466-char first projection
|
|
1654
|
-
* measured against a system prompt gives a meaningless 0.46 -- and the result is
|
|
1655
|
-
* bounded, so a pathological pairing can neither disable the fence nor trip it.
|
|
1656
|
-
*/
|
|
1657
1302
|
const PROJECTION_CALIBRATION_MIN_CHARS = 20_000;
|
|
1658
1303
|
const PROJECTION_CALIBRATION_MIN_TOKENS = 5_000;
|
|
1659
1304
|
const PROJECTION_CHARS_PER_TOKEN_FLOOR = 2;
|
|
1660
1305
|
const PROJECTION_CHARS_PER_TOKEN_CEILING = 12;
|
|
1661
|
-
/** How many recent pairings the ratio, the error window and the inflow window keep. */
|
|
1662
1306
|
const PROJECTION_CALIBRATION_WINDOW = 6;
|
|
1663
1307
|
const PROJECTION_ERROR_WINDOW = 8;
|
|
1664
|
-
/** Margin floor, as a share of the window, before any error or inflow is measured. */
|
|
1665
1308
|
const PROJECTION_MARGIN_FLOOR_SHARE = 0.05;
|
|
1666
1309
|
|
|
1667
1310
|
const noteProjectionCalibration = (measurement: ProviderContextMeasurement): void => {
|
|
1668
1311
|
const previous = measurements.lastProviderMeasurement;
|
|
1669
1312
|
if (Number.isFinite(measurement.tokens) && previous && Number.isFinite(previous.tokens)) {
|
|
1670
1313
|
const step = measurement.tokens - previous.tokens;
|
|
1671
|
-
// Only GROWTH is an inflow step. A commit shrinks the window; that is the thing
|
|
1672
|
-
// the margin has to survive, not a size the next request can be predicted from.
|
|
1673
1314
|
if (step > 0) {
|
|
1674
1315
|
measurements.inflowSteps.push(step);
|
|
1675
1316
|
if (measurements.inflowSteps.length > PROJECTION_ERROR_WINDOW) measurements.inflowSteps.shift();
|
|
@@ -1678,11 +1319,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
1678
1319
|
const chars = measurements.lastProjectedChars;
|
|
1679
1320
|
if (chars === null || chars < PROJECTION_CALIBRATION_MIN_CHARS) return;
|
|
1680
1321
|
if (!Number.isFinite(measurement.tokens) || measurement.tokens < PROJECTION_CALIBRATION_MIN_TOKENS) return;
|
|
1681
|
-
// The signed error of the estimate we made for the projection this measurement
|
|
1682
|
-
// describes. This is the only direct measure of how wrong the fence can be.
|
|
1683
|
-
// Only a CALIBRATED estimate's error belongs in the window. The bootstrap constant
|
|
1684
|
-
// is wrong by design -- 76% high in rep12 -- and letting that one reading set the
|
|
1685
|
-
// margin would keep every later request permanently inside it.
|
|
1686
1322
|
if (measurements.lastProjectedEstimate !== null && measurements.lastProjectedEstimateCalibrated &&
|
|
1687
1323
|
measurement.tokens > 0) {
|
|
1688
1324
|
measurements.estimatorErrors.push(
|
|
@@ -1696,17 +1332,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
1696
1332
|
}
|
|
1697
1333
|
};
|
|
1698
1334
|
|
|
1699
|
-
/**
|
|
1700
|
-
* Recent, and pessimistic.
|
|
1701
|
-
*
|
|
1702
|
-
* A single latest pairing tracks drift but carries its noise; an average lags it.
|
|
1703
|
-
* Near the top of the window neither is acceptable, because the error that matters is
|
|
1704
|
-
* one-sided: a ratio that is too HIGH under-counts tokens and transmits a request the
|
|
1705
|
-
* provider will reject. So the fence takes the smallest ratio in the recent window,
|
|
1706
|
-
* which converges instantly in the dangerous direction and within a window in the
|
|
1707
|
-
* cheap one. Measured 2026-08-06 (rep13): the ratio moved 5.34 to 6.01 and back
|
|
1708
|
-
* across twenty requests while the window sat above 90% full.
|
|
1709
|
-
*/
|
|
1710
1335
|
const projectionCharsPerToken = (): number => {
|
|
1711
1336
|
const usable = measurements.projectionCalibrations.filter((entry) =>
|
|
1712
1337
|
entry.tokens > 0 && Number.isFinite(entry.chars / entry.tokens) && entry.chars / entry.tokens > 0);
|
|
@@ -1718,40 +1343,14 @@ export function registerActiveContext(pi: any, options: {
|
|
|
1718
1343
|
);
|
|
1719
1344
|
};
|
|
1720
1345
|
|
|
1721
|
-
/**
|
|
1722
|
-
* Bind the provider's count to the projection it counted.
|
|
1723
|
-
*
|
|
1724
|
-
* The anchor is the number the provider reported for a request this process actually
|
|
1725
|
-
* TRANSMITTED, held with that projection's serialized bytes. Identity is carried three
|
|
1726
|
-
* ways so a stale anchor cannot pass for a fresh one: the session it belongs to, the
|
|
1727
|
-
* attribution generation (a model or thinking-level change is a different tokenizer, so
|
|
1728
|
-
* its counts describe nothing here), and the projection text itself, which the reading
|
|
1729
|
-
* re-checks byte for byte before adding anything to it.
|
|
1730
|
-
*/
|
|
1731
1346
|
const noteProviderProjectionAnchor = (measurement: ProviderContextMeasurement): void => {
|
|
1732
|
-
// ONE ANCHOR PER MEASURED RESPONSE. A pass that re-observes the same assistant
|
|
1733
|
-
// message -- the retry after a provider rejection, or any pass the host runs without
|
|
1734
|
-
// a new response -- would otherwise re-bind that count to a newer projection the
|
|
1735
|
-
// provider never counted, and the count would silently describe the wrong bytes.
|
|
1736
1347
|
if (measurements.projectionAnchor?.messageSha256 === measurement.messageSha256) return;
|
|
1737
1348
|
const text = instrumentation.previousText;
|
|
1738
|
-
// Only a TRANSMITTED projection can be anchored: an aborted pass built a projection
|
|
1739
|
-
// the provider never saw, so no reported count describes it.
|
|
1740
1349
|
if (!text || text.length < 2 || !text.endsWith("]") || !persistence.state) {
|
|
1741
1350
|
measurements.projectionAnchor = null;
|
|
1742
1351
|
return;
|
|
1743
1352
|
}
|
|
1744
1353
|
const chars = bytes(text);
|
|
1745
|
-
// AND THE COUNT HAS TO FIT THE BYTES IT IS BOUND TO.
|
|
1746
|
-
//
|
|
1747
|
-
// An anchor is exact for the bytes it covers only if it actually describes them. A
|
|
1748
|
-
// pairing that implies fewer serialized chars per token than any tokenizer produces
|
|
1749
|
-
// is not describing this projection, and taken at face value it would put the fence
|
|
1750
|
-
// under permanent pressure a projection of that size cannot explain. The calibration
|
|
1751
|
-
// window already refuses such a ratio through the same floor; the anchor refuses it
|
|
1752
|
-
// outright and falls back to the estimate. Only the FLOOR applies: a rate ABOVE the
|
|
1753
|
-
// calibration ceiling is the signature-heavy composition this mechanism exists to
|
|
1754
|
-
// read, and clamping it would throw away the measurement that motivated the build.
|
|
1755
1354
|
if (!(measurement.tokens > 0) || chars / measurement.tokens < PROJECTION_CHARS_PER_TOKEN_FLOOR) {
|
|
1756
1355
|
measurements.projectionAnchor = null;
|
|
1757
1356
|
return;
|
|
@@ -1766,45 +1365,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
1766
1365
|
};
|
|
1767
1366
|
};
|
|
1768
1367
|
|
|
1769
|
-
/**
|
|
1770
|
-
* THE OCCUPANCY READING: what the provider counted, plus only what arrived after it.
|
|
1771
|
-
*
|
|
1772
|
-
* A whole-projection estimate divides every byte in the window by one rate, and one
|
|
1773
|
-
* rate does not fit the window's contents. Measured 2026-08-10 on the sealed rep-23
|
|
1774
|
-
* run, byte exact against that run's own projection records: the estimate is unbiased
|
|
1775
|
-
* at the pre-commit PEAKS (provider over estimate, mean 1.004 across six) and over-reads
|
|
1776
|
-
* the post-fold TROUGHS by 22 to 35 percent (mean ratio 0.784 across six). It is a phase
|
|
1777
|
-
* effect, not drift: encrypted reasoning-signature blobs on assistant messages grew from
|
|
1778
|
-
* 14.6 to 51.3 percent of projection bytes over that run and price at a fitted 24.8 chars
|
|
1779
|
-
* per token against 3.957 for ordinary content, so a projection's rate depends on what it
|
|
1780
|
-
* is made of. The runtime therefore believed its floor climbed 0.298 to 0.490 of budget
|
|
1781
|
-
* while the provider read 0.276 to 0.382: roughly 45 percent of the apparent floor rise
|
|
1782
|
-
* was measurement error, five of six true troughs sat at or below the 0.35 minTarget, and
|
|
1783
|
-
* the ladder was folding past its own floor without seeing it.
|
|
1784
|
-
*
|
|
1785
|
-
* So the rate is applied to the DELTA only. The provider's count for the projection this
|
|
1786
|
-
* one extends is exact for every byte it covers, whatever those bytes are made of, and
|
|
1787
|
-
* only the material appended since is estimated. Measured on the same run: mean absolute
|
|
1788
|
-
* occupancy error across all requests falls from 8.38 to 3.08 percent, and inside every
|
|
1789
|
-
* post-fold refill window it drops from the +9 to +27 percent band to under 4 percent from
|
|
1790
|
-
* the second request onward.
|
|
1791
|
-
*
|
|
1792
|
-
* Two things this deliberately does NOT do. It does not move the commit trigger: at all
|
|
1793
|
-
* twelve commit points of that run the delta since the last provider count was exactly
|
|
1794
|
-
* zero, because the commit hook runs after the response and its usage record have landed.
|
|
1795
|
-
* And it does not fix the one pass per cycle where the projection is REWRITTEN rather than
|
|
1796
|
-
* appended, right after a commit: the anchor describes a projection this one does not
|
|
1797
|
-
* begin with, so there is nothing to add a delta to and the whole-projection estimate
|
|
1798
|
-
* stands, error and all (+27 to +35 percent, measured). That case is detected by
|
|
1799
|
-
* construction, not by the sign of the delta: the current serialization must begin with
|
|
1800
|
-
* the anchored one verbatim, up to the array separator.
|
|
1801
|
-
*
|
|
1802
|
-
* The anchor is the provider's TOTAL for that request, which includes the output tokens
|
|
1803
|
-
* of the response the next projection appends and then prices again as bytes. That is one
|
|
1804
|
-
* response's worth of double count, it runs HIGH rather than low, and it keeps a single
|
|
1805
|
-
* definition of "the provider's number" across occupancy, the ratio and the calibration
|
|
1806
|
-
* instead of a second one that only this reading would use.
|
|
1807
|
-
*/
|
|
1808
1368
|
const projectedTokenReading = (projected: unknown[]): {
|
|
1809
1369
|
tokens: number;
|
|
1810
1370
|
basis: ProjectionReadingBasis;
|
|
@@ -1821,10 +1381,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
1821
1381
|
anchor.sessionId !== persistence.state?.sessionId) {
|
|
1822
1382
|
return { tokens: estimate, basis: "unmeasured", chars, anchorTokens: null, deltaChars: null };
|
|
1823
1383
|
}
|
|
1824
|
-
// By construction: the anchored serialization minus its closing bracket must be a
|
|
1825
|
-
// byte-exact prefix, and the byte after it must be the array separator or the close.
|
|
1826
|
-
// A message that merely GREW in place fails this, because the anchored head ends with
|
|
1827
|
-
// that message's own closing brace.
|
|
1828
1384
|
const separator = text.length > anchor.head.length ? text[anchor.head.length] : "";
|
|
1829
1385
|
if (!text.startsWith(anchor.head) || (separator !== "," && separator !== "]")) {
|
|
1830
1386
|
return { tokens: estimate, basis: "rewritten", chars, anchorTokens: anchor.tokens, deltaChars: null };
|
|
@@ -1839,39 +1395,19 @@ export function registerActiveContext(pi: any, options: {
|
|
|
1839
1395
|
};
|
|
1840
1396
|
};
|
|
1841
1397
|
|
|
1842
|
-
/** The largest recent estimator error, as a share. Unmeasured sessions assume none. */
|
|
1843
1398
|
const estimatorErrorShare = (): number => measurements.estimatorErrors.length
|
|
1844
1399
|
? Math.max(...measurements.estimatorErrors.map((error) => Math.abs(error)))
|
|
1845
1400
|
: 0;
|
|
1846
1401
|
|
|
1847
|
-
/** The largest recent growth step, which is what one more turn can add. */
|
|
1848
1402
|
const expectedInflowTokens = (): number => measurements.inflowSteps.length
|
|
1849
1403
|
? Math.max(...measurements.inflowSteps)
|
|
1850
1404
|
: 0;
|
|
1851
1405
|
|
|
1852
|
-
/**
|
|
1853
|
-
* THE OUTPUT WALL'S OWN FORWARD LOOK, AND WHY IT IS NOT THE ONE ABOVE.
|
|
1854
|
-
*
|
|
1855
|
-
* `inflowSteps` is filled by `noteProjectionCalibration`, which runs on the context pass.
|
|
1856
|
-
* By then the message-end path has already advanced `lastProviderMeasurement` to the
|
|
1857
|
-
* count being calibrated, so the difference is a measurement against itself and the step
|
|
1858
|
-
* is zero. That window has therefore been empty in every session ever run, which means
|
|
1859
|
-
* the transmission fence margin and the commit depth floor, both of which read it, have
|
|
1860
|
-
* been running on their floor shares alone. Reviving it moves both, and both are
|
|
1861
|
-
* calibrated against reps recorded while it was empty, so that is its own build with its
|
|
1862
|
-
* own gates rather than a rider on this one.
|
|
1863
|
-
*
|
|
1864
|
-
* This window is filled where the previous count is still readable, and nothing but the
|
|
1865
|
-
* wall reads it. When a count reaches only the context pass the window stays empty and
|
|
1866
|
-
* the wall degrades to the condition it replaced, which is the safe direction.
|
|
1867
|
-
*/
|
|
1868
1406
|
const WALL_INFLOW_WINDOW = 8;
|
|
1869
1407
|
const noteWallInflow = (measurement: ProviderContextMeasurement): void => {
|
|
1870
1408
|
const previous = measurements.lastProviderMeasurement;
|
|
1871
1409
|
if (!previous || !Number.isFinite(measurement.tokens) || !Number.isFinite(previous.tokens)) return;
|
|
1872
1410
|
const step = measurement.tokens - previous.tokens;
|
|
1873
|
-
// Only GROWTH. A commit shrinks the window; that is what the wall has to survive, not
|
|
1874
|
-
// a size the next request can be predicted from.
|
|
1875
1411
|
if (step <= 0) return;
|
|
1876
1412
|
measurements.wallInflowSteps.push(step);
|
|
1877
1413
|
if (measurements.wallInflowSteps.length > WALL_INFLOW_WINDOW) measurements.wallInflowSteps.shift();
|
|
@@ -1881,64 +1417,18 @@ export function registerActiveContext(pi: any, options: {
|
|
|
1881
1417
|
? Math.max(...measurements.wallInflowSteps)
|
|
1882
1418
|
: 0;
|
|
1883
1419
|
|
|
1884
|
-
/**
|
|
1885
|
-
* The safety margin.
|
|
1886
|
-
*
|
|
1887
|
-
* A fence that fires AT the budget is a fence that fires after the wire: the request
|
|
1888
|
-
* that kills the session is the one built after the last measurement, and it is never
|
|
1889
|
-
* the one being weighed. Measured 2026-08-06 (rep13): the last measurement read
|
|
1890
|
-
* 370,320 tokens against a 383,616 budget -- 13,296 of headroom against a median
|
|
1891
|
-
* inflow of 13,865 and a maximum of 27,815 -- while the estimate for that request was
|
|
1892
|
-
* 366,934, comfortably "under budget". The next request crossed the real limit and
|
|
1893
|
-
* the provider rejected it. Every number in that sentence was known to the runtime.
|
|
1894
|
-
*
|
|
1895
|
-
* So the reduction threshold carries what the runtime knows it does not know: the
|
|
1896
|
-
* worst recent estimator error applied to this estimate, plus one worst recent inflow
|
|
1897
|
-
* step, never less than a floor share of the window.
|
|
1898
|
-
*/
|
|
1899
1420
|
const projectionMarginTokens = (estimate: number, windowTokens: number): number => Math.ceil(Math.max(
|
|
1900
1421
|
PROJECTION_MARGIN_FLOOR_SHARE * windowTokens,
|
|
1901
1422
|
estimatorErrorShare() * estimate + expectedInflowTokens(),
|
|
1902
1423
|
));
|
|
1903
1424
|
|
|
1904
|
-
/**
|
|
1905
|
-
* The TRANSMISSION fence.
|
|
1906
|
-
*
|
|
1907
|
-
* `abortUnsafeHardContext` gates on `measurements.latestRatio`, which describes the
|
|
1908
|
-
* request the provider ALREADY answered. It is a lagging indicator: between that
|
|
1909
|
-
* response and the next request an excursion can add a hundred thousand tokens of
|
|
1910
|
-
* tool results, and nothing in the fence looks at the projection actually about to
|
|
1911
|
-
* be sent. Measured 2026-08-06 (rep 11): the last measurement read 359,625 tokens of
|
|
1912
|
-
* a 400,000 window, ratio 0.937 against a 0.959 hard fence, so no abort fired -- and
|
|
1913
|
-
* the projection that went out was 1,831,936 chars, about 458k estimated tokens, 1.2x
|
|
1914
|
-
* the whole window. The provider rejected it twice and the run died. rep4 aborted
|
|
1915
|
-
* correctly only because its 272k DESCRIPTOR window put the stale ratio over the
|
|
1916
|
-
* fence by luck of arithmetic, not because anything measured the request.
|
|
1917
|
-
*
|
|
1918
|
-
* So the projection itself is measured here, against the truthful serving budget the
|
|
1919
|
-
* capacity accounting already computes. Over budget, an emergency reduction runs at
|
|
1920
|
-
* fence pressure -- where every guarded mark is waived -- and the rebuilt projection
|
|
1921
|
-
* is measured again. If it still does not fit, the request is aborted BEFORE
|
|
1922
|
-
* transmission, which is the whole point of having a fence.
|
|
1923
|
-
*/
|
|
1924
1425
|
const projectionExceedsBudget = (projected: unknown[], ctx: any): {
|
|
1925
|
-
/** Occupancy: what the provider counted, plus what was appended after it. */
|
|
1926
1426
|
tokens: number;
|
|
1927
|
-
/**
|
|
1928
|
-
* This projection's OWN size, every byte of it at one rate. Occupancy answers "how
|
|
1929
|
-
* full is the window"; a before-and-after comparison between two projections answers
|
|
1930
|
-
* "did this pass make the request smaller", and those are different questions. A
|
|
1931
|
-
* reduction is measured in size space on both sides, because the projection the pass
|
|
1932
|
-
* rebuilds is a rewrite the anchor does not describe, and subtracting an anchored
|
|
1933
|
-
* reading from an unanchored one reports a change that no folding caused.
|
|
1934
|
-
*/
|
|
1935
1427
|
sizeTokens: number;
|
|
1936
1428
|
basis: ProjectionReadingBasis;
|
|
1937
1429
|
budgetTokens: number;
|
|
1938
1430
|
marginTokens: number;
|
|
1939
|
-
/** Past the wire: this request must not be transmitted at all. */
|
|
1940
1431
|
over: boolean;
|
|
1941
|
-
/** Inside the margin: still sendable, but the next one may not be. Reduce NOW. */
|
|
1942
1432
|
crowded: boolean;
|
|
1943
1433
|
} => {
|
|
1944
1434
|
const capacity = currentCapacity(ctx);
|
|
@@ -1997,17 +1487,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
1997
1487
|
ctx.abort();
|
|
1998
1488
|
};
|
|
1999
1489
|
|
|
2000
|
-
/**
|
|
2001
|
-
* Enforce the budget, and RECOVER rather than die.
|
|
2002
|
-
*
|
|
2003
|
-
* A provider context-overflow rejection mutates nothing durable: the assistant
|
|
2004
|
-
* message never lands and this runtime rebuilds the projection from the branch on
|
|
2005
|
-
* every request. So the terminal path is not an abort, it is a rollback: do not
|
|
2006
|
-
* append the failed exchange, fold at fence pressure until the request fits, rebuild,
|
|
2007
|
-
* and let Pi resend. Recovery is capped, and the cap is what makes it safe -- an
|
|
2008
|
-
* inflow that still will not fit after maximal folding is a genuine impossibility,
|
|
2009
|
-
* and that one fails LOUDLY exactly as before.
|
|
2010
|
-
*/
|
|
2011
1490
|
const enforceProjectionBudget = async (
|
|
2012
1491
|
snapshot: ActiveContextSnapshot,
|
|
2013
1492
|
projected: unknown[],
|
|
@@ -2015,44 +1494,28 @@ export function registerActiveContext(pi: any, options: {
|
|
|
2015
1494
|
): Promise<{ projected: unknown[]; aborted: boolean }> => {
|
|
2016
1495
|
let measured = projectionExceedsBudget(projected, ctx);
|
|
2017
1496
|
const rejected = curation.pendingRejection !== null;
|
|
2018
|
-
// CROWDED, not over, is the trigger for reducing: the request that kills a session
|
|
2019
|
-
// is the one built after this one. A provider rejection outranks our own estimate:
|
|
2020
|
-
// it is ground truth that the last request did not fit, whatever we measured.
|
|
2021
1497
|
if (!measured.crowded && !rejected) return { projected, aborted: false };
|
|
2022
|
-
// Crowded is a MARGIN PREDICTION, not an overflow. While a last-call round is
|
|
2023
|
-
// genuinely OPEN, exposure rendered this pass or the agent's response still
|
|
2024
|
-
// outstanding, the prediction waits for it: this request still fits and the
|
|
2025
|
-
// round ends on the very next context pass. Once the round has elapsed the
|
|
2026
|
-
// margin lane acts freely, because a stale exposure parked behind the reopen
|
|
2027
|
-
// latch must never muzzle the one reducer that can still act (the 20k-window
|
|
2028
|
-
// probe climbed from crowded to the wire in exactly that state). Genuine
|
|
2029
|
-
// untransmissibility, an over-budget projection or a provider rejection, never
|
|
2030
|
-
// waits: a request whose projection exceeds the provider input budget is rejected
|
|
2031
|
-
// outright, so recovery must produce a window that fits. Economy does not outrank
|
|
2032
|
-
// the one round.
|
|
2033
|
-
const lastCall = persistence.state?.lastCall;
|
|
2034
|
-
const delivery = curation.lastCallDelivery;
|
|
2035
|
-
const roundOpen = Boolean(lastCall) && (!delivery || delivery.exposure !== lastCall.exposure ||
|
|
2036
|
-
markOrdinal(snapshot) <= delivery.ordinal);
|
|
2037
|
-
if (!measured.over && !rejected && roundOpen) {
|
|
2038
|
-
return { projected, aborted: false };
|
|
2039
|
-
}
|
|
2040
|
-
// Why this fired, recorded from the measurement that TRIGGERED it rather than the
|
|
2041
|
-
// one taken afterwards, which by then describes a projection that fits.
|
|
2042
1498
|
const trigger = measured;
|
|
2043
1499
|
let reduced = projected;
|
|
2044
1500
|
let attempts = 0;
|
|
2045
1501
|
let reducedAtLeastOnce = false;
|
|
2046
|
-
|
|
2047
|
-
// transmissible, so every reduction runs with every guarded mark waived.
|
|
2048
|
-
while (attempts < OVERFLOW_RECOVERY_MAX_ATTEMPTS && (measured.crowded || measured.over || rejected)) {
|
|
1502
|
+
while (measured.crowded || measured.over || rejected) {
|
|
2049
1503
|
attempts += 1;
|
|
2050
|
-
|
|
1504
|
+
// THE EMERGENCY IS THE REQUEST THAT WILL NOT FIT, and only that. A projection at
|
|
1505
|
+
// the margin is ordinary work: the fence commits it because it is the pass holding
|
|
1506
|
+
// the request, but it is not a recovery, it does not waive the current-turn guard,
|
|
1507
|
+
// and it does not buy an exemption from the reclaim floor. Waiving at the margin
|
|
1508
|
+
// spends the open turn's own evidence on headroom the boundary was about to buy.
|
|
1509
|
+
const emergency = measured.over || rejected;
|
|
1510
|
+
if (emergency) curation.recoveryAttempts += 1;
|
|
2051
1511
|
let action: Record<string, unknown> | null = null;
|
|
2052
1512
|
try {
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
1513
|
+
// The fence COMMITS. A mark moves no bytes, and the request it is holding is the
|
|
1514
|
+
// one that will not fit, so marking here would spin the loop against an unchanged
|
|
1515
|
+
// projection until it ran out of things to mark.
|
|
1516
|
+
action = await attemptAutomaticCommit(
|
|
1517
|
+
snapshot, ctx, "projection-budget", emergency ? 1 : measurements.latestRatio,
|
|
1518
|
+
);
|
|
2056
1519
|
} catch (error) {
|
|
2057
1520
|
suspendAutomatic(error, "projection-budget", ctx);
|
|
2058
1521
|
break;
|
|
@@ -2065,7 +1528,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
2065
1528
|
}
|
|
2066
1529
|
if (reducedAtLeastOnce) {
|
|
2067
1530
|
ladder.overBudgetReduction = {
|
|
2068
|
-
// Size space on both sides: what this pass CHANGED, not how full the window is.
|
|
2069
1531
|
estimatedTokensBefore: trigger.sizeTokens,
|
|
2070
1532
|
estimatedTokensAfter: measured.sizeTokens,
|
|
2071
1533
|
occupancyTokensAfter: measured.tokens,
|
|
@@ -2083,34 +1545,10 @@ export function registerActiveContext(pi: any, options: {
|
|
|
2083
1545
|
}
|
|
2084
1546
|
if (rejected) {
|
|
2085
1547
|
const overflowBefore = trigger.sizeTokens;
|
|
2086
|
-
// RECOVERY IS A CHANGE, NOT AN OPINION.
|
|
2087
|
-
//
|
|
2088
|
-
// This read `!measured.over`: our own estimator answering the question the
|
|
2089
|
-
// provider had just answered differently. A rejection is ground truth that the
|
|
2090
|
-
// estimate was wrong, so the estimate cannot also be the evidence that the retry
|
|
2091
|
-
// will work. And a rejection mutates nothing durable -- the rolled-back branch
|
|
2092
|
-
// rebuilds the same projection -- so a pass that changed nothing hands the
|
|
2093
|
-
// provider a byte-identical request and calls it a rescue. Measured 2026-08-10
|
|
2094
|
-
// (luna-20260810 pifold rep 2): two rejections at stage 25, not one byte folded
|
|
2095
|
-
// either time, both recorded recovered true, and the run died anyway.
|
|
2096
|
-
//
|
|
2097
|
-
// The baseline is the request that was actually REJECTED, which is the last
|
|
2098
|
-
// projection this process built: it is written at the end of every pass that
|
|
2099
|
-
// builds one, and this lane runs before the current pass overwrites it. Measuring
|
|
2100
|
-
// against the projection handed to this function instead would credit only the
|
|
2101
|
-
// recovery loop's own folds and miss the ordinary commit that already ran earlier
|
|
2102
|
-
// in the same pass, which is the usual way a retried request gets smaller.
|
|
2103
|
-
//
|
|
2104
|
-
// Size space on both sides. Occupancy is anchored to the provider's count for the
|
|
2105
|
-
// projection it measured, and the projection this lane rebuilds is a rewrite that
|
|
2106
|
-
// anchor does not describe, so a rejected-minus-retried subtraction across the two
|
|
2107
|
-
// bases reports a change no folding caused.
|
|
2108
1548
|
const rejectedTokens = measurements.lastProjectedSizeTokens;
|
|
2109
1549
|
const freedTokens = typeof rejectedTokens === "number"
|
|
2110
1550
|
? Math.max(0, rejectedTokens - measured.sizeTokens)
|
|
2111
1551
|
: 0;
|
|
2112
|
-
// Smaller than what the provider refused, AND inside the budget. Either half alone
|
|
2113
|
-
// is a claim this runtime has already been wrong about once.
|
|
2114
1552
|
const recovered = freedTokens > 0 && !measured.over;
|
|
2115
1553
|
curation.lastRecovery = {
|
|
2116
1554
|
status: curation.pendingRejection?.status ?? null,
|
|
@@ -2122,7 +1560,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
2122
1560
|
emit("context.recovery", {
|
|
2123
1561
|
provider_status: curation.pendingRejection?.status ?? null,
|
|
2124
1562
|
attempts,
|
|
2125
|
-
max_attempts: OVERFLOW_RECOVERY_MAX_ATTEMPTS,
|
|
2126
1563
|
tokens_before: overflowBefore,
|
|
2127
1564
|
tokens_after: measured.sizeTokens,
|
|
2128
1565
|
occupancy_tokens_after: measured.tokens,
|
|
@@ -2130,15 +1567,9 @@ export function registerActiveContext(pi: any, options: {
|
|
|
2130
1567
|
budget_tokens: measured.budgetTokens,
|
|
2131
1568
|
margin_tokens: measured.marginTokens,
|
|
2132
1569
|
recovered,
|
|
2133
|
-
// What the verdict rests on, so a false claim cannot be read back as a true one:
|
|
2134
|
-
// the size of the request the provider refused, and how much smaller this one is.
|
|
2135
1570
|
rejected_tokens: typeof rejectedTokens === "number" ? rejectedTokens : null,
|
|
2136
1571
|
freed_tokens: freedTokens,
|
|
2137
|
-
// Whether the recovery loop itself folded, as distinct from the pass as a whole.
|
|
2138
1572
|
loop_reduced: reducedAtLeastOnce,
|
|
2139
|
-
// The join. `context.rollback` records what left the branch; this records what
|
|
2140
|
-
// the retried pass folded to make the shorter window fit, and the two are one
|
|
2141
|
-
// episode. Null when a rejection was recorded without a tree rollback.
|
|
2142
1573
|
rollback_seq: typeof rollback.last?.seq === "number" ? rollback.last.seq : null,
|
|
2143
1574
|
});
|
|
2144
1575
|
const unchangedNote = "The provider rejected the last request and this pass made it no smaller, so the " +
|
|
@@ -2173,21 +1604,14 @@ export function registerActiveContext(pi: any, options: {
|
|
|
2173
1604
|
|
|
2174
1605
|
const startPreparation = (snapshot: ActiveContextSnapshot, ratio: number | null, ctx: any): void => {
|
|
2175
1606
|
if (lifecycle.shuttingDown || !persistence.state || ladder.automaticFailure || ratio === null || persistence.state.prepared || ladder.preparing ||
|
|
2176
|
-
ratio < snapshot.policy.
|
|
1607
|
+
ratio < snapshot.policy.prepareRatio ||
|
|
2177
1608
|
!measurements.lastProviderMeasurement || !durableProviderMeasurementMatches(measurements.lastProviderMeasurement)) return;
|
|
2178
|
-
|
|
2179
|
-
// committable deterministic fold on the same measured projection.
|
|
2180
|
-
const selection = selectAutomaticRung(snapshot, persistence.state, ratio, {
|
|
2181
|
-
summarizerAvailable: Boolean(options.summarizeContextSpan),
|
|
2182
|
-
failedPreparationIds: ladder.failedPreparations,
|
|
2183
|
-
});
|
|
1609
|
+
const selection = selectAutomaticRung(snapshot, persistence.state, ratio);
|
|
2184
1610
|
ladder.lastSelectionKind = selection && "candidate" in selection ? selection.candidate.kind : null;
|
|
2185
1611
|
ladder.lastSelectionSourceIds = selection && "candidate" in selection
|
|
2186
1612
|
? selection.candidate.sourceRefs.slice(0, 8).map((ref) => ref.entryId)
|
|
2187
1613
|
: [];
|
|
2188
1614
|
if (selection?.kind !== "chapter-prepare") return;
|
|
2189
|
-
// Already bite-sized: the chapter selector never proposes a span past the cap, so a
|
|
2190
|
-
// model brief is never spent on a fold nobody can read back cheaply.
|
|
2191
1615
|
const candidate = selection.candidate;
|
|
2192
1616
|
const id = automaticPreparationId(candidate, persistence.state);
|
|
2193
1617
|
const controller = new AbortController();
|
|
@@ -2202,11 +1626,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
2202
1626
|
snapshot,
|
|
2203
1627
|
state: capturedState,
|
|
2204
1628
|
generation: capturedGeneration,
|
|
2205
|
-
summarize: ladder.failedPreparations.has(id) ? undefined : observedSummarize,
|
|
2206
|
-
onSummarizerFailure: (error) => {
|
|
2207
|
-
ladder.lastPreparationError = error instanceof Error ? error.message : String(error);
|
|
2208
|
-
ladder.failedPreparations.add(id);
|
|
2209
|
-
},
|
|
2210
1629
|
ctx,
|
|
2211
1630
|
signal: controller.signal,
|
|
2212
1631
|
}).then((preparedFold) => {
|
|
@@ -2225,7 +1644,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
2225
1644
|
if (controller.signal.aborted ||
|
|
2226
1645
|
!sessionIdentityStillValid(ctx, snapshot.sessionId, capturedGeneration)) return;
|
|
2227
1646
|
ladder.lastPreparationError = error instanceof Error ? error.message : String(error);
|
|
2228
|
-
ladder.failedPreparations.add(id);
|
|
2229
1647
|
suspendAutomatic(
|
|
2230
1648
|
error,
|
|
2231
1649
|
"chapter-prepare",
|
|
@@ -2238,18 +1656,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
2238
1656
|
if (ownsSlot && sessionIdentityStillValid(ctx, snapshot.sessionId, capturedGeneration)) updateStatus(ctx);
|
|
2239
1657
|
});
|
|
2240
1658
|
};
|
|
2241
|
-
// Appended at the very TAIL, after the stable prefix, so a suggestion never
|
|
2242
|
-
// invalidates a cached prefix and never becomes durable transcript.
|
|
2243
|
-
/**
|
|
2244
|
-
* The receipt block: what the runtime did to this window, as status rather than as
|
|
2245
|
-
* advice. It is the only carrier this runtime builds.
|
|
2246
|
-
*
|
|
2247
|
-
* It is free because it is RETROSPECTIVE. A receipt exists only after a commit has
|
|
2248
|
-
* already rewritten the projection, so it rides a cache break the runtime had to pay
|
|
2249
|
-
* for regardless, and the freeze then closes over it so it is never paid for again.
|
|
2250
|
-
* It is hard-bounded and its ring evicts the oldest entry, because a report about
|
|
2251
|
-
* bloat that becomes bloat has argued against itself.
|
|
2252
|
-
*/
|
|
2253
1659
|
const appendReceipts = (projected: unknown[], snapshot: ActiveContextSnapshot): unknown[] => {
|
|
2254
1660
|
const content = receiptBlockText({ receipts: curation.receipts, toolName, brandNoun });
|
|
2255
1661
|
if (!content) return projected;
|
|
@@ -2272,12 +1678,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
2272
1678
|
return projected;
|
|
2273
1679
|
};
|
|
2274
1680
|
|
|
2275
|
-
/**
|
|
2276
|
-
* The rider rides the SAME admission as the receipt block: it renders from the
|
|
2277
|
-
* persisted literal bytes, lands once per freeze cycle, and the freeze closes over
|
|
2278
|
-
* it, so it can never diverge a prefix on its own. It is never regenerated: the
|
|
2279
|
-
* bytes the epoch persisted are the bytes every re-render carries.
|
|
2280
|
-
*/
|
|
2281
1681
|
const appendRider = (projected: unknown[], snapshot: ActiveContextSnapshot): unknown[] => {
|
|
2282
1682
|
const rider = persistence.state?.rider;
|
|
2283
1683
|
if (!rider) return projected;
|
|
@@ -2299,92 +1699,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
2299
1699
|
return projected;
|
|
2300
1700
|
};
|
|
2301
1701
|
|
|
2302
|
-
/**
|
|
2303
|
-
* Threshold notices are APPEND-ONCE: each delivered notice is its own carrier with
|
|
2304
|
-
* its own key, so a notice that fires mid-freeze lands at the tail as a pure append,
|
|
2305
|
-
* the freeze closes over it, and it then persists in the window the way a tool
|
|
2306
|
-
* result does. A freeze break re-renders every retained notice from its persisted
|
|
2307
|
-
* literal bytes inside the rewrite that broke the freeze.
|
|
2308
|
-
*/
|
|
2309
|
-
const appendNotices = (projected: unknown[], snapshot: ActiveContextSnapshot): unknown[] => {
|
|
2310
|
-
const notices = persistence.state?.notices;
|
|
2311
|
-
if (!notices?.ring.length) return projected;
|
|
2312
|
-
for (const notice of notices.ring) {
|
|
2313
|
-
if (!carrierAdmitted(`notice-${notice.share}-${notice.ordinal}`)) continue;
|
|
2314
|
-
projected.push({
|
|
2315
|
-
role: "custom",
|
|
2316
|
-
customType: noticeProjectionType,
|
|
2317
|
-
content: notice.text,
|
|
2318
|
-
display: false,
|
|
2319
|
-
details: {
|
|
2320
|
-
source: activeContextSource(entryTypePrefix),
|
|
2321
|
-
ephemeral: true,
|
|
2322
|
-
share: notice.share,
|
|
2323
|
-
noticeOrdinal: notice.ordinal,
|
|
2324
|
-
},
|
|
2325
|
-
timestamp: typeof ownValue(snapshot.messages.at(-1), "timestamp") === "number"
|
|
2326
|
-
? ownValue(snapshot.messages.at(-1), "timestamp")
|
|
2327
|
-
: 0,
|
|
2328
|
-
});
|
|
2329
|
-
}
|
|
2330
|
-
return projected;
|
|
2331
|
-
};
|
|
2332
|
-
|
|
2333
|
-
/**
|
|
2334
|
-
* The pre-commit last-call rides the commit BOUNDARY: it is armed when the band-top
|
|
2335
|
-
* trigger fires, appended at the tail (a pure append) for exactly one gated round,
|
|
2336
|
-
* and the commit that consumes it clears the state, so the carrier's disappearance
|
|
2337
|
-
* rides the rewrite that commit already pays for. Rendering records the delivery,
|
|
2338
|
-
* which is what the one-round clock is measured from.
|
|
2339
|
-
*/
|
|
2340
|
-
const appendLastCall = (projected: unknown[], snapshot: ActiveContextSnapshot): unknown[] => {
|
|
2341
|
-
const lastCall = persistence.state?.lastCall;
|
|
2342
|
-
if (!lastCall) return projected;
|
|
2343
|
-
if (carrierAdmitted(`lastcall-${lastCall.exposure}`)) {
|
|
2344
|
-
projected.push({
|
|
2345
|
-
role: "custom",
|
|
2346
|
-
customType: lastCallProjectionType,
|
|
2347
|
-
content: lastCall.text,
|
|
2348
|
-
display: false,
|
|
2349
|
-
details: {
|
|
2350
|
-
source: activeContextSource(entryTypePrefix),
|
|
2351
|
-
ephemeral: true,
|
|
2352
|
-
exposure: lastCall.exposure,
|
|
2353
|
-
},
|
|
2354
|
-
timestamp: typeof ownValue(snapshot.messages.at(-1), "timestamp") === "number"
|
|
2355
|
-
? ownValue(snapshot.messages.at(-1), "timestamp")
|
|
2356
|
-
: 0,
|
|
2357
|
-
});
|
|
2358
|
-
}
|
|
2359
|
-
if (curation.lastCallDelivery?.exposure !== lastCall.exposure) {
|
|
2360
|
-
curation.lastCallDelivery = { exposure: lastCall.exposure, ordinal: markOrdinal(snapshot) };
|
|
2361
|
-
}
|
|
2362
|
-
return projected;
|
|
2363
|
-
};
|
|
2364
|
-
|
|
2365
|
-
/**
|
|
2366
|
-
* Whatever this pass ended up sending becomes the frozen surface for the next one, in
|
|
2367
|
-
* full. Nothing rides after the frozen region.
|
|
2368
|
-
*
|
|
2369
|
-
* The earlier build kept a live suggestion slate outside the freeze, reasoning that
|
|
2370
|
-
* advice goes stale and that a re-rendered block "only diverges the prefix when its
|
|
2371
|
-
* own content changes". That reasoning is wrong, and rep 21 measured the cost. A
|
|
2372
|
-
* positional cache compares bytes from the start: this pass sends [frozen][slate] and
|
|
2373
|
-
* the next sends [frozen][new messages][slate], so at the offset where the slate sat
|
|
2374
|
-
* the next pass has new messages instead. The slate diverges the prefix EVERY pass
|
|
2375
|
-
* whatever it says, because appending anything displaces it. There is no stable
|
|
2376
|
-
* position at a moving tail.
|
|
2377
|
-
*
|
|
2378
|
-
* Measured on rep 21: a 1503-character slate against a 1.5M-character prompt, 99.86%
|
|
2379
|
-
* of the projection preserved by our own accounting, and 16 full cache rebuilds
|
|
2380
|
-
* costing 3.71M tokens -- 21.9% of every input token in the run. Prefix caching is a
|
|
2381
|
-
* step function, not a gradient: mutation cost has no relation to mutation size.
|
|
2382
|
-
*
|
|
2383
|
-
* So the rule this build enforces: the runtime adds to the window only at a moment it
|
|
2384
|
-
* is already rewriting the window, which means at a commit. A carrier landed there is
|
|
2385
|
-
* free, because the break is already paid for, and it stays landed forever because
|
|
2386
|
-
* the freeze closes over it.
|
|
2387
|
-
*/
|
|
2388
1702
|
const holdFrozen = (projected: unknown[]): unknown[] => {
|
|
2389
1703
|
freeze.projection = [...projected];
|
|
2390
1704
|
freeze.active = false;
|
|
@@ -2395,13 +1709,9 @@ export function registerActiveContext(pi: any, options: {
|
|
|
2395
1709
|
const body = projectActiveContext(snapshot, persistence.state!).filter((message) => {
|
|
2396
1710
|
const customType = ownValue(message, "customType");
|
|
2397
1711
|
return customType !== milestoneProjectionType && customType !== advisoryProjectionType &&
|
|
2398
|
-
customType !==
|
|
2399
|
-
customType !== riderProjectionType && customType !== curationProjectionType
|
|
2400
|
-
customType !== lastCallProjectionType && customType !== noticeProjectionType;
|
|
1712
|
+
customType !== receiptProjectionType &&
|
|
1713
|
+
customType !== riderProjectionType && customType !== curationProjectionType;
|
|
2401
1714
|
});
|
|
2402
|
-
// The freeze test, stated as bytes rather than as intent: if the rebuilt body still
|
|
2403
|
-
// OPENS with the frozen body verbatim, nothing structural moved and the previous
|
|
2404
|
-
// projection is reused whole, with only what arrived since appended after it.
|
|
2405
1715
|
const held = freeze.body?.length ?? 0;
|
|
2406
1716
|
freeze.active = freeze.projection !== null && body.length >= held &&
|
|
2407
1717
|
stableStringify(body.slice(0, held)) === freeze.bodyText;
|
|
@@ -2411,28 +1721,10 @@ export function registerActiveContext(pi: any, options: {
|
|
|
2411
1721
|
if (!freeze.active) freeze.keys.clear();
|
|
2412
1722
|
freeze.body = body;
|
|
2413
1723
|
freeze.bodyText = stableStringify(body);
|
|
2414
|
-
// The one carrier left, and the only one that can ever be free: a statement of what
|
|
2415
|
-
// the runtime just did. It is built only when a commit produced a receipt, it lands
|
|
2416
|
-
// inside the freeze, and it is never rendered twice.
|
|
2417
|
-
//
|
|
2418
|
-
// Everything that used to stand here was ANTICIPATORY -- pressure milestones, the
|
|
2419
|
-
// live advisory, curation reminders, the last-call notice. Anticipatory guidance has
|
|
2420
|
-
// to arrive BEFORE the event it warns about, so it can never ride a break the
|
|
2421
|
-
// runtime was already paying for; it has to create one. Anticipatory guidance and an
|
|
2422
|
-
// append-only projection are mutually exclusive, and eleven runs say the warning
|
|
2423
|
-
// bought nothing anyway: voluntary fold share was 0.00, and the three runs built
|
|
2424
|
-
// specifically to invite curation produced 0, 1 and 0 voluntary folds.
|
|
2425
1724
|
appendReceipts(projected, snapshot);
|
|
2426
1725
|
appendRider(projected, snapshot);
|
|
2427
|
-
appendNotices(projected, snapshot);
|
|
2428
|
-
appendLastCall(projected, snapshot);
|
|
2429
1726
|
return holdFrozen(projected);
|
|
2430
1727
|
};
|
|
2431
|
-
/**
|
|
2432
|
-
* Classify the projection we are about to send against the one before it. A rewrite
|
|
2433
|
-
* is ours; a pure append that the provider still re-reads is not, and conflating the
|
|
2434
|
-
* two is what made a scheduling lever look responsible for 12 misses it never caused.
|
|
2435
|
-
*/
|
|
2436
1728
|
const noteProjection = (projected: unknown[]): void => {
|
|
2437
1729
|
const digests = messageDigests(projected);
|
|
2438
1730
|
const comparison = compareProjections(instrumentation.previousDigests, digests);
|
|
@@ -2442,17 +1734,12 @@ export function registerActiveContext(pi: any, options: {
|
|
|
2442
1734
|
const text = stableStringify(projected);
|
|
2443
1735
|
const divergence = prefixDivergence(instrumentation.previousText, text);
|
|
2444
1736
|
const previousChars = instrumentation.previousText?.length ?? 0;
|
|
2445
|
-
// How much of the PREVIOUS prompt this projection still opens with. identicalShare
|
|
2446
|
-
// measures the new projection instead, which grows every pass and would call a
|
|
2447
|
-
// total rewrite "preserved" as soon as enough fresh content landed after it.
|
|
2448
1737
|
instrumentation.lastPreservedShare = previousChars > 0
|
|
2449
1738
|
? divergence.identicalChars / previousChars
|
|
2450
1739
|
: null;
|
|
2451
1740
|
const charsPerToken = projectionCharsPerToken();
|
|
2452
1741
|
const causes = instrumentation.sinceHandoff.filter((event) =>
|
|
2453
1742
|
PREFIX_MUTATING_KINDS.has(event.kind));
|
|
2454
|
-
// The reading this projection was judged by, with the pieces it was built from, so
|
|
2455
|
-
// the anchored series is readable from the stream instead of reconstructed.
|
|
2456
1743
|
const reading = projectedTokenReading(projected);
|
|
2457
1744
|
emit("context.projection", {
|
|
2458
1745
|
change: comparison.change,
|
|
@@ -2466,10 +1753,10 @@ export function registerActiveContext(pi: any, options: {
|
|
|
2466
1753
|
anchor_tokens: reading.anchorTokens,
|
|
2467
1754
|
delta_chars: reading.deltaChars,
|
|
2468
1755
|
chars_per_token: charsPerToken,
|
|
1756
|
+
derivations: instrumentation.derivations,
|
|
1757
|
+
derive_ms: Math.round(instrumentation.deriveMs),
|
|
1758
|
+
derivation_hits: instrumentation.derivationHits,
|
|
2469
1759
|
});
|
|
2470
|
-
// Emission only. Which of these is a provider-side miss and which is a rewrite we
|
|
2471
|
-
// caused is a join against provider-reported cacheRead, and that join is the
|
|
2472
|
-
// analyst's, not this runtime's.
|
|
2473
1760
|
emit("context.prefix", {
|
|
2474
1761
|
change: comparison.change,
|
|
2475
1762
|
divergent_char: divergence.index,
|
|
@@ -2484,8 +1771,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
2484
1771
|
: (causes.length ? causes.map((event) => event.kind).join(",") : "unattributed"),
|
|
2485
1772
|
cause_event_seqs: causes.map((event) => event.seq).join(","),
|
|
2486
1773
|
events_since_handoff: instrumentation.sinceHandoff.length,
|
|
2487
|
-
// The request class, which falls straight out of the attribution and makes a run
|
|
2488
|
-
// directly comparable to a production cache table. Not new measurement.
|
|
2489
1774
|
request_class: divergence.index === null
|
|
2490
1775
|
? "steady-state"
|
|
2491
1776
|
: (causes.some((event) => event.kind === "context.recovery")
|
|
@@ -2497,368 +1782,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
2497
1782
|
instrumentation.mutationsSinceHandoff = 0;
|
|
2498
1783
|
};
|
|
2499
1784
|
|
|
2500
|
-
/**
|
|
2501
|
-
* Enqueue the folds this commit created with a deterministic brief.
|
|
2502
|
-
*
|
|
2503
|
-
* The request is built HERE, against the snapshot and the pre-commit state, because
|
|
2504
|
-
* this is the last moment the fold's source is still exact active evidence: after the
|
|
2505
|
-
* commit its span is a placeholder and the orientation either side of it is gone.
|
|
2506
|
-
* These are the bytes `prepareFold` already hashed into the fold's identity, so the
|
|
2507
|
-
* generator briefs the span that was folded and nothing else.
|
|
2508
|
-
*
|
|
2509
|
-
* A PARENT is queued too, and reads its children at depth one rather than through their
|
|
2510
|
-
* briefs: `consolidationSourceText` opens each child and leaves the grandchildren folded,
|
|
2511
|
-
* so the group is briefed from material instead of from a summary of summaries. The one
|
|
2512
|
-
* thing it must not race is a child's own upgrade, so a parent whose children are still
|
|
2513
|
-
* in this lane is deferred to the next boundary instead of reading briefs about to be
|
|
2514
|
-
* replaced. Unlike a leaf, a parent's source keeps: its children are folds, and folds
|
|
2515
|
-
* stay resolvable after the commit that nested them.
|
|
2516
|
-
*/
|
|
2517
|
-
const queueBriefUpgrades = (
|
|
2518
|
-
snapshot: ActiveContextSnapshot,
|
|
2519
|
-
stateBeforeCommit: ActiveContextState,
|
|
2520
|
-
foldIds: readonly string[],
|
|
2521
|
-
): void => {
|
|
2522
|
-
if (!observedSummarize || !persistence.state) return;
|
|
2523
|
-
const queued = (id: string): boolean =>
|
|
2524
|
-
upgrades.queue.some((entry) => entry.members.some((member) => member.foldId === id));
|
|
2525
|
-
const pending = (id: string): boolean => upgrades.running.has(id) || queued(id) ||
|
|
2526
|
-
upgrades.ready.some((entry) => entry.foldId === id) ||
|
|
2527
|
-
upgrades.deferred.has(id);
|
|
2528
|
-
// Gathered before any packing, because a batch is a property of the whole commit and
|
|
2529
|
-
// not of the fold that happens to be read first.
|
|
2530
|
-
const gathered: Array<{
|
|
2531
|
-
foldId: string;
|
|
2532
|
-
sourceSha256: string;
|
|
2533
|
-
refs: EvidenceRef[];
|
|
2534
|
-
span: Record<string, unknown>;
|
|
2535
|
-
chars: number;
|
|
2536
|
-
parent: boolean;
|
|
2537
|
-
}> = [];
|
|
2538
|
-
// Parents that waited are considered first and at every boundary, so the wait ends as
|
|
2539
|
-
// soon as the children settle rather than whenever the ladder happens to build another.
|
|
2540
|
-
for (const foldId of [...upgrades.deferred, ...foldIds]) {
|
|
2541
|
-
const full = upgrades.queue.length >= MAX_BRIEF_UPGRADE_QUEUE;
|
|
2542
|
-
if (upgrades.failed.has(foldId) || upgrades.running.has(foldId) || queued(foldId) ||
|
|
2543
|
-
upgrades.ready.some((entry) => entry.foldId === foldId)) continue;
|
|
2544
|
-
const fold = persistence.state.folds.find((item) => item.id === foldId);
|
|
2545
|
-
// A supplied brief is agent judgment and outranks the generator; a model brief is
|
|
2546
|
-
// already what this lane exists to produce. A fold that is no longer there is no
|
|
2547
|
-
// longer owed anything, so a rolled-back parent leaves the wait rather than sitting
|
|
2548
|
-
// in it.
|
|
2549
|
-
if (!fold || foldProvenance(fold, persistence.state).kind !== "deterministic") {
|
|
2550
|
-
upgrades.deferred.delete(foldId);
|
|
2551
|
-
continue;
|
|
2552
|
-
}
|
|
2553
|
-
const parent = fold.parts.some((part) => part.kind === "fold");
|
|
2554
|
-
// A parent waits rather than being shed: its children are folds, so its source is
|
|
2555
|
-
// still there whenever the lane has room, and a group that loses its turn at a busy
|
|
2556
|
-
// boundary would otherwise keep the index its children could not write. A leaf is the
|
|
2557
|
-
// other way round. Its source is exact active evidence at this commit and a
|
|
2558
|
-
// placeholder after it, so a full queue drops it here and the receipt says so.
|
|
2559
|
-
if (parent && (full || childFoldIds(fold).some(pending))) {
|
|
2560
|
-
upgrades.deferred.add(foldId);
|
|
2561
|
-
continue;
|
|
2562
|
-
}
|
|
2563
|
-
if (full) continue;
|
|
2564
|
-
upgrades.deferred.delete(foldId);
|
|
2565
|
-
const refs = parent
|
|
2566
|
-
? flattenFoldRefs(fold, persistence.state)
|
|
2567
|
-
: fold.parts.every((part) => part.kind === "raw")
|
|
2568
|
-
? fold.parts.map((part) => (part as { kind: "raw"; ref: EvidenceRef }).ref)
|
|
2569
|
-
: null;
|
|
2570
|
-
if (!refs) continue;
|
|
2571
|
-
try {
|
|
2572
|
-
// The parent reads the state that holds its children and their upgraded briefs;
|
|
2573
|
-
// a leaf reads the state where its own span was still raw active evidence.
|
|
2574
|
-
const sourceText = parent
|
|
2575
|
-
? consolidationSourceText(snapshot, persistence.state, fold.parts)
|
|
2576
|
-
: encodedFoldSource(snapshot, stateBeforeCommit, fold.parts, fold.kind);
|
|
2577
|
-
const chars = bytes(sourceText);
|
|
2578
|
-
if (chars > snapshot.policy.maxSourceChars) continue;
|
|
2579
|
-
gathered.push({
|
|
2580
|
-
foldId,
|
|
2581
|
-
sourceSha256: fold.sourceSha256,
|
|
2582
|
-
refs,
|
|
2583
|
-
chars,
|
|
2584
|
-
parent,
|
|
2585
|
-
span: {
|
|
2586
|
-
candidateId: fold.id,
|
|
2587
|
-
sourceRefs: clone(refs),
|
|
2588
|
-
sourceText,
|
|
2589
|
-
sourceSha256: sha256Text(sourceText),
|
|
2590
|
-
// What the PAYLOAD holds, which is one entry per part: a consolidation can
|
|
2591
|
-
// carry absorbed raw evidence beside its folded children, and the coverage
|
|
2592
|
-
// rule counts the things the model is actually shown. Counting only the fold
|
|
2593
|
-
// children told it to write fewer clauses than the payload has subjects.
|
|
2594
|
-
children: parent ? fold.parts.length : 0,
|
|
2595
|
-
},
|
|
2596
|
-
});
|
|
2597
|
-
} catch (error) {
|
|
2598
|
-
// Evidence drift between the commit and this read leaves the deterministic brief
|
|
2599
|
-
// standing, and the next commit record reports the attempt rather than hiding it.
|
|
2600
|
-
upgrades.failed.add(foldId);
|
|
2601
|
-
upgrades.failures += 1;
|
|
2602
|
-
upgrades.lastError = boundReceiptText(
|
|
2603
|
-
error instanceof Error ? error.message : String(error), 240, "brief upgrade",
|
|
2604
|
-
);
|
|
2605
|
-
}
|
|
2606
|
-
}
|
|
2607
|
-
// Pack the commit's spans into as few calls as the source budget allows. Order is the
|
|
2608
|
-
// order they were gathered, which is span order within the commit, so the numbered
|
|
2609
|
-
// spans a batch shows the generator run the way the session did and one really is the
|
|
2610
|
-
// orientation for the next.
|
|
2611
|
-
for (let at = 0; at < gathered.length;) {
|
|
2612
|
-
if (upgrades.queue.length >= MAX_BRIEF_UPGRADE_QUEUE) {
|
|
2613
|
-
// No room for another call. A parent goes back to waiting, because its children are
|
|
2614
|
-
// folds and its source will still be there whenever the lane frees up. A leaf
|
|
2615
|
-
// cannot wait: its source is exact active evidence at THIS commit and a placeholder
|
|
2616
|
-
// after it, so it keeps the deterministic brief it committed with.
|
|
2617
|
-
for (const item of gathered.slice(at)) {
|
|
2618
|
-
if (item.parent) upgrades.deferred.add(item.foldId);
|
|
2619
|
-
}
|
|
2620
|
-
break;
|
|
2621
|
-
}
|
|
2622
|
-
const members: typeof gathered = [];
|
|
2623
|
-
let chars = 0;
|
|
2624
|
-
while (at < gathered.length && members.length < MAX_BRIEF_BATCH_SPANS &&
|
|
2625
|
-
(!members.length || chars + gathered[at].chars <= snapshot.policy.maxSourceChars)) {
|
|
2626
|
-
chars += gathered[at].chars;
|
|
2627
|
-
members.push(gathered[at]);
|
|
2628
|
-
at += 1;
|
|
2629
|
-
}
|
|
2630
|
-
// Orientation is the run's, not each span's: where this batch of consecutive spans
|
|
2631
|
-
// sits in the wider conversation. Taken from the ends rather than from the union,
|
|
2632
|
-
// because a commit can fold spans with protected material still raw between them and
|
|
2633
|
-
// the union of those refs is not one contiguous range.
|
|
2634
|
-
const before = boundedOrientation(snapshot, members[0].refs);
|
|
2635
|
-
const after = boundedOrientation(snapshot, members[members.length - 1].refs);
|
|
2636
|
-
upgrades.queue.push({
|
|
2637
|
-
members: members.map((item) => ({ foldId: item.foldId, sourceSha256: item.sourceSha256 })),
|
|
2638
|
-
request: {
|
|
2639
|
-
spans: members.map((item) => item.span),
|
|
2640
|
-
beforeRefs: clone(before.beforeRefs),
|
|
2641
|
-
beforeText: before.beforeText,
|
|
2642
|
-
beforeSha256: sha256Text(before.beforeText),
|
|
2643
|
-
afterRefs: clone(after.afterRefs),
|
|
2644
|
-
afterText: after.afterText,
|
|
2645
|
-
afterSha256: sha256Text(after.afterText),
|
|
2646
|
-
maxBriefChars: snapshot.policy.maxBriefChars,
|
|
2647
|
-
// Read by the observed wrapper and by nothing else, so a queue that backs up is
|
|
2648
|
-
// visible as waiting rather than as a slow generator. The summarizer contract is
|
|
2649
|
-
// on the RESULT, so an extra request field reaches it harmlessly.
|
|
2650
|
-
queuedAtMs: Date.now(),
|
|
2651
|
-
},
|
|
2652
|
-
});
|
|
2653
|
-
}
|
|
2654
|
-
};
|
|
2655
|
-
|
|
2656
|
-
/**
|
|
2657
|
-
* One generator call, between boundaries, cancel-safe. It writes nothing durable: a
|
|
2658
|
-
* finished brief waits in `ready` for a commit to carry it, and an aborted or failed
|
|
2659
|
-
* call leaves the fold exactly as it committed.
|
|
2660
|
-
*
|
|
2661
|
-
* Returns whether a call was started, which is what lets the drain below stop.
|
|
2662
|
-
*/
|
|
2663
|
-
const startBriefUpgrade = (snapshot: ActiveContextSnapshot, ctx: any): boolean => {
|
|
2664
|
-
const summarize = observedSummarize;
|
|
2665
|
-
// In CALLS, not folds. `running` is keyed by every fold a call briefs so the queue can
|
|
2666
|
-
// ask whether one is already being written, so its size is a fold count and reading it
|
|
2667
|
-
// as the in-flight bound would stop the lane after a single batch.
|
|
2668
|
-
const inFlight = new Set(upgrades.running.values()).size;
|
|
2669
|
-
if (!summarize || lifecycle.shuttingDown || !upgrades.queue.length ||
|
|
2670
|
-
inFlight >= MAX_BRIEF_UPGRADES_IN_FLIGHT) return false;
|
|
2671
|
-
const entry = upgrades.queue.shift()!;
|
|
2672
|
-
const controller = new AbortController();
|
|
2673
|
-
const slot = { controller, promise: Promise.resolve() };
|
|
2674
|
-
// Keyed by EVERY fold the call briefs, so the queue's "already being briefed" check and
|
|
2675
|
-
// the in-flight bound both read one call as the one thing it is.
|
|
2676
|
-
for (const member of entry.members) upgrades.running.set(member.foldId, slot);
|
|
2677
|
-
const capturedSessionId = snapshot.sessionId;
|
|
2678
|
-
const capturedGeneration = lifecycle.generation;
|
|
2679
|
-
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
2680
|
-
slot.promise = (async () => {
|
|
2681
|
-
const timed = new Promise<never>((_resolve, reject) => {
|
|
2682
|
-
timeout = setTimeout(() => {
|
|
2683
|
-
controller.abort();
|
|
2684
|
-
reject(new Error(`Brief upgrade exceeded ${snapshot.policy.briefTimeoutMs}ms`));
|
|
2685
|
-
}, snapshot.policy.briefTimeoutMs);
|
|
2686
|
-
});
|
|
2687
|
-
// The same contract `prepareFold` holds a generated brief to, through the same
|
|
2688
|
-
// function: an upgrade with its own copy of the check would drift from the fold
|
|
2689
|
-
// path's, and the cure a missed criterion earns belongs to both or to neither.
|
|
2690
|
-
const generated = await Promise.race([
|
|
2691
|
-
generatedBriefs({
|
|
2692
|
-
summarize: (request, callCtx) => summarize({ ...request, signal: controller.signal }, callCtx),
|
|
2693
|
-
request: entry.request,
|
|
2694
|
-
spans: entry.request.spans as Array<Record<string, unknown>>,
|
|
2695
|
-
ctx,
|
|
2696
|
-
maxBriefChars: snapshot.policy.maxBriefChars,
|
|
2697
|
-
toolName: snapshot.toolName,
|
|
2698
|
-
}),
|
|
2699
|
-
timed,
|
|
2700
|
-
]);
|
|
2701
|
-
if (controller.signal.aborted ||
|
|
2702
|
-
!sessionIdentityStillValid(ctx, capturedSessionId, capturedGeneration)) return;
|
|
2703
|
-
// Per span, not per call. A batch is one request and many answers, so one span that
|
|
2704
|
-
// could not be cured is one fold keeping its deterministic brief while its neighbours
|
|
2705
|
-
// take theirs; the whole call failing on it would be the coverage problem batching
|
|
2706
|
-
// exists to solve, wearing a different hat.
|
|
2707
|
-
entry.members.forEach((member, at) => {
|
|
2708
|
-
const written = generated.briefs[at];
|
|
2709
|
-
if (!written) {
|
|
2710
|
-
upgrades.failed.add(member.foldId);
|
|
2711
|
-
upgrades.failures += 1;
|
|
2712
|
-
upgrades.lastError = boundReceiptText(
|
|
2713
|
-
`span ${at + 1} of ${entry.members.length}: ${generated.complaints[0] ?? "no brief"}`,
|
|
2714
|
-
240, "brief upgrade",
|
|
2715
|
-
);
|
|
2716
|
-
return;
|
|
2717
|
-
}
|
|
2718
|
-
upgrades.ready.push({
|
|
2719
|
-
foldId: member.foldId,
|
|
2720
|
-
sourceSha256: member.sourceSha256,
|
|
2721
|
-
brief: written.brief,
|
|
2722
|
-
provenance: written.provenance,
|
|
2723
|
-
});
|
|
2724
|
-
});
|
|
2725
|
-
upgrades.cures += generated.cured;
|
|
2726
|
-
})().catch((error) => {
|
|
2727
|
-
if (controller.signal.aborted) return;
|
|
2728
|
-
// LOUD, and once, for every fold the call carried. They keep their deterministic
|
|
2729
|
-
// briefs, the failure is counted onto the next commit record, and none of them is
|
|
2730
|
-
// ever sent again: a generator that fails on a span fails on it again, and a retry
|
|
2731
|
-
// loop would spend the session's provider calls proving that.
|
|
2732
|
-
for (const member of entry.members) upgrades.failed.add(member.foldId);
|
|
2733
|
-
upgrades.failures += 1;
|
|
2734
|
-
upgrades.lastError = boundReceiptText(
|
|
2735
|
-
error instanceof Error ? error.message : String(error), 240, "brief upgrade",
|
|
2736
|
-
);
|
|
2737
|
-
}).finally(() => {
|
|
2738
|
-
clearTimeout(timeout);
|
|
2739
|
-
for (const member of entry.members) {
|
|
2740
|
-
if (upgrades.running.get(member.foldId) === slot) upgrades.running.delete(member.foldId);
|
|
2741
|
-
}
|
|
2742
|
-
// A finished call starts the next one, so the drain rate is the generator's latency
|
|
2743
|
-
// rather than however often a ladder pass happens to look. Still between
|
|
2744
|
-
// boundaries: nothing here touches the projection, and the generation check is the
|
|
2745
|
-
// same fence `cancelBriefUpgrades` is called behind, so a session that moved on
|
|
2746
|
-
// cannot be restarted by a call that outlived it.
|
|
2747
|
-
if (!lifecycle.shuttingDown && lifecycle.generation === capturedGeneration) {
|
|
2748
|
-
resumeBriefUpgrades(snapshot, ctx);
|
|
2749
|
-
}
|
|
2750
|
-
});
|
|
2751
|
-
return true;
|
|
2752
|
-
};
|
|
2753
|
-
|
|
2754
|
-
/**
|
|
2755
|
-
* Fill the in-flight slots from the queue. The bound is a constant, not a pace: the
|
|
2756
|
-
* lane starts everything it is allowed to start the moment there is work, because a
|
|
2757
|
-
* fold whose brief is written late is a fold the session read deterministic in the
|
|
2758
|
-
* meantime.
|
|
2759
|
-
*/
|
|
2760
|
-
const startBriefUpgrades = (snapshot: ActiveContextSnapshot, ctx: any): void => {
|
|
2761
|
-
while (startBriefUpgrade(snapshot, ctx)) { /* Bounded by the in-flight constant. */ }
|
|
2762
|
-
};
|
|
2763
|
-
|
|
2764
|
-
/**
|
|
2765
|
-
* Take up the waiting parents, then fill the slots.
|
|
2766
|
-
*
|
|
2767
|
-
* A deferred parent was waiting on the lane, so the lane finishing is the news, not the
|
|
2768
|
-
* next commit. Boundaries are where briefs APPLY; hanging the retry on them too would
|
|
2769
|
-
* leave a session that stops folding holding parents whose children were briefed long
|
|
2770
|
-
* ago, and the last group the ladder built is exactly the one a session ends beside.
|
|
2771
|
-
*/
|
|
2772
|
-
const resumeBriefUpgrades = (snapshot: ActiveContextSnapshot, ctx: any): void => {
|
|
2773
|
-
if (upgrades.deferred.size && persistence.state) {
|
|
2774
|
-
queueBriefUpgrades(snapshot, persistence.state, []);
|
|
2775
|
-
}
|
|
2776
|
-
startBriefUpgrades(snapshot, ctx);
|
|
2777
|
-
};
|
|
2778
|
-
|
|
2779
|
-
const cancelBriefUpgrades = (): void => {
|
|
2780
|
-
for (const slot of upgrades.running.values()) slot.controller.abort();
|
|
2781
|
-
upgrades.running.clear();
|
|
2782
|
-
upgrades.queue = [];
|
|
2783
|
-
upgrades.ready = [];
|
|
2784
|
-
upgrades.deferred.clear();
|
|
2785
|
-
};
|
|
2786
|
-
|
|
2787
|
-
/**
|
|
2788
|
-
* Apply finished upgrades INSIDE the commit's own mutation.
|
|
2789
|
-
*
|
|
2790
|
-
* Called between `commitPendingMarks` and the byte accounting, so an upgraded brief is
|
|
2791
|
-
* part of what this commit is measured to have done rather than a rewrite nobody
|
|
2792
|
-
* charged for. Nothing else may call this: an upgrade that finishes between boundaries
|
|
2793
|
-
* waits, however long the wait is.
|
|
2794
|
-
*
|
|
2795
|
-
* Identity is revalidated the way `preparedFoldError` revalidates a prepared fold. A
|
|
2796
|
-
* fold that was expanded, reclaimed, re-briefed by the agent, or rebuilt over a
|
|
2797
|
-
* different span is a different object than the one the generator read, and its upgrade
|
|
2798
|
-
* is DROPPED rather than deferred.
|
|
2799
|
-
*
|
|
2800
|
-
* It lands in the brief OVERRIDE map, the same channel the agent's own `rebrief` uses,
|
|
2801
|
-
* and carries its generator beside the text. A fold record is content-addressed and
|
|
2802
|
-
* immutable: rewriting one in place reports a conflicting durable fold at the next
|
|
2803
|
-
* persist and suspends automatic management, which is precisely why the override map
|
|
2804
|
-
* exists.
|
|
2805
|
-
*
|
|
2806
|
-
* EVERY finished brief lands, and there is no per-boundary cap. A prefix cache is
|
|
2807
|
-
* invalidated from the FIRST byte that differs, so the commit's mutation point is the
|
|
2808
|
-
* position of the earliest fold it upgrades and every later upgrade in the same commit
|
|
2809
|
-
* sits inside a suffix that break already re-sent: the second and third upgrade on one
|
|
2810
|
-
* boundary cost nothing the first did not already cost. Holding one back does not save
|
|
2811
|
-
* that cost, it defers the same break to a later commit and pays it there on top of
|
|
2812
|
-
* whatever that commit does. What bounds the count is the work that can be outstanding,
|
|
2813
|
-
* MAX_BRIEF_UPGRADE_QUEUE waiting plus MAX_BRIEF_UPGRADES_IN_FLIGHT running, and the
|
|
2814
|
-
* boundary that clears them.
|
|
2815
|
-
*/
|
|
2816
|
-
const applyBriefUpgrades = (): string[] => {
|
|
2817
|
-
if (!upgrades.ready.length || !persistence.state) return [];
|
|
2818
|
-
const applied: string[] = [];
|
|
2819
|
-
for (const entry of upgrades.ready) {
|
|
2820
|
-
const state = persistence.state;
|
|
2821
|
-
const fold = state.folds.find((item) => item.id === entry.foldId);
|
|
2822
|
-
if (!fold || fold.sourceSha256 !== entry.sourceSha256 ||
|
|
2823
|
-
foldProvenance(fold, state).kind !== "deterministic" ||
|
|
2824
|
-
state.expanded.includes(fold.id) || state.briefs?.[fold.id] !== undefined) continue;
|
|
2825
|
-
persistence.state = {
|
|
2826
|
-
...state,
|
|
2827
|
-
briefs: {
|
|
2828
|
-
...(state.briefs ?? {}),
|
|
2829
|
-
[fold.id]: { brief: entry.brief, provenance: clone(entry.provenance) },
|
|
2830
|
-
},
|
|
2831
|
-
};
|
|
2832
|
-
applied.push(fold.id);
|
|
2833
|
-
}
|
|
2834
|
-
upgrades.ready = [];
|
|
2835
|
-
return applied;
|
|
2836
|
-
};
|
|
2837
|
-
|
|
2838
|
-
const commitDeterministicCandidate = async (
|
|
2839
|
-
snapshot: ActiveContextSnapshot,
|
|
2840
|
-
candidate: FoldCandidate,
|
|
2841
|
-
brief: string,
|
|
2842
|
-
): Promise<string> => {
|
|
2843
|
-
const stateBeforeCommit = persistence.state!;
|
|
2844
|
-
const preparedFold = await prepareFold({
|
|
2845
|
-
candidate,
|
|
2846
|
-
snapshot,
|
|
2847
|
-
state: stateBeforeCommit,
|
|
2848
|
-
generation: lifecycle.generation,
|
|
2849
|
-
brief,
|
|
2850
|
-
briefProvenance: "deterministic",
|
|
2851
|
-
});
|
|
2852
|
-
persistence.state = commitPreparedFold({
|
|
2853
|
-
prepared: preparedFold,
|
|
2854
|
-
snapshot,
|
|
2855
|
-
state: stateBeforeCommit,
|
|
2856
|
-
generation: lifecycle.generation,
|
|
2857
|
-
});
|
|
2858
|
-
queueBriefUpgrades(snapshot, stateBeforeCommit, [preparedFold.id]);
|
|
2859
|
-
return preparedFold.id;
|
|
2860
|
-
};
|
|
2861
|
-
|
|
2862
1785
|
const prepareAndCommitExplicit = async (input: {
|
|
2863
1786
|
snapshot: ActiveContextSnapshot;
|
|
2864
1787
|
candidate: FoldCandidate;
|
|
@@ -2880,7 +1803,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
2880
1803
|
state: baseState,
|
|
2881
1804
|
generation: generationAtStart,
|
|
2882
1805
|
brief: input.brief,
|
|
2883
|
-
summarize: observedSummarize,
|
|
2884
1806
|
ctx: input.ctx,
|
|
2885
1807
|
signal: input.signal,
|
|
2886
1808
|
});
|
|
@@ -2900,18 +1822,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
2900
1822
|
return { preparedFold, nextState };
|
|
2901
1823
|
};
|
|
2902
1824
|
|
|
2903
|
-
/**
|
|
2904
|
-
* The one mutation of an epoch: every pending mark, plus the automatic additions
|
|
2905
|
-
* a commit is allowed to make, applied through the same machinery as an immediate
|
|
2906
|
-
* fold. Free additions come first (peek reads the agent already discarded), then
|
|
2907
|
-
* the quota top-up that guarantees the commit is worth its rewrite.
|
|
2908
|
-
*/
|
|
2909
|
-
/**
|
|
2910
|
-
* One turn of inflow, as a share of the window: the least a commit at high occupancy
|
|
2911
|
-
* has to free to be worth its rewrite. Measured inflow is used when the session has
|
|
2912
|
-
* any -- rep13 ran at a median 13,865 and a maximum 27,815 tokens per request against
|
|
2913
|
-
* a 400,000 window -- and the floor share stands in until then.
|
|
2914
|
-
*/
|
|
2915
1825
|
const commitDepthFloorShare = (snapshot: ActiveContextSnapshot): number => {
|
|
2916
1826
|
const window = snapshot.contextWindow;
|
|
2917
1827
|
if (!Number.isFinite(window) || window <= 0) return COMMIT_RECLAIM_FLOOR_SHARE;
|
|
@@ -2924,89 +1834,18 @@ export function registerActiveContext(pi: any, options: {
|
|
|
2924
1834
|
return occupancy !== null && occupancy >= snapshot.policy.refoldRatio;
|
|
2925
1835
|
};
|
|
2926
1836
|
|
|
2927
|
-
/**
|
|
2928
|
-
* THE OUTPUT WALL.
|
|
2929
|
-
*
|
|
2930
|
-
* A declared serving budget has already netted out the reservation the answer needs,
|
|
2931
|
-
* so the runtime treats the whole budget as spendable and fences only when it is
|
|
2932
|
-
* spent. That fence is the input one, and the input is not what fails first. The host
|
|
2933
|
-
* derives the answer's ceiling per request as its DESCRIPTOR window less its own
|
|
2934
|
-
* estimate of the projection less a fixed safety margin, so filling the declared
|
|
2935
|
-
* budget to the brim leaves the answer exactly the reservation and nothing else.
|
|
2936
|
-
*
|
|
2937
|
-
* Which is a fence that fires after the wire, the same defect the transmission fence
|
|
2938
|
-
* was given a forward look to fix in rep 13. The commit trigger never got one.
|
|
2939
|
-
* Measured 2026-08-12 (sol-20260812 rep 1): occupancy walked 218,713 -> 236,595 over
|
|
2940
|
-
* twelve ordinals against a 251,520 budget with no commit, and the request built after
|
|
2941
|
-
* the last measurement was served 16 output tokens and aborted. Every number was known
|
|
2942
|
-
* to the runtime; nothing was weighing the request that had not been built yet.
|
|
2943
|
-
*
|
|
2944
|
-
* So the trigger carries what the transmission fence already carries: one worst recent
|
|
2945
|
-
* inflow step. With no measured inflow this is exactly the old `used > budget`
|
|
2946
|
-
* condition, which is the state it degrades to on a session's first turns.
|
|
2947
|
-
*/
|
|
2948
1837
|
const outputWallDue = (snapshot: ActiveContextSnapshot): boolean => {
|
|
2949
1838
|
const capacity = servingCapacity(snapshot.contextWindow);
|
|
2950
1839
|
if (capacity.usedTokens === null || !(capacity.budgetTokens > 0)) return false;
|
|
2951
|
-
// THE EARLY ZONE ONLY, so this is strictly additive.
|
|
2952
|
-
//
|
|
2953
|
-
// Past the budget the pre-existing overflow paths already own the emergency: the
|
|
2954
|
-
// commit-path exemption, the last-call gate and the fence all read `used > budget`
|
|
2955
|
-
// and have since before this existed. A second trigger in that territory changes the
|
|
2956
|
-
// cadence of sessions that were already being handled, and gate 107 measured what
|
|
2957
|
-
// that costs: a fixture parked at 88,000 against an 83,616 budget put four generator
|
|
2958
|
-
// calls in flight against a bound of two. So the wall speaks only where nothing spoke
|
|
2959
|
-
// before, in the band one inflow step wide below the budget.
|
|
2960
1840
|
if (capacity.usedTokens > capacity.budgetTokens) return false;
|
|
2961
1841
|
return capacity.usedTokens + expectedWallInflowTokens() > capacity.budgetTokens;
|
|
2962
1842
|
};
|
|
2963
1843
|
|
|
2964
|
-
/**
|
|
2965
|
-
* What the gated round did, measured against the arming snapshot. Pins and unpins
|
|
2966
|
-
* are read from the stream itself (context.protect records after the exposure); the
|
|
2967
|
-
* call and mark deltas are clamped at zero because contextCalls is process-local and
|
|
2968
|
-
* a reload inside a round must not report a negative response.
|
|
2969
|
-
*/
|
|
2970
|
-
const lastCallAttribution = (
|
|
2971
|
-
lastCall: NonNullable<ActiveContextState["lastCall"]>,
|
|
2972
|
-
agentMarksNow: number,
|
|
2973
|
-
): { responded: boolean; context_calls: number; marks_added: number; protects: number; unprotects: number } => {
|
|
2974
|
-
const contextCalls = Math.max(0, curation.contextCalls - lastCall.contextCalls);
|
|
2975
|
-
const protectRecords = instrumentation.ledger.events.filter((record) =>
|
|
2976
|
-
record.seq > lastCall.exposure && record.kind === "context.protect");
|
|
2977
|
-
const protects = protectRecords.filter((record) => record.protect === true).length;
|
|
2978
|
-
return {
|
|
2979
|
-
responded: contextCalls > 0,
|
|
2980
|
-
context_calls: contextCalls,
|
|
2981
|
-
marks_added: Math.max(0, agentMarksNow - lastCall.agentMarks),
|
|
2982
|
-
protects,
|
|
2983
|
-
unprotects: protectRecords.length - protects,
|
|
2984
|
-
};
|
|
2985
|
-
};
|
|
2986
|
-
|
|
2987
|
-
const clearLastCall = (): void => {
|
|
2988
|
-
if (!persistence.state?.lastCall) return;
|
|
2989
|
-
const next = { ...persistence.state };
|
|
2990
|
-
delete next.lastCall;
|
|
2991
|
-
persistence.state = next;
|
|
2992
|
-
curation.lastCallDelivery = null;
|
|
2993
|
-
};
|
|
2994
|
-
|
|
2995
1844
|
const runCommitEpoch = async (
|
|
2996
1845
|
snapshot: ActiveContextSnapshot,
|
|
2997
1846
|
trigger: string,
|
|
2998
1847
|
topUp: boolean,
|
|
2999
|
-
/**
|
|
3000
|
-
* Pressure the waiver is measured against. Defaults to the measured ratio; the
|
|
3001
|
-
* over-budget reduction forces the fence so nothing survivable is held back.
|
|
3002
|
-
*/
|
|
3003
1848
|
waiverRatio: number | null = measurements.latestRatio,
|
|
3004
|
-
/**
|
|
3005
|
-
* A user asked for this commit. It outranks the two ECONOMIC guards -- the reclaim
|
|
3006
|
-
* floor and the one-mutation-per-handoff budget -- because those exist to spend the
|
|
3007
|
-
* session's rewrites well and the user just said where to spend one. It outranks
|
|
3008
|
-
* nothing else: freshness, pins, exactness and the provider fence are unchanged.
|
|
3009
|
-
*/
|
|
3010
1849
|
userRequested = false,
|
|
3011
1850
|
): Promise<Record<string, unknown> | null> => {
|
|
3012
1851
|
const ordinal = markOrdinal(snapshot);
|
|
@@ -3019,79 +1858,16 @@ export function registerActiveContext(pi: any, options: {
|
|
|
3019
1858
|
const addition = addPendingMark(state, mark);
|
|
3020
1859
|
if (addition.added) { state = addition.state; peekAdded += 1; }
|
|
3021
1860
|
}
|
|
3022
|
-
// THE COUNT LAW RUNS AT EVERY COMMIT EPOCH, BEFORE ANYTHING ELSE PROPOSES.
|
|
3023
|
-
//
|
|
3024
|
-
// The number of parents the eligible-root count owes is arithmetic, not a judgment
|
|
3025
|
-
// about pressure, so it is not the top-up's to make and not the user commit's to
|
|
3026
|
-
// skip: at any point the count allows k groups, k parents form. Running first is
|
|
3027
|
-
// what makes the rest coherent -- the groups claim their children before the top-up
|
|
3028
|
-
// reads the claim set, so nothing proposes a span over a fold a parent is taking.
|
|
3029
1861
|
for (const mark of consolidationMarks({ snapshot, state, ordinal })) {
|
|
3030
1862
|
const addition = addPendingMark(state, mark);
|
|
3031
1863
|
if (addition.added) { state = addition.state; consolidationAdded += 1; }
|
|
3032
1864
|
}
|
|
3033
1865
|
const guarded = currentTurnRefKeys(snapshot);
|
|
3034
|
-
// THE GUARD IS ADJUDICATED AT COMMIT, NEVER AT PROPOSAL.
|
|
3035
|
-
//
|
|
3036
|
-
// `guarded` is every tool result since the last terminal assistant message. When no
|
|
3037
|
-
// turn has ever closed, `currentTurnBoundary` is -1 and that set is the WHOLE
|
|
3038
|
-
// window. Handing it to `topUpMarks` as `excludeRefKeys` therefore proposed nothing
|
|
3039
|
-
// at all on that shape, so `guardWaiverCount` below counted zero guarded marks and
|
|
3040
|
-
// the waiver it exists to grant could never fire. Measured 2026-08-10
|
|
3041
|
-
// (luna-20260810 pifold rep 2): 274,173 tokens of unmarked stale spans, zero marks
|
|
3042
|
-
// proposed, zero commits, two provider rejections.
|
|
3043
|
-
//
|
|
3044
|
-
// So the top-up proposes freely and `commitPendingMarks({ guardCurrentTurn: true,
|
|
3045
|
-
// guardWaiver })` decides. That is the only place the guard has ever had a waiver,
|
|
3046
|
-
// and one adjudicator is why the starvation case is reachable at all.
|
|
3047
|
-
//
|
|
3048
|
-
// Wedge absorption keeps the exclusion (below): it grows a mark backward over a
|
|
3049
|
-
// gap without any waiver of its own, so the guard there has no second chance.
|
|
3050
|
-
// THE CLASS LAW IS UNCONDITIONAL.
|
|
3051
|
-
//
|
|
3052
|
-
// There was a fence-only snapshot here: at high occupancy it narrowed the fresh
|
|
3053
|
-
// tail to a quarter and extended the automatic reach over the middle, so a reduction
|
|
3054
|
-
// that had to make a rejected request sendable had mass to reach. That existed
|
|
3055
|
-
// because the runtime had exactly one answer to a provider rejection, folding
|
|
3056
|
-
// harder, and folding harder inside a positional region runs out of legal material.
|
|
3057
|
-
// The rollback lane is the answer now: an overflow rolls the leaf back past the
|
|
3058
|
-
// request that failed and the ordinary commit runs on the shorter window. So the
|
|
3059
|
-
// law holds in EVERY snapshot at every occupancy -- membership decides what folds,
|
|
3060
|
-
// the fresh tail never folds, pins are exempt -- and there is one set of rules to
|
|
3061
|
-
// reason about instead of two.
|
|
3062
|
-
// The thermostat. Firing at the trigger line and folding down to the target line is
|
|
3063
|
-
// what makes event SPACING structural rather than hoped for.
|
|
3064
1866
|
const capacity = servingCapacity(snapshot.contextWindow);
|
|
3065
1867
|
const usedTokens = capacity.usedTokens;
|
|
3066
1868
|
const budgetTokens = capacity.budgetTokens;
|
|
3067
|
-
// THE UNTRANSMISSIBILITY EXEMPTION, NARROWED.
|
|
3068
|
-
//
|
|
3069
|
-
// A request whose projection exceeds the provider input budget is rejected outright,
|
|
3070
|
-
// so recovery must produce a window that fits. The hard fence, though, is a RATIO
|
|
3071
|
-
// prediction. Standing near it is not the same as being unable to send, and treating
|
|
3072
|
-
// the two alike handed the fence path a standing waiver from both economy guards. Measured 2026-08-07 (rep 17): fence-path
|
|
3073
|
-
// window-pressure commits fired at eligible-freed shares as low as 0.018, under the
|
|
3074
|
-
// 0.02 floor the guided path honors, and re-fired inside a single ordinal. So only
|
|
3075
|
-
// two states outrank the economy: the overflow recovery lane, which runs because a
|
|
3076
|
-
// request already did not fit, and an occupancy genuinely past the serving budget,
|
|
3077
|
-
// where the next request aborts unless something moves. Everything else defers.
|
|
3078
|
-
// The output wall deliberately does NOT join this list. It decides WHEN the one
|
|
3079
|
-
// structural mutation of a handoff fires, not how many fire: exempting it here let a
|
|
3080
|
-
// window parked one inflow step under the budget rebuild its prefix on every pass,
|
|
3081
|
-
// which is the cost the mutation budget exists to refuse and what gate 58 caught.
|
|
3082
1869
|
const overflowExempt = userRequested || curation.recoveryAttempts > 0 ||
|
|
3083
1870
|
(usedTokens !== null && budgetTokens > 0 && usedTokens > budgetTokens);
|
|
3084
|
-
// ONE STRUCTURAL MUTATION PER HANDOFF.
|
|
3085
|
-
//
|
|
3086
|
-
// Measured 2026-08-06 (rep 15): two context.commit records 50ms apart inside one
|
|
3087
|
-
// ordinal, revisions 14 and 15 -- two REAL mutations, not a duplicated record. The
|
|
3088
|
-
// pass had committed at the announced trigger, rebuilt the projection, found it
|
|
3089
|
-
// still CROWDED (inside the fence margin, but transmissible), and committed again.
|
|
3090
|
-
// The second commit bought margin the next pass would have bought anyway and cost a
|
|
3091
|
-
// whole second prefix rewrite. So a second commit in the same handoff defers unless
|
|
3092
|
-
// the request is genuinely untransmittable: a request whose projection exceeds the
|
|
3093
|
-
// provider input budget is rejected outright, so recovery must produce a window that
|
|
3094
|
-
// fits, and the fence and the recovery lane spend the budget to get one.
|
|
3095
1871
|
if (instrumentation.mutationsSinceHandoff > 0 && !overflowExempt) {
|
|
3096
1872
|
emit("context.commit", {
|
|
3097
1873
|
trigger,
|
|
@@ -3103,20 +1879,10 @@ export function registerActiveContext(pi: any, options: {
|
|
|
3103
1879
|
});
|
|
3104
1880
|
return null;
|
|
3105
1881
|
}
|
|
3106
|
-
// THE HYSTERESIS, AND THE WHOLE FREEING TARGET.
|
|
3107
|
-
//
|
|
3108
|
-
// How deep this event cuts is one subtraction on one denominator: what is used,
|
|
3109
|
-
// less where the thermostat wants to land, over the serving budget. The separate
|
|
3110
|
-
// 0.40 floor is gone -- it was a share of the WINDOW under a share of the BUDGET,
|
|
3111
|
-
// so it never once bound (the hysteresis share at these thresholds bottoms out at
|
|
3112
|
-
// 0.405 of window), and a floor that cannot fire is a number that only drifts.
|
|
3113
1882
|
const freeingTarget = usedTokens === null || budgetTokens <= 0
|
|
3114
1883
|
? 0
|
|
3115
1884
|
: Math.max(0, (usedTokens - thresholds.minTarget * budgetTokens) / budgetTokens);
|
|
3116
1885
|
if (topUp) {
|
|
3117
|
-
// Measuring top-up progress against ELIGIBLE mass is what keeps the pressure
|
|
3118
|
-
// backstop working under a peek-heavy agent: pinned and retained marks are mass
|
|
3119
|
-
// no commit can move, and counting them as progress stops the top-up dead.
|
|
3120
1886
|
for (const mark of topUpMarks({
|
|
3121
1887
|
snapshot,
|
|
3122
1888
|
state,
|
|
@@ -3127,17 +1893,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
3127
1893
|
const addition = addPendingMark(state, mark);
|
|
3128
1894
|
if (addition.added) { state = addition.state; topUpAdded += 1; }
|
|
3129
1895
|
}
|
|
3130
|
-
// DEPTH, inside the zones.
|
|
3131
|
-
//
|
|
3132
|
-
// Near the top of the window a commit that frees less than one turn of inflow
|
|
3133
|
-
// does not reduce anything: it pays a full prefix rewrite, gives back less than
|
|
3134
|
-
// the next stage adds, and the window ratchets UP through commit after commit.
|
|
3135
|
-
// Measured 2026-08-06 (rep13): once the big stale mass was folded, six commits of
|
|
3136
|
-
// two to four folds each carried the window from 340k to 370k and into a provider
|
|
3137
|
-
// rejection, with the backstop firing the whole way. So a commit at or above the
|
|
3138
|
-
// backstop that has not reached one inflow step tops up against everything
|
|
3139
|
-
// ELIGIBLE instead of billing a rewrite for crumbs. Eligible is the operative
|
|
3140
|
-
// word: this reaches harder inside what the class law admits, never outside it.
|
|
3141
1896
|
const reachedShare = markAccounting(snapshot, state).eligibleFreedBudgetShare;
|
|
3142
1897
|
const shallow = reachedShare < Math.max(commitDepthFloorShare(snapshot), freeingTarget);
|
|
3143
1898
|
if (shallow && atOrAboveBackstop(snapshot, waiverRatio)) {
|
|
@@ -3153,9 +1908,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
3153
1908
|
}
|
|
3154
1909
|
}
|
|
3155
1910
|
}
|
|
3156
|
-
// Boundary slivers ride along in the mutation this commit already pays for; they
|
|
3157
|
-
// are never a mutation of their own, and a gap above the tiny token threshold is
|
|
3158
|
-
// deliberate curation the absorber must not touch.
|
|
3159
1911
|
const wedges = absorbWedgeMarks({
|
|
3160
1912
|
snapshot,
|
|
3161
1913
|
state,
|
|
@@ -3164,18 +1916,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
3164
1916
|
});
|
|
3165
1917
|
state = wedges.state;
|
|
3166
1918
|
const accounting = markAccounting(snapshot, state);
|
|
3167
|
-
// A COMMIT PASS THAT FINDS NOTHING SAYS SO.
|
|
3168
|
-
//
|
|
3169
|
-
// This was a bare `return null`, and it is how the rep-2 starvation stayed invisible
|
|
3170
|
-
// for 25 stages: the last call announced 274,173 tokens of unmarked stale spans, the
|
|
3171
|
-
// pass that answered it proposed nothing, and the stream carried no record that a
|
|
3172
|
-
// commit had even been attempted. Every other deferral on this path already names
|
|
3173
|
-
// itself; this one is the same shape with the reason that matters, and it reports
|
|
3174
|
-
// the remainder beside the emptiness so the contradiction -- mass on the table, no
|
|
3175
|
-
// proposal -- is readable on one record instead of inferred across two.
|
|
3176
|
-
//
|
|
3177
|
-
// Stream record, not a `context.receipt`: receipts are window carriers, and a pass
|
|
3178
|
-
// that changed nothing must not add bytes to the projection to announce it.
|
|
3179
1919
|
if (!accounting.pending) {
|
|
3180
1920
|
const remainder = unmarkedRemainder(snapshot, state, projectionCharsPerToken());
|
|
3181
1921
|
emit("context.commit", {
|
|
@@ -3190,12 +1930,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
3190
1930
|
});
|
|
3191
1931
|
return null;
|
|
3192
1932
|
}
|
|
3193
|
-
// The reclaim floor. One structural mutation per model call is the budget, so a
|
|
3194
|
-
// commit that would free crumbs spends the whole budget on nothing: it defers and
|
|
3195
|
-
// the marks accumulate. A request whose projection exceeds the provider input budget
|
|
3196
|
-
// is rejected outright, so recovery must produce a window that fits: genuine overflow
|
|
3197
|
-
// -- the recovery lane, or an occupancy already past the serving budget -- fires
|
|
3198
|
-
// regardless of what it frees. Standing at the fence RATIO does not.
|
|
3199
1933
|
if (!overflowExempt &&
|
|
3200
1934
|
accounting.eligibleFreedBudgetShare < COMMIT_RECLAIM_FLOOR_SHARE) {
|
|
3201
1935
|
persistence.state = state;
|
|
@@ -3213,19 +1947,12 @@ export function registerActiveContext(pi: any, options: {
|
|
|
3213
1947
|
return null;
|
|
3214
1948
|
}
|
|
3215
1949
|
const bytesBefore = bytes(projectActiveContext(snapshot, state));
|
|
3216
|
-
// The guard protects an in-flight excursion, never at the cost of the session's
|
|
3217
|
-
// ability to send a request at all: above the pressure backstop the oldest guarded
|
|
3218
|
-
// marks are released. The guard is a protection with a WAIVER, which is why it
|
|
3219
|
-
// survived the fence-snapshot deletion: one protection, one owner, one waiver.
|
|
3220
1950
|
const guardWaiver = guardWaiverCount({
|
|
3221
1951
|
snapshot,
|
|
3222
1952
|
ratio: waiverRatio,
|
|
1953
|
+
boundary: trigger === "compaction-boundary",
|
|
3223
1954
|
guardedMarks: pendingMarks(state).filter((mark) =>
|
|
3224
1955
|
markTouchesCurrentTurn(state, mark, guarded)).length,
|
|
3225
|
-
// The starvation test asks the same question the commit will, against the same
|
|
3226
|
-
// snapshot the commit adjudicates against: does this epoch have work that does
|
|
3227
|
-
// not touch the open turn? Two snapshots gave two answers, and the one that
|
|
3228
|
-
// decided the commit was not the one being asked. There is one snapshot now.
|
|
3229
1956
|
otherApplicableMarks: pendingMarks(state).filter((mark) =>
|
|
3230
1957
|
!markTouchesCurrentTurn(state, mark, guarded) &&
|
|
3231
1958
|
markEligibility(snapshot, state, mark) === "eligible").length,
|
|
@@ -3238,17 +1965,7 @@ export function registerActiveContext(pi: any, options: {
|
|
|
3238
1965
|
guardCurrentTurn: true,
|
|
3239
1966
|
guardWaiver,
|
|
3240
1967
|
});
|
|
3241
|
-
|
|
3242
|
-
//
|
|
3243
|
-
// The folds this commit just landed are eligible roots the moment they exist, and
|
|
3244
|
-
// the law is about the count, not about which pass noticed it. Leaving them for the
|
|
3245
|
-
// next epoch is what let the pile stand between commits: measured 2026-08-10 (PT-3),
|
|
3246
|
-
// a commit that landed 26 tool folds left 32 visible roots up until the next band
|
|
3247
|
-
// crossing four cycles later. So the epoch re-reads its own count and parents what
|
|
3248
|
-
// it just made, inside the rewrite it has already paid for. No waiver here: a parent
|
|
3249
|
-
// this round cannot apply is held by the ordinary guard and waits, exactly like any
|
|
3250
|
-
// other fold. It converges because a parent is excluded from the count it came from.
|
|
3251
|
-
for (let round = 0; round < EPOCH_CONSOLIDATION_ROUNDS; round += 1) {
|
|
1968
|
+
for (;;) {
|
|
3252
1969
|
let closing = result.state;
|
|
3253
1970
|
for (const mark of consolidationMarks({ snapshot, state: closing, ordinal })) {
|
|
3254
1971
|
const addition = addPendingMark(closing, mark);
|
|
@@ -3263,9 +1980,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
3263
1980
|
guardCurrentTurn: true,
|
|
3264
1981
|
});
|
|
3265
1982
|
if (!closed.applied.length) break;
|
|
3266
|
-
// A mark the guard RETAINED is still pending, so the closing round sees it again
|
|
3267
|
-
// and reports it again; only the terminal refusals accumulate, and the last round
|
|
3268
|
-
// is the truth about what is still held.
|
|
3269
1983
|
result = {
|
|
3270
1984
|
...closed,
|
|
3271
1985
|
applied: [...result.applied, ...closed.applied],
|
|
@@ -3274,20 +1988,8 @@ export function registerActiveContext(pi: any, options: {
|
|
|
3274
1988
|
};
|
|
3275
1989
|
}
|
|
3276
1990
|
persistence.state = result.state;
|
|
3277
|
-
// The upgrade wedge: model briefs written since the last boundary replace their
|
|
3278
|
-
// deterministic ones HERE, inside the mutation this commit already pays for, before
|
|
3279
|
-
// anything measures what the commit did.
|
|
3280
|
-
const upgradedFolds = applyBriefUpgrades();
|
|
3281
|
-
const upgradeFailures = upgrades.failures;
|
|
3282
|
-
const upgradeError = upgrades.lastError;
|
|
3283
|
-
const upgradeCures = upgrades.cures;
|
|
3284
|
-
upgrades.failures = 0;
|
|
3285
|
-
upgrades.cures = 0;
|
|
3286
|
-
upgrades.lastError = null;
|
|
3287
1991
|
const bytesAfter = bytes(projectActiveContext(snapshot, persistence.state));
|
|
3288
1992
|
const freedBytes = Math.max(0, bytesBefore - bytesAfter);
|
|
3289
|
-
// The cost of the agent's pins, measured at the moment it matters: mass this
|
|
3290
|
-
// commit could not touch because protect holds it.
|
|
3291
1993
|
const pinHeld = protectedStaleMass(snapshot, result.state);
|
|
3292
1994
|
const commitEvent = emit("context.commit", {
|
|
3293
1995
|
trigger,
|
|
@@ -3295,19 +1997,12 @@ export function registerActiveContext(pi: any, options: {
|
|
|
3295
1997
|
reason: null,
|
|
3296
1998
|
eligible_freed_share: accounting.eligibleFreedBudgetShare,
|
|
3297
1999
|
reclaim_floor_share: COMMIT_RECLAIM_FLOOR_SHARE,
|
|
3298
|
-
// The two first-class dials: how much ONE event reclaims, and how long since the
|
|
3299
|
-
// last one. A trigger that frees too little re-fires immediately, and that shape
|
|
3300
|
-
// is only visible with both numbers on the same record.
|
|
3301
2000
|
target_freed_share: freeingTarget,
|
|
3302
2001
|
hysteresis_target_share: freeingTarget,
|
|
3303
2002
|
target_occupancy_share: thresholds.minTarget,
|
|
3304
2003
|
shortfall_share: Math.max(0, freeingTarget - accounting.eligibleFreedBudgetShare),
|
|
3305
2004
|
occupancy_tokens_before: usedTokens,
|
|
3306
2005
|
budget_tokens: budgetTokens,
|
|
3307
|
-
// The forward look, on the record beside the occupancy it was added to. A commit
|
|
3308
|
-
// that fired because one more turn would have spent the answer's reservation reads
|
|
3309
|
-
// differently from one that fired at the band top, and the difference is only
|
|
3310
|
-
// legible with the allowance printed.
|
|
3311
2006
|
expected_inflow_tokens: expectedWallInflowTokens(),
|
|
3312
2007
|
output_wall: outputWallDue(snapshot),
|
|
3313
2008
|
requests_since_previous: instrumentation.requests - instrumentation.lastMutationRequest,
|
|
@@ -3315,8 +2010,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
3315
2010
|
? null
|
|
3316
2011
|
: usedTokens - instrumentation.lastMutationTokens,
|
|
3317
2012
|
applied_marks: result.applied.length,
|
|
3318
|
-
// Refusal is TERMINAL only. A mark whose span was still fresh is deferred, not
|
|
3319
|
-
// refused: it is held and folds at the first commit after the span ages out.
|
|
3320
2013
|
refused_marks: result.refused.filter((mark) => !mark.retained).length,
|
|
3321
2014
|
deferred_marks: result.retained.length,
|
|
3322
2015
|
waived_marks: result.waived.length,
|
|
@@ -3328,28 +2021,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
3328
2021
|
consolidation_marks: consolidationAdded,
|
|
3329
2022
|
closing_consolidation_marks: closingAdded,
|
|
3330
2023
|
absorbed_wedges: wedges.absorbed.length,
|
|
3331
|
-
// The upgrade is a mutation of a fold whose `context.fold` record was written at
|
|
3332
|
-
// creation, so the stream would otherwise show a brief that silently changed. The
|
|
3333
|
-
// count, the ids, and the failures ride the boundary that carried them.
|
|
3334
|
-
brief_upgrades: upgradedFolds.length,
|
|
3335
|
-
brief_upgrade_ids: upgradedFolds.join(","),
|
|
3336
|
-
brief_upgrade_failures: upgradeFailures,
|
|
3337
|
-
// Briefs that needed a second ask before they met the contract. Beside the failures
|
|
3338
|
-
// rather than inside them: a cured brief is a brief that landed, and the two together
|
|
3339
|
-
// are how the brief cap gets judged against something other than intuition.
|
|
3340
|
-
brief_upgrade_cures: upgradeCures,
|
|
3341
|
-
brief_upgrade_error: upgradeError,
|
|
3342
|
-
// Everything still owed to a FOLD: queued, in flight, and (always zero once the
|
|
3343
|
-
// wedge above ran) written but uncarried. In flight is counted because the lane
|
|
3344
|
-
// holds several calls open and work nobody can see is work nobody bounds. Counted in
|
|
3345
|
-
// folds rather than calls because a call now carries a whole commit's spans, and the
|
|
3346
|
-
// question this answers is how many folds are still reading deterministic.
|
|
3347
|
-
brief_upgrades_waiting: upgrades.ready.length + upgrades.running.size +
|
|
3348
|
-
upgrades.queue.reduce((total, entry) => total + entry.members.length, 0),
|
|
3349
|
-
// The same work in the unit the lane's two constants actually bound: calls, queued
|
|
3350
|
-
// plus in flight. Reported beside the fold count so a backed-up lane cannot read as
|
|
3351
|
-
// a busy one, or the reverse.
|
|
3352
|
-
brief_upgrade_calls: upgrades.queue.length + new Set(upgrades.running.values()).size,
|
|
3353
2024
|
freed_bytes: freedBytes,
|
|
3354
2025
|
freed_tokens: estimatedTokens(freedBytes),
|
|
3355
2026
|
rewrite_tokens: accounting.rewriteTokens,
|
|
@@ -3362,39 +2033,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
3362
2033
|
instrumentation.mutationsSinceHandoff += 1;
|
|
3363
2034
|
instrumentation.lastMutationRequest = instrumentation.requests;
|
|
3364
2035
|
instrumentation.lastMutationTokens = usedTokens;
|
|
3365
|
-
// THE RESPONSE ATTRIBUTION. Whatever the gated round did, or that it did nothing,
|
|
3366
|
-
// is linked to the exposure it answered, and the exposure is consumed by the
|
|
3367
|
-
// commit whatever its trigger: a fence or user commit landing mid-round is still
|
|
3368
|
-
// the commit boundary the last-call announced. The carrier's disappearance rides
|
|
3369
|
-
// this commit's own rewrite.
|
|
3370
|
-
if (persistence.state.lastCall) {
|
|
3371
|
-
const lastCall = persistence.state.lastCall;
|
|
3372
|
-
const attribution = lastCallAttribution(lastCall, accounting.agentMarks);
|
|
3373
|
-
emit("context.response", {
|
|
3374
|
-
exposure_seq: lastCall.exposure,
|
|
3375
|
-
commit_seq: commitEvent.seq,
|
|
3376
|
-
trigger,
|
|
3377
|
-
outcome: attribution.responded ? "responded" : "silent",
|
|
3378
|
-
...attribution,
|
|
3379
|
-
});
|
|
3380
|
-
clearLastCall();
|
|
3381
|
-
}
|
|
3382
|
-
// A commit that drops occupancy back under a waypoint re-arms its notice: the next
|
|
3383
|
-
// upward crossing is a new event and earns a new one. The estimate is the same one
|
|
3384
|
-
// the receipt reports: measured occupancy before, less what this commit freed.
|
|
3385
|
-
if (persistence.state.notices && usedTokens !== null && budgetTokens > 0) {
|
|
3386
|
-
const postOccupancy = Math.max(0, usedTokens - estimatedTokens(freedBytes)) / budgetTokens;
|
|
3387
|
-
const fired = persistence.state.notices.fired.filter((share) => postOccupancy >= share);
|
|
3388
|
-
if (fired.length !== persistence.state.notices.fired.length) {
|
|
3389
|
-
persistence.state = { ...persistence.state, notices: { ...persistence.state.notices, fired } };
|
|
3390
|
-
}
|
|
3391
|
-
}
|
|
3392
|
-
// The rider: at most ONE action prompt per fold epoch, composed from post-commit
|
|
3393
|
-
// numbers, persisted as literal bytes, delivered beside the receipt inside the
|
|
3394
|
-
// rewrite this commit already paid for. The epoch key is the commit event's own
|
|
3395
|
-
// stream sequence: strictly monotone, one per applied commit, so refold-only
|
|
3396
|
-
// epochs get their rider too and no two epochs can ever share a key. The next
|
|
3397
|
-
// epoch REPLACES the text; nothing stacks.
|
|
3398
2036
|
const riderEpoch = commitEvent.seq;
|
|
3399
2037
|
if (persistence.state.rider?.epoch !== riderEpoch) {
|
|
3400
2038
|
const postAccounting = markAccounting(snapshot, persistence.state);
|
|
@@ -3406,7 +2044,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
3406
2044
|
const riderText = contextRiderText({
|
|
3407
2045
|
toolName,
|
|
3408
2046
|
brandNoun,
|
|
3409
|
-
suggestion: deliverSurfacing(snapshot, "rider"),
|
|
3410
2047
|
pendingAgentMarks: postAccounting.agentMarks,
|
|
3411
2048
|
eligibleMarks: postAccounting.eligibleMarks,
|
|
3412
2049
|
freedTokens: postAccounting.freedTokens,
|
|
@@ -3432,17 +2069,12 @@ export function registerActiveContext(pi: any, options: {
|
|
|
3432
2069
|
const fold = persistence.state.folds.find((item) => item.id === applied.foldId);
|
|
3433
2070
|
if (fold?.kind === "tool-result") foldedToolResults += 1;
|
|
3434
2071
|
else foldedSpans += 1;
|
|
3435
|
-
// A reclaimed peek copy names what it copied. Derived from the transcript rather
|
|
3436
|
-
// than carried on the mark, so the pointer is the same whichever mechanism claimed
|
|
3437
|
-
// the copy: the exposure's reclaim marking, the commit's, or the doorless ladder.
|
|
3438
2072
|
const peekedSources = fold?.kind === "tool-result"
|
|
3439
2073
|
? peekedSourceFoldIds(snapshot, flattenFoldRefs(fold, persistence.state))
|
|
3440
2074
|
: null;
|
|
3441
2075
|
emit("context.fold", {
|
|
3442
2076
|
commit_seq: commitEvent.seq,
|
|
3443
2077
|
fold_id: applied.foldId,
|
|
3444
|
-
// The MARK this fold came from. The stream is now the only account of what a
|
|
3445
|
-
// commit applied, so it has to name both ends of that mapping.
|
|
3446
2078
|
mark_id: applied.id,
|
|
3447
2079
|
fold_kind: fold?.kind ?? applied.mark,
|
|
3448
2080
|
origin: applied.origin,
|
|
@@ -3452,9 +2084,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
3452
2084
|
brief_provenance: fold ? foldProvenance(fold, persistence.state).kind : null,
|
|
3453
2085
|
});
|
|
3454
2086
|
}
|
|
3455
|
-
// Queued AFTER the applications above, so no fold can be committed and upgraded on
|
|
3456
|
-
// one boundary: the generator has not run yet, and the wait is the mechanism.
|
|
3457
|
-
queueBriefUpgrades(snapshot, state, result.applied.map((applied) => applied.foldId));
|
|
3458
2087
|
for (const wedge of wedges.absorbed) {
|
|
3459
2088
|
emit("context.absorb", {
|
|
3460
2089
|
commit_seq: commitEvent.seq,
|
|
@@ -3467,9 +2096,7 @@ export function registerActiveContext(pi: any, options: {
|
|
|
3467
2096
|
threshold_tokens: MAX_WEDGE_ABSORB_TOKENS,
|
|
3468
2097
|
});
|
|
3469
2098
|
}
|
|
3470
|
-
|
|
3471
|
-
// applied/refused list alone, so the epoch reports its own composition.
|
|
3472
|
-
return {
|
|
2099
|
+
const epoch = {
|
|
3473
2100
|
trigger,
|
|
3474
2101
|
applied: result.applied,
|
|
3475
2102
|
refused: result.refused,
|
|
@@ -3479,13 +2106,9 @@ export function registerActiveContext(pi: any, options: {
|
|
|
3479
2106
|
peekMarks: peekAdded,
|
|
3480
2107
|
topUpMarks: topUpAdded,
|
|
3481
2108
|
consolidationMarks: consolidationAdded,
|
|
3482
|
-
// Parents formed over the folds THIS commit landed. They were never in
|
|
3483
|
-
// `pendingMarks`, so an epoch's arithmetic is applied = pending + closing.
|
|
3484
2109
|
closingMarks: closingAdded,
|
|
3485
2110
|
absorbedWedges: wedges.absorbed.length,
|
|
3486
2111
|
absorbed: wedges.absorbed,
|
|
3487
|
-
// What the commit actually folded, the way an agent counts it, so the receipt can
|
|
3488
|
-
// report impact instead of an opaque mark total.
|
|
3489
2112
|
foldedSpans,
|
|
3490
2113
|
foldedToolResults,
|
|
3491
2114
|
occupancyTokensBefore: usedTokens,
|
|
@@ -3516,125 +2139,18 @@ export function registerActiveContext(pi: any, options: {
|
|
|
3516
2139
|
requestsSincePreviousCommit: instrumentation.requests - instrumentation.lastMutationRequest,
|
|
3517
2140
|
instrumentation: ledgerSummary(instrumentation.ledger),
|
|
3518
2141
|
};
|
|
2142
|
+
// The receipt is the LAST statement of the commit, and stays that way: harness gate
|
|
2143
|
+
// 54 reads a commit that reported applied marks and delivered no receipt as one that
|
|
2144
|
+
// threw partway and left its folds in the stream and nowhere else. The rung used to
|
|
2145
|
+
// write it because the rung used to own the epoch; the boundary owns it now.
|
|
2146
|
+
ladder.lastAutomaticAction = { kind: "epoch-commit", foldIds: [], sourceIds: [], epoch };
|
|
2147
|
+
ladder.pendingContextNote =
|
|
2148
|
+
`A commit epoch applied ${result.applied.length} pending mark(s) in one rewrite; ` +
|
|
2149
|
+
"exact evidence remains expandable.";
|
|
2150
|
+
noteAutomaticReceipt(snapshot, ladder.lastAutomaticAction, epoch);
|
|
2151
|
+
return epoch;
|
|
3519
2152
|
};
|
|
3520
2153
|
|
|
3521
|
-
/** The two curation signals, measured against the ONE serving budget. */
|
|
3522
|
-
const measuredCurationSignals = (snapshot: ActiveContextSnapshot): CurationSignals => {
|
|
3523
|
-
const capacity = servingCapacity(snapshot.contextWindow);
|
|
3524
|
-
curation.lastSignals = curationSignals({
|
|
3525
|
-
snapshot,
|
|
3526
|
-
state: persistence.state!,
|
|
3527
|
-
usedTokens: capacity.usedTokens,
|
|
3528
|
-
budgetTokens: capacity.budgetTokens,
|
|
3529
|
-
window: capacity.window,
|
|
3530
|
-
charsPerToken: projectionCharsPerToken(),
|
|
3531
|
-
eligibleFolds: markAccounting(snapshot, persistence.state!).eligibleMarks,
|
|
3532
|
-
});
|
|
3533
|
-
return curation.lastSignals;
|
|
3534
|
-
};
|
|
3535
|
-
|
|
3536
|
-
/**
|
|
3537
|
-
* The silent early commit. The two signals decide, the reclaim floor stops a commit
|
|
3538
|
-
* that would free nothing, and the hysteresis stops a plateau from reading as a
|
|
3539
|
-
* stream of events. What is gone is only the announcement in front of it.
|
|
3540
|
-
*/
|
|
3541
|
-
/**
|
|
3542
|
-
* THE trigger, with its one piece of hysteresis.
|
|
3543
|
-
*
|
|
3544
|
-
* Occupancy at or above maxTarget, and then the reopen latch: a window that is still
|
|
3545
|
-
* full because the last commit could not reach the target is the SAME event, not a
|
|
3546
|
-
* new one, so a second commit waits for at least a reclaim floor of genuinely new
|
|
3547
|
-
* eligible mass. Without it a parked window fires a commit per pass and every one of
|
|
3548
|
-
* them rebuilds the prefix for crumbs.
|
|
3549
|
-
*/
|
|
3550
|
-
const commitTriggerDue = (snapshot: ActiveContextSnapshot, ratio: number | null): boolean => {
|
|
3551
|
-
if (!persistence.state) return false;
|
|
3552
|
-
// The output wall outranks the quiet-runtime line rather than sitting under it. A
|
|
3553
|
-
// window one inflow step from spending the answer's whole reservation is past
|
|
3554
|
-
// economising, and the band top is not the thing it is about to cross.
|
|
3555
|
-
//
|
|
3556
|
-
// ONE COMMIT PER CROSSING, NOT ONE PER PASS. The wall is a level, and a window that
|
|
3557
|
-
// sits on it sits there for many passes: releasing the reopen latch on each of them
|
|
3558
|
-
// fires a commit per pass, which is the prefix-rewrite churn the latch exists to
|
|
3559
|
-
// prevent and which the quiet cadence is built around. Measured on gate 52's fixture:
|
|
3560
|
-
// the first wall commit applied 44 marks and correctly retained all ten of the open
|
|
3561
|
-
// turn, and the two that followed it applied nothing, rewrote the prefix anyway, and
|
|
3562
|
-
// dragged the guard's starvation waiver in on a session that had just made progress.
|
|
3563
|
-
// So the crossing is the event. While the wall stays due the ordinary hysteresis
|
|
3564
|
-
// decides, which is what makes a second commit wait for genuinely new foldable mass.
|
|
3565
|
-
const wall = outputWallDue(snapshot);
|
|
3566
|
-
if (!wall) curation.wallEpisodeOpen = false;
|
|
3567
|
-
if (!wall && !epochCommitDue(snapshot, ratio)) return false;
|
|
3568
|
-
measuredCurationSignals(snapshot);
|
|
3569
|
-
if (wall && !curation.wallEpisodeOpen) {
|
|
3570
|
-
curation.wallEpisodeOpen = true;
|
|
3571
|
-
curation.reopenBaselineShare = null;
|
|
3572
|
-
return true;
|
|
3573
|
-
}
|
|
3574
|
-
// The latch is an ECONOMY rule, and the fence is not latched: a request whose
|
|
3575
|
-
// projection exceeds the provider input budget is rejected outright, so recovery
|
|
3576
|
-
// must produce a window that fits.
|
|
3577
|
-
// A request that does not fit gets its commit whether or not the previous one left
|
|
3578
|
-
// new eligible mass behind; holding it back would be spending the session's
|
|
3579
|
-
// transmissibility to save a prefix rewrite.
|
|
3580
|
-
if (typeof ratio === "number" && Number.isFinite(ratio) && ratio >= hardFenceRatio(snapshot)) {
|
|
3581
|
-
curation.reopenBaselineShare = null;
|
|
3582
|
-
return true;
|
|
3583
|
-
}
|
|
3584
|
-
if (curation.reopenBaselineShare !== null) {
|
|
3585
|
-
const eligibleShare = markAccounting(snapshot, persistence.state).eligibleFreedBudgetShare;
|
|
3586
|
-
if (eligibleShare - curation.reopenBaselineShare < COMMIT_RECLAIM_FLOOR_SHARE) {
|
|
3587
|
-
// A HOLD THAT SAYS SO.
|
|
3588
|
-
//
|
|
3589
|
-
// This was a bare `return false`, and it is the last silent deferral on the
|
|
3590
|
-
// commit path: every other one names itself and reports its numbers. In
|
|
3591
|
-
// sol-20260812 rep 1 the trigger was due for twelve straight ordinals while
|
|
3592
|
-
// occupancy climbed to 94 percent of budget, and the stream carries no record of
|
|
3593
|
-
// what held it, so the stall can be proven but not explained. Emitted on every
|
|
3594
|
-
// held pass rather than once per episode: under-reporting is the defect being
|
|
3595
|
-
// fixed, the record is a stream record that never reaches the projection, and
|
|
3596
|
-
// the volume is bounded by passes above the band top.
|
|
3597
|
-
const capacity = servingCapacity(snapshot.contextWindow);
|
|
3598
|
-
emit("context.commit", {
|
|
3599
|
-
trigger: "band-top",
|
|
3600
|
-
deferred: true,
|
|
3601
|
-
reason: "reopen-latch",
|
|
3602
|
-
applied_marks: 0,
|
|
3603
|
-
eligible_freed_share: eligibleShare,
|
|
3604
|
-
reopen_baseline_share: curation.reopenBaselineShare,
|
|
3605
|
-
reclaim_floor_share: COMMIT_RECLAIM_FLOOR_SHARE,
|
|
3606
|
-
occupancy_tokens: capacity.usedTokens,
|
|
3607
|
-
expected_inflow_tokens: expectedWallInflowTokens(),
|
|
3608
|
-
budget_tokens: capacity.budgetTokens,
|
|
3609
|
-
window_tokens: snapshot.contextWindow,
|
|
3610
|
-
});
|
|
3611
|
-
return false;
|
|
3612
|
-
}
|
|
3613
|
-
curation.reopenBaselineShare = null;
|
|
3614
|
-
}
|
|
3615
|
-
return true;
|
|
3616
|
-
};
|
|
3617
|
-
|
|
3618
|
-
/**
|
|
3619
|
-
* Condition (a) of the reopen hysteresis, evaluated on EVERY context pass. A window
|
|
3620
|
-
* that fell back under the trigger has ended its cycle, and the next crossing is a
|
|
3621
|
-
* fresh event. This cannot live inside the trigger: the quiet passes that matter are
|
|
3622
|
-
* exactly the ones too far below it to evaluate it.
|
|
3623
|
-
*/
|
|
3624
|
-
const clearCommitLatchBelowTrigger = (): void => {
|
|
3625
|
-
if (curation.reopenBaselineShare === null) return;
|
|
3626
|
-
const capacity = servingCapacity(lifecycle.latestSnapshot?.contextWindow ?? null);
|
|
3627
|
-
if (capacity.usedTokens === null || capacity.budgetTokens <= 0) return;
|
|
3628
|
-
if (capacity.usedTokens / capacity.budgetTokens < thresholds.maxTarget) {
|
|
3629
|
-
curation.reopenBaselineShare = null;
|
|
3630
|
-
}
|
|
3631
|
-
};
|
|
3632
|
-
|
|
3633
|
-
/**
|
|
3634
|
-
* One receipt per automatic context action, in the window and in the durable stream.
|
|
3635
|
-
* Informatory, never exhortative: it says what happened, what it freed, and which
|
|
3636
|
-
* verbs correct it. A mark that changed nothing is not an event and gets no receipt.
|
|
3637
|
-
*/
|
|
3638
2154
|
const noteAutomaticReceipt = (
|
|
3639
2155
|
snapshot: ActiveContextSnapshot,
|
|
3640
2156
|
action: Record<string, unknown>,
|
|
@@ -3657,7 +2173,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
3657
2173
|
foldsCommitted: epoch ? Number(epoch.appliedMarks ?? 0) : 0,
|
|
3658
2174
|
foldsCreated: foldIds || (epoch ? Number(epoch.appliedMarks ?? 0) : 0),
|
|
3659
2175
|
freedTokens,
|
|
3660
|
-
// The impact, in the same unit the budget is stated in.
|
|
3661
2176
|
occupancyBefore,
|
|
3662
2177
|
occupancyAfter: occupancyBefore === null ? null : Math.max(0, occupancyBefore - freedTokens),
|
|
3663
2178
|
spansFolded: epoch ? Number(epoch.foldedSpans ?? 0) : foldIds,
|
|
@@ -3671,234 +2186,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
3671
2186
|
}));
|
|
3672
2187
|
};
|
|
3673
2188
|
|
|
3674
|
-
/**
|
|
3675
|
-
* Threshold notices, evaluated on every pass the ladder runs on. One waypoint fires
|
|
3676
|
-
* once per upward crossing of the measured occupancy; delivery is an append-once
|
|
3677
|
-
* carrier plus its stream record, and re-arming belongs to the commit that drops
|
|
3678
|
-
* occupancy back under the line. Default on; `guidance.thresholdNotices: false`
|
|
3679
|
-
* registers the runtime with the waypoints silent.
|
|
3680
|
-
*/
|
|
3681
|
-
const deliverThresholdNotices = (snapshot: ActiveContextSnapshot): boolean => {
|
|
3682
|
-
if (!guidance.thresholdNotices || !persistence.state) return false;
|
|
3683
|
-
const capacity = servingCapacity(snapshot.contextWindow);
|
|
3684
|
-
if (capacity.usedTokens === null || capacity.budgetTokens <= 0) return false;
|
|
3685
|
-
const occupancy = capacity.usedTokens / capacity.budgetTokens;
|
|
3686
|
-
const current = persistence.state.notices ?? { fired: [], ring: [] };
|
|
3687
|
-
let fired = current.fired;
|
|
3688
|
-
let ring = current.ring;
|
|
3689
|
-
let changed = false;
|
|
3690
|
-
for (const share of THRESHOLD_NOTICE_SHARES) {
|
|
3691
|
-
if (occupancy < share || fired.includes(share)) continue;
|
|
3692
|
-
const text = thresholdNoticeText({
|
|
3693
|
-
share,
|
|
3694
|
-
occupancyTokens: capacity.usedTokens,
|
|
3695
|
-
budgetTokens: capacity.budgetTokens,
|
|
3696
|
-
maxTarget: thresholds.maxTarget,
|
|
3697
|
-
toolName,
|
|
3698
|
-
brandNoun,
|
|
3699
|
-
});
|
|
3700
|
-
fired = [...fired, share];
|
|
3701
|
-
ring = [...ring, { share, ordinal: markOrdinal(snapshot), text }];
|
|
3702
|
-
if (ring.length > MAX_THRESHOLD_NOTICES) ring = ring.slice(ring.length - MAX_THRESHOLD_NOTICES);
|
|
3703
|
-
changed = true;
|
|
3704
|
-
emit("context.notice", {
|
|
3705
|
-
share,
|
|
3706
|
-
occupancy,
|
|
3707
|
-
occupancy_tokens: capacity.usedTokens,
|
|
3708
|
-
budget_tokens: capacity.budgetTokens,
|
|
3709
|
-
max_target: thresholds.maxTarget,
|
|
3710
|
-
chars: text.length,
|
|
3711
|
-
});
|
|
3712
|
-
}
|
|
3713
|
-
if (changed) persistence.state = { ...persistence.state, notices: { fired, ring } };
|
|
3714
|
-
return changed;
|
|
3715
|
-
};
|
|
3716
|
-
|
|
3717
|
-
/**
|
|
3718
|
-
* The same selection, read rather than delivered: what the next carrier WOULD say,
|
|
3719
|
-
* with the ledger that decides whether it says anything at all.
|
|
3720
|
-
*/
|
|
3721
|
-
const statusSurfacing = (snapshot: ActiveContextSnapshot): Record<string, unknown> => {
|
|
3722
|
-
const ledger = persistence.state ? surfacingLedger(persistence.state) : [];
|
|
3723
|
-
if (!persistence.state) return { slate: [], line: null, ledger, silenced: [] };
|
|
3724
|
-
const selection = selectSurfacingSlate({
|
|
3725
|
-
state: persistence.state,
|
|
3726
|
-
snapshot,
|
|
3727
|
-
toolName,
|
|
3728
|
-
ordinal: markOrdinal(snapshot),
|
|
3729
|
-
});
|
|
3730
|
-
return {
|
|
3731
|
-
slate: selection.slate.map((suggestion) => ({
|
|
3732
|
-
id: suggestion.id,
|
|
3733
|
-
route: suggestion.route,
|
|
3734
|
-
contentScore: suggestion.contentScore,
|
|
3735
|
-
briefScore: suggestion.briefScore,
|
|
3736
|
-
margin: suggestion.margin,
|
|
3737
|
-
slot: suggestion.slot,
|
|
3738
|
-
})),
|
|
3739
|
-
line: surfacingSlateText({ slate: selection.slate, queryTerms: selection.queryTerms, brandNoun }),
|
|
3740
|
-
considered: selection.considered,
|
|
3741
|
-
divergent: selection.divergent,
|
|
3742
|
-
suppressed: selection.suppressed,
|
|
3743
|
-
intentTerms: selection.queryTerms.size,
|
|
3744
|
-
contentHit: SURFACING_CONTENT_HIT,
|
|
3745
|
-
briefHit: SURFACING_BRIEF_HIT,
|
|
3746
|
-
divergenceMargin: SURFACING_DIVERGENCE_MARGIN,
|
|
3747
|
-
slateSize: SURFACING_SLATE_SIZE,
|
|
3748
|
-
silenced: [...surfacingSilenced(ledger)],
|
|
3749
|
-
ledger,
|
|
3750
|
-
};
|
|
3751
|
-
};
|
|
3752
|
-
|
|
3753
|
-
/**
|
|
3754
|
-
* THE SURFACING SLATE, delivered.
|
|
3755
|
-
*
|
|
3756
|
-
* The agent cannot peek a fold it does not know to want, so one line names the fold
|
|
3757
|
-
* whose stored content matches the task in hand when its brief does not. Where it
|
|
3758
|
-
* lands is the whole economics: only carriers that ride a rewrite the runtime is
|
|
3759
|
-
* already paying for (the pre-commit last call and the post-commit rider) plus the
|
|
3760
|
-
* on-demand status surface, which the agent asked for. The per-request ephemeral
|
|
3761
|
-
* carrier stays dead; it cost 21.9% of every input token in rep 21 and nothing else
|
|
3762
|
-
* about this build changes that verdict.
|
|
3763
|
-
*
|
|
3764
|
-
* One suggestion per delivery point, and most delivery points get none. Every
|
|
3765
|
-
* delivery first resolves whatever offers have run out of window, so the ignore that
|
|
3766
|
-
* silences a fold is recorded before the next selection reads the ledger.
|
|
3767
|
-
*/
|
|
3768
|
-
const deliverSurfacing = (snapshot: ActiveContextSnapshot, carrier: string): string | null => {
|
|
3769
|
-
if (!persistence.state) return null;
|
|
3770
|
-
const ordinal = markOrdinal(snapshot);
|
|
3771
|
-
const resolved = resolveSurfacing({ state: persistence.state, snapshot, ordinal });
|
|
3772
|
-
persistence.state = resolved.state;
|
|
3773
|
-
for (const transition of resolved.transitions) {
|
|
3774
|
-
emit("context.outcome", {
|
|
3775
|
-
fold_id: transition.id,
|
|
3776
|
-
from_outcome: transition.from,
|
|
3777
|
-
outcome: transition.to,
|
|
3778
|
-
outcome_ordinal: transition.ordinal,
|
|
3779
|
-
window_ordinals: SURFACING_OUTCOME_WINDOW_ORDINALS,
|
|
3780
|
-
});
|
|
3781
|
-
}
|
|
3782
|
-
const selection = selectSurfacingSlate({ state: persistence.state, snapshot, toolName, ordinal });
|
|
3783
|
-
const suggestion = selection.slate[0];
|
|
3784
|
-
if (!suggestion) return null;
|
|
3785
|
-
const text = surfacingSlateText({
|
|
3786
|
-
slate: selection.slate,
|
|
3787
|
-
queryTerms: selection.queryTerms,
|
|
3788
|
-
brandNoun,
|
|
3789
|
-
});
|
|
3790
|
-
if (!text) return null;
|
|
3791
|
-
persistence.state = issueSurfacing(persistence.state, suggestion.id, ordinal);
|
|
3792
|
-
emit("context.suggestion", {
|
|
3793
|
-
carrier,
|
|
3794
|
-
fold_id: suggestion.id,
|
|
3795
|
-
content_score: suggestion.contentScore,
|
|
3796
|
-
brief_score: suggestion.briefScore,
|
|
3797
|
-
margin: suggestion.margin,
|
|
3798
|
-
content_hit: SURFACING_CONTENT_HIT,
|
|
3799
|
-
brief_hit: SURFACING_BRIEF_HIT,
|
|
3800
|
-
divergence_margin: SURFACING_DIVERGENCE_MARGIN,
|
|
3801
|
-
slot: suggestion.slot,
|
|
3802
|
-
slate_size: SURFACING_SLATE_SIZE,
|
|
3803
|
-
fold_depth: suggestion.depth,
|
|
3804
|
-
considered: selection.considered,
|
|
3805
|
-
divergent: selection.divergent,
|
|
3806
|
-
suppressed: selection.suppressed,
|
|
3807
|
-
intent_terms: selection.queryTerms.size,
|
|
3808
|
-
chars: text.length,
|
|
3809
|
-
});
|
|
3810
|
-
return text;
|
|
3811
|
-
};
|
|
3812
|
-
|
|
3813
|
-
/**
|
|
3814
|
-
* THE PRE-COMMIT LAST-CALL GATE. When the band-top trigger fires, the commit defers
|
|
3815
|
-
* exactly one gated round behind one exposure of the ruled prompt; then it proceeds
|
|
3816
|
-
* with whatever marks exist. A request whose projection exceeds the provider input
|
|
3817
|
-
* budget is rejected outright, so recovery must produce a window that fits: fence
|
|
3818
|
-
* pressure, the recovery lane, or an occupancy already past the serving budget
|
|
3819
|
-
* commit NOW, and the user
|
|
3820
|
-
* command never reaches this gate because explicit intent is its own answer. The
|
|
3821
|
-
* round is measured from DELIVERY: the commit proceeds only on a context pass whose
|
|
3822
|
-
* ordinal is past the pass that rendered the exposure, which is exactly one agent
|
|
3823
|
-
* response between prompt and rewrite.
|
|
3824
|
-
*/
|
|
3825
|
-
const lastCallVerdict = (
|
|
3826
|
-
snapshot: ActiveContextSnapshot,
|
|
3827
|
-
pressure: number | null,
|
|
3828
|
-
phase: string,
|
|
3829
|
-
): "expose" | "hold" | "proceed" => {
|
|
3830
|
-
const state = persistence.state!;
|
|
3831
|
-
const capacity = servingCapacity(snapshot.contextWindow);
|
|
3832
|
-
const fenceLevel = typeof pressure === "number" && Number.isFinite(pressure) &&
|
|
3833
|
-
pressure >= hardFenceRatio(snapshot);
|
|
3834
|
-
// The output wall deliberately does NOT proceed here. It buys the crossing one inflow
|
|
3835
|
-
// step of lead, and the gated round is what that lead is for: the agent still gets its
|
|
3836
|
-
// one bounded turn to curate before a rewrite, which is a contract the wall has no
|
|
3837
|
-
// business cancelling. If the round overruns and occupancy passes the budget outright,
|
|
3838
|
-
// the condition below is what proceeds, on the pass that builds the next request.
|
|
3839
|
-
const overBudget = capacity.usedTokens !== null && capacity.budgetTokens > 0 &&
|
|
3840
|
-
capacity.usedTokens > capacity.budgetTokens;
|
|
3841
|
-
if (fenceLevel || overBudget || curation.recoveryAttempts > 0 || curation.pendingRejection) {
|
|
3842
|
-
return "proceed";
|
|
3843
|
-
}
|
|
3844
|
-
if (!state.lastCall) return "expose";
|
|
3845
|
-
if (phase !== "context") return "hold";
|
|
3846
|
-
const delivery = curation.lastCallDelivery;
|
|
3847
|
-
if (!delivery || delivery.exposure !== state.lastCall.exposure) return "hold";
|
|
3848
|
-
return markOrdinal(snapshot) > delivery.ordinal ? "proceed" : "hold";
|
|
3849
|
-
};
|
|
3850
|
-
|
|
3851
|
-
const exposeLastCall = (snapshot: ActiveContextSnapshot): void => {
|
|
3852
|
-
if (!persistence.state) return;
|
|
3853
|
-
// THE PEEK RECLAIM IS MARKED HERE, NOT ONLY AT THE COMMIT.
|
|
3854
|
-
//
|
|
3855
|
-
// A peek copy is reclaimed at the next commit by contract, and a contract the agent
|
|
3856
|
-
// cannot see until after it executes is not one it can veto. The exposure is the one
|
|
3857
|
-
// bounded round before the rewrite, so minting the reclaim marks now puts the pending
|
|
3858
|
-
// disposal in front of the agent while a pin still changes the outcome: a pinned mark
|
|
3859
|
-
// waits instead of applying, and lifting the pin hands the copy to a later commit.
|
|
3860
|
-
const exposureOrdinal = markOrdinal(snapshot);
|
|
3861
|
-
let peekReclaims = 0;
|
|
3862
|
-
for (const mark of ephemeralPeekMarks({ snapshot, state: persistence.state, ordinal: exposureOrdinal })) {
|
|
3863
|
-
const addition = addPendingMark(persistence.state, mark);
|
|
3864
|
-
if (addition.added) { persistence.state = addition.state; peekReclaims += 1; }
|
|
3865
|
-
}
|
|
3866
|
-
const signals = measuredCurationSignals(snapshot);
|
|
3867
|
-
const remainder = unmarkedRemainder(snapshot, persistence.state, projectionCharsPerToken());
|
|
3868
|
-
const accounting = markAccounting(snapshot, persistence.state);
|
|
3869
|
-
const text = lastCallText({
|
|
3870
|
-
signals,
|
|
3871
|
-
unmarked: { spans: remainder.spans, tokens: remainder.tokens },
|
|
3872
|
-
pendingMarks: accounting.pending,
|
|
3873
|
-
peekReclaims,
|
|
3874
|
-
suggestion: deliverSurfacing(snapshot, "lastcall"),
|
|
3875
|
-
toolName,
|
|
3876
|
-
brandNoun,
|
|
3877
|
-
});
|
|
3878
|
-
const record = emit("context.lastcall", {
|
|
3879
|
-
occupancy: signals.occupancy,
|
|
3880
|
-
max_target: thresholds.maxTarget,
|
|
3881
|
-
occupancy_tokens: signals.occupancyTokens,
|
|
3882
|
-
budget_tokens: signals.budgetTokens,
|
|
3883
|
-
unmarked_stale_spans: remainder.spans,
|
|
3884
|
-
unmarked_stale_tokens: remainder.tokens,
|
|
3885
|
-
pending_marks: accounting.pending,
|
|
3886
|
-
pending_agent_marks: accounting.agentMarks,
|
|
3887
|
-
peek_marks: peekReclaims,
|
|
3888
|
-
chars: text.length,
|
|
3889
|
-
});
|
|
3890
|
-
persistence.state = {
|
|
3891
|
-
...persistence.state,
|
|
3892
|
-
lastCall: {
|
|
3893
|
-
exposure: record.seq,
|
|
3894
|
-
ordinal: markOrdinal(snapshot),
|
|
3895
|
-
contextCalls: curation.contextCalls,
|
|
3896
|
-
agentMarks: accounting.agentMarks,
|
|
3897
|
-
text,
|
|
3898
|
-
},
|
|
3899
|
-
};
|
|
3900
|
-
};
|
|
3901
|
-
|
|
3902
2189
|
const applyAutomaticRung = async (
|
|
3903
2190
|
snapshot: ActiveContextSnapshot,
|
|
3904
2191
|
ratio: number,
|
|
@@ -3906,18 +2193,9 @@ export function registerActiveContext(pi: any, options: {
|
|
|
3906
2193
|
toolOnly?: boolean;
|
|
3907
2194
|
waiverRatio?: number;
|
|
3908
2195
|
} = {},
|
|
3909
|
-
phase = "context",
|
|
3910
2196
|
): Promise<Record<string, unknown> | null> => {
|
|
3911
2197
|
if (!persistence.state || ladder.automaticFailure || ladder.preparing) return null;
|
|
3912
|
-
const rungSelectionOptions = {
|
|
3913
|
-
toolOnly: rungOptions.toolOnly,
|
|
3914
|
-
summarizerAvailable: Boolean(options.summarizeContextSpan),
|
|
3915
|
-
failedPreparationIds: ladder.failedPreparations,
|
|
3916
|
-
};
|
|
3917
|
-
// The ladder decides but does not move bytes. Marks already pending are decisions
|
|
3918
|
-
// already made: excluding the evidence they cover makes every eligible turn choose
|
|
3919
|
-
// NEW stale content, so marks accumulate with stale growth instead of re-proposing
|
|
3920
|
-
// one stale batch.
|
|
2198
|
+
const rungSelectionOptions = { toolOnly: rungOptions.toolOnly };
|
|
3921
2199
|
const markLadderSelection = (): Record<string, unknown> | null => {
|
|
3922
2200
|
if (!persistence.state) return null;
|
|
3923
2201
|
const decision = selectAutomaticRung(snapshot, persistence.state, ratio, {
|
|
@@ -3948,246 +2226,25 @@ export function registerActiveContext(pi: any, options: {
|
|
|
3948
2226
|
};
|
|
3949
2227
|
return ladder.lastAutomaticAction;
|
|
3950
2228
|
};
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
//
|
|
3954
|
-
//
|
|
3955
|
-
|
|
3956
|
-
// the whole prefix cache, so the cadence has to be the fewest commits that still
|
|
3957
|
-
// keep the window inside its budget. Occupancy reaching maxTarget is the whole
|
|
3958
|
-
// automatic condition. Below it the runtime is quiet and marks accumulate free;
|
|
3959
|
-
// at it, one commit folds down toward minTarget, and the gap between the two lines
|
|
3960
|
-
// is what makes the spacing of events structural rather than hoped for.
|
|
3961
|
-
//
|
|
3962
|
-
// Two other arms used to reach this line and both are deleted. The eligible-share
|
|
3963
|
-
// arm fired 164 commits from ordinal 17 in rep 15 on marks that had not earned a
|
|
3964
|
-
// rewrite, and it could fire below maxTarget, which is the quiet law it sat under.
|
|
3965
|
-
// The stale-mass AND-condition guarded an ANNOUNCEMENT that no longer exists: a
|
|
3966
|
-
// commit is never announced, so a commit with nothing eligible now applies nothing
|
|
3967
|
-
// and reports it rather than being suppressed in advance.
|
|
3968
|
-
//
|
|
3969
|
-
// The trigger decides early and says nothing. Nothing warns that a commit is
|
|
3970
|
-
// coming, because a warning has to arrive before the event it warns about and
|
|
3971
|
-
// therefore has to break a prefix nothing else was breaking.
|
|
3972
|
-
// Evaluated on EVERY pass the ladder runs on, which is where the pressure backstop
|
|
3973
|
-
// always sat. Crossing the band top is a property of occupancy, not of which
|
|
3974
|
-
// lifecycle hook happened to notice it, and a trigger that waits for the next
|
|
3975
|
-
// projection pass is a window that keeps climbing while it waits.
|
|
3976
|
-
const noticesChanged = deliverThresholdNotices(snapshot);
|
|
3977
|
-
let lastCallChanged = false;
|
|
3978
|
-
const commitDue = commitTriggerDue(snapshot, ratio);
|
|
3979
|
-
if (commitDue) {
|
|
3980
|
-
const verdict = lastCallVerdict(snapshot, rungOptions.waiverRatio ?? ratio, phase);
|
|
3981
|
-
if (verdict === "expose") {
|
|
3982
|
-
exposeLastCall(snapshot);
|
|
3983
|
-
lastCallChanged = true;
|
|
3984
|
-
} else if (verdict === "proceed") {
|
|
3985
|
-
epoch = await runCommitEpoch(
|
|
3986
|
-
snapshot,
|
|
3987
|
-
"band-top",
|
|
3988
|
-
true,
|
|
3989
|
-
rungOptions.waiverRatio ?? ratio,
|
|
3990
|
-
);
|
|
3991
|
-
// Latch the eligible share this commit left behind, so the next crossing waits
|
|
3992
|
-
// for genuinely new foldable mass rather than for the same window to still be
|
|
3993
|
-
// full. The band top is a LINE, and a window parked on it is one event.
|
|
3994
|
-
if (persistence.state) {
|
|
3995
|
-
curation.reopenBaselineShare =
|
|
3996
|
-
markAccounting(snapshot, persistence.state).eligibleFreedBudgetShare;
|
|
3997
|
-
}
|
|
3998
|
-
}
|
|
3999
|
-
// "hold": the exposure is out and the round is still the agent's. Marks keep
|
|
4000
|
-
// accumulating below; nothing commits and nothing re-exposes.
|
|
4001
|
-
} else if (persistence.state.lastCall) {
|
|
4002
|
-
// The crossing died without its commit (a user rescue or expiry dropped
|
|
4003
|
-
// occupancy back under the trigger), so the exposure lapses: attributed,
|
|
4004
|
-
// cleared, and the next crossing is a fresh event with a fresh exposure.
|
|
4005
|
-
const capacity = servingCapacity(snapshot.contextWindow);
|
|
4006
|
-
if (capacity.usedTokens !== null && capacity.budgetTokens > 0 &&
|
|
4007
|
-
capacity.usedTokens / capacity.budgetTokens < thresholds.maxTarget) {
|
|
4008
|
-
const lastCall = persistence.state.lastCall;
|
|
4009
|
-
const attribution = lastCallAttribution(
|
|
4010
|
-
lastCall,
|
|
4011
|
-
markAccounting(snapshot, persistence.state).agentMarks,
|
|
4012
|
-
);
|
|
4013
|
-
emit("context.response", {
|
|
4014
|
-
exposure_seq: lastCall.exposure,
|
|
4015
|
-
commit_seq: null,
|
|
4016
|
-
trigger: null,
|
|
4017
|
-
outcome: "lapsed",
|
|
4018
|
-
...attribution,
|
|
4019
|
-
});
|
|
4020
|
-
clearLastCall();
|
|
4021
|
-
lastCallChanged = true;
|
|
4022
|
-
}
|
|
4023
|
-
}
|
|
4024
|
-
// An inline rung is free only INSIDE a rewrite the epoch already paid for. Below
|
|
4025
|
-
// the threshold no commit ran, and an epoch that applied NOTHING -- every mark
|
|
4026
|
-
// held back by an open turn or by ineligibility -- paid for no rewrite either,
|
|
4027
|
-
// so folding inline there is a fresh single-fold rewrite of its own. Measured
|
|
4028
|
-
// 2026-08-06 (rep 10): the current-turn guard retained all nine marks every turn
|
|
4029
|
-
// while the inline rung folded one batch per turn anyway, 52 single-fold
|
|
4030
|
-
// rewrites that left the prefix cache share at zero. Below a paid rewrite the
|
|
4031
|
-
// ladder MARKS, so the decisions batch into the first commit that can apply them.
|
|
4032
|
-
inlineRungs = Boolean(epoch) && Number(epoch?.appliedMarks ?? 0) > 0;
|
|
4033
|
-
if (!inlineRungs) {
|
|
4034
|
-
const marked = markLadderSelection();
|
|
4035
|
-
if (marked) {
|
|
4036
|
-
if (epoch) ladder.lastAutomaticAction = { ...marked, epoch };
|
|
4037
|
-
return ladder.lastAutomaticAction;
|
|
4038
|
-
}
|
|
4039
|
-
// A carrier change is durable state even when no mark and no epoch moved: the
|
|
4040
|
-
// exposure and the notices must survive a reload, so the pass returns an action
|
|
4041
|
-
// for the transaction to persist. Never a receipt: a carrier reports itself.
|
|
4042
|
-
if (!epoch && (lastCallChanged || noticesChanged)) {
|
|
4043
|
-
ladder.lastAutomaticAction = { kind: "carrier", foldIds: [], sourceIds: [], sourceBytesSaved: 0 };
|
|
4044
|
-
return ladder.lastAutomaticAction;
|
|
4045
|
-
}
|
|
4046
|
-
}
|
|
4047
|
-
// Evidence a pending mark already covers is not the inline rung's to fold: a mark
|
|
4048
|
-
// the commit RETAINED (the open turn's own reads) would otherwise be folded here
|
|
4049
|
-
// by the very pass that just protected it.
|
|
4050
|
-
//
|
|
4051
|
-
// The open turn's keys are excluded DIRECTLY, not just via the marks that claim
|
|
4052
|
-
// them. Claiming is not protection: once a commit applies every pending mark the
|
|
4053
|
-
// claim set is empty, and the inline rung then folds the open turn's oldest read
|
|
4054
|
-
// with nothing left to stop it. That is the rep-8 shape, and byte freshness does
|
|
4055
|
-
// not reach it -- the first read of a long excursion sits outside the fresh tail
|
|
4056
|
-
// while the turn that gathered it is still running. It became reachable when the
|
|
4057
|
-
// quiet cadence started landing a commit on the same context pass, which is what
|
|
4058
|
-
// turns the inline rung on in the first place.
|
|
4059
|
-
const selection = inlineRungs
|
|
4060
|
-
? selectAutomaticRung(snapshot, persistence.state, ratio, {
|
|
4061
|
-
...rungSelectionOptions,
|
|
4062
|
-
claimed: new Set([
|
|
4063
|
-
...claimedRefKeys(persistence.state),
|
|
4064
|
-
...currentTurnRefKeys(snapshot),
|
|
4065
|
-
]),
|
|
4066
|
-
claimedFoldIds: markedFoldIds(persistence.state),
|
|
4067
|
-
})
|
|
4068
|
-
: null;
|
|
4069
|
-
// Above the commit threshold we are already inside the epoch's rewrite turn, so
|
|
4070
|
-
// every rung applies inline: the extra rung costs at most one more invalidation
|
|
4071
|
-
// in a turn that has already paid one. Restricting this to prepared chapters made
|
|
4072
|
-
// the refold and consolidation rungs unreachable in epoch mode, which drove a
|
|
4073
|
-
// session whose reducible bytes sit in expanded folds into the hard fence.
|
|
4074
|
-
const applicable = selection;
|
|
4075
|
-
if (!applicable || applicable.kind === "chapter-prepare") {
|
|
4076
|
-
if (!epoch || !persistence.state) return null;
|
|
4077
|
-
ladder.pendingContextNote =
|
|
4078
|
-
`A commit epoch applied ${(epoch.applied as unknown[]).length} pending mark(s) in one rewrite; ` +
|
|
4079
|
-
"exact evidence remains expandable.";
|
|
4080
|
-
ladder.lastAutomaticAction = { kind: "epoch-commit", foldIds: [], sourceIds: [], epoch };
|
|
4081
|
-
noteAutomaticReceipt(snapshot, ladder.lastAutomaticAction, epoch);
|
|
4082
|
-
return ladder.lastAutomaticAction;
|
|
4083
|
-
}
|
|
4084
|
-
const projectedBytesBefore = bytes(projectActiveContext(snapshot, persistence.state));
|
|
4085
|
-
let action: Record<string, unknown> | null = null;
|
|
4086
|
-
if (applicable.kind === "prepared-chapter" && persistence.state.prepared) {
|
|
4087
|
-
const error = preparedFoldError({
|
|
4088
|
-
prepared: persistence.state.prepared,
|
|
4089
|
-
snapshot,
|
|
4090
|
-
state: persistence.state,
|
|
4091
|
-
generation: lifecycle.generation,
|
|
4092
|
-
ratio,
|
|
4093
|
-
});
|
|
4094
|
-
if (error) {
|
|
4095
|
-
persistence.state = clearPrepared(persistence.state);
|
|
4096
|
-
} else {
|
|
4097
|
-
const id = persistence.state.prepared.id;
|
|
4098
|
-
const sourceIds = persistence.state.prepared.sourceRefs.map((ref) => ref.entryId);
|
|
4099
|
-
persistence.state = commitPreparedFold({
|
|
4100
|
-
prepared: persistence.state.prepared,
|
|
4101
|
-
snapshot,
|
|
4102
|
-
state: persistence.state,
|
|
4103
|
-
generation: lifecycle.generation,
|
|
4104
|
-
});
|
|
4105
|
-
action = { kind: "chapter-fold", foldIds: [id], sourceIds };
|
|
4106
|
-
ladder.pendingContextNote = `A coherent stale chapter was folded under ${id}; exact evidence remains expandable.`;
|
|
4107
|
-
}
|
|
4108
|
-
} else if (applicable.kind === "tool") {
|
|
4109
|
-
cancelPreparation();
|
|
4110
|
-
const tool = applicable.candidate;
|
|
4111
|
-
const id = await commitDeterministicCandidate(snapshot, tool, automaticToolBrief(snapshot, tool));
|
|
4112
|
-
action = {
|
|
4113
|
-
kind: "tool-fold",
|
|
4114
|
-
foldIds: [id],
|
|
4115
|
-
sourceIds: tool.sourceRefs.map((ref) => ref.entryId),
|
|
4116
|
-
};
|
|
4117
|
-
ladder.pendingContextNote = `${tool.sourceRefs.length} stale completed read-only tool result(s) were folded.`;
|
|
4118
|
-
} else if (applicable.kind === "refold") {
|
|
4119
|
-
cancelPreparation();
|
|
4120
|
-
persistence.state = setFoldProjectionState(persistence.state, applicable.foldId, "folded");
|
|
4121
|
-
action = { kind: "refold", foldIds: [applicable.foldId] };
|
|
4122
|
-
ladder.pendingContextNote = `Stale expanded fold ${applicable.foldId} returned to its identical placeholder.`;
|
|
4123
|
-
} else if (applicable.kind === "consolidation") {
|
|
4124
|
-
cancelPreparation();
|
|
4125
|
-
const consolidation = applicable.candidate;
|
|
4126
|
-
const id = await commitDeterministicCandidate(
|
|
4127
|
-
snapshot,
|
|
4128
|
-
consolidation,
|
|
4129
|
-
deterministicConsolidationBrief(consolidation, persistence.state),
|
|
4130
|
-
);
|
|
4131
|
-
action = {
|
|
4132
|
-
kind: "consolidation",
|
|
4133
|
-
foldIds: [id],
|
|
4134
|
-
sourceIds: consolidation.sourceRefs.map((ref) => ref.entryId),
|
|
4135
|
-
};
|
|
4136
|
-
ladder.pendingContextNote =
|
|
4137
|
-
`Stale folded chapters were consolidated under ${id}; every child remains expandable.`;
|
|
4138
|
-
} else if (applicable.kind === "chapter") {
|
|
4139
|
-
const chapter = applicable.candidate;
|
|
4140
|
-
// Bite-sized by construction: an oversized coherent chapter becomes sequential
|
|
4141
|
-
// bounded folds, each with its own brief, split only at closed unit boundaries.
|
|
4142
|
-
const spanChars = candidateSpanChars(snapshot, persistence.state, chapter);
|
|
4143
|
-
const parts = splitCandidateBySize(snapshot, persistence.state, chapter);
|
|
4144
|
-
const foldIds: string[] = [];
|
|
4145
|
-
for (const part of parts) {
|
|
4146
|
-
foldIds.push(await commitDeterministicCandidate(
|
|
4147
|
-
snapshot,
|
|
4148
|
-
part,
|
|
4149
|
-
deterministicChapterCandidateBrief(snapshot, part),
|
|
4150
|
-
));
|
|
4151
|
-
}
|
|
4152
|
-
action = {
|
|
4153
|
-
kind: "chapter-fold",
|
|
4154
|
-
foldIds,
|
|
4155
|
-
sourceIds: chapter.sourceRefs.map((ref) => ref.entryId),
|
|
4156
|
-
...(parts.length > 1 ? { splitFolds: parts.length, splitFromChars: spanChars } : {}),
|
|
4157
|
-
};
|
|
4158
|
-
if (parts.length > 1) {
|
|
4159
|
-
emit("context.split", {
|
|
4160
|
-
source: "ladder",
|
|
4161
|
-
span_chars: spanChars,
|
|
4162
|
-
parts: parts.length,
|
|
4163
|
-
cap_chars: MAX_FOLD_SPAN_CHARS,
|
|
4164
|
-
fold_ids: foldIds.join(","),
|
|
4165
|
-
});
|
|
4166
|
-
}
|
|
4167
|
-
ladder.pendingContextNote = parts.length > 1
|
|
4168
|
-
? `A ${spanChars}-char stale chapter was folded as ${parts.length} bite-sized folds ` +
|
|
4169
|
-
`(${foldIds.join(", ")}); exact evidence remains expandable.`
|
|
4170
|
-
: `A coherent stale chapter was folded under ${foldIds[0]}; exact evidence remains expandable.`;
|
|
4171
|
-
}
|
|
4172
|
-
if ((!action && !epoch) || !persistence.state) return null;
|
|
4173
|
-
const projectedBytesAfter = bytes(projectActiveContext(snapshot, persistence.state));
|
|
4174
|
-
ladder.lastAutomaticAction = {
|
|
4175
|
-
...(action ?? { kind: "epoch-commit", foldIds: [], sourceIds: [] }),
|
|
4176
|
-
...(epoch ? { epoch } : {}),
|
|
4177
|
-
sourceBytesSaved: Math.max(0, projectedBytesBefore - projectedBytesAfter),
|
|
4178
|
-
};
|
|
4179
|
-
noteAutomaticReceipt(snapshot, ladder.lastAutomaticAction, epoch);
|
|
4180
|
-
return ladder.lastAutomaticAction;
|
|
2229
|
+
// MARKING ONLY. Every mutation the rung used to perform inline -- a prepared chapter,
|
|
2230
|
+
// a tool fold, a refold, a consolidation, a chapter split -- now travels as a pending
|
|
2231
|
+
// mark and lands at the boundary, which is the one point the projection changes.
|
|
2232
|
+
// WHAT the rung decides is unchanged; WHEN it takes effect is.
|
|
2233
|
+
return markLadderSelection();
|
|
4181
2234
|
};
|
|
4182
|
-
|
|
2235
|
+
/**
|
|
2236
|
+
* The one durable transaction. Whatever it wraps either lands with its state persisted
|
|
2237
|
+
* or leaves the state it entered with, and a failure suspends folding by name rather
|
|
2238
|
+
* than being retried quietly. Marking and committing both go through it, which is why
|
|
2239
|
+
* a commit that vanishes at persistence (gate 122) and a suspension that says nothing
|
|
2240
|
+
* (gate 123) are single properties rather than one per caller.
|
|
2241
|
+
*/
|
|
2242
|
+
const runAutomaticTransaction = async (
|
|
4183
2243
|
snapshot: ActiveContextSnapshot,
|
|
4184
2244
|
ratio: number,
|
|
4185
2245
|
ctx: any,
|
|
4186
2246
|
phase: string,
|
|
4187
|
-
|
|
4188
|
-
toolOnly?: boolean;
|
|
4189
|
-
waiverRatio?: number;
|
|
4190
|
-
} = {},
|
|
2247
|
+
operation: () => Promise<Record<string, unknown> | null>,
|
|
4191
2248
|
): Promise<Record<string, unknown> | null> => {
|
|
4192
2249
|
if (!persistence.state || !measurements.lastProviderMeasurement ||
|
|
4193
2250
|
!durableProviderMeasurementMatches(measurements.lastProviderMeasurement)) return null;
|
|
@@ -4205,7 +2262,7 @@ export function registerActiveContext(pi: any, options: {
|
|
|
4205
2262
|
const transientAtEntry = captureTransient();
|
|
4206
2263
|
let action: Record<string, unknown> | null = null;
|
|
4207
2264
|
try {
|
|
4208
|
-
action = await
|
|
2265
|
+
action = await operation();
|
|
4209
2266
|
if (action) await persist(ctx);
|
|
4210
2267
|
if (action) ladder.boundaryFailure = null;
|
|
4211
2268
|
return action;
|
|
@@ -4216,17 +2273,24 @@ export function registerActiveContext(pi: any, options: {
|
|
|
4216
2273
|
persistence.state = stateAtEntry;
|
|
4217
2274
|
restoreTransient(transientAtEntry);
|
|
4218
2275
|
}
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
phase,
|
|
4222
|
-
ctx,
|
|
4223
|
-
key,
|
|
4224
|
-
stateCommitted ? "state-committed" : recordOnly ? "record-only" : "none",
|
|
4225
|
-
);
|
|
2276
|
+
const disposition = stateCommitted ? "state-committed" : recordOnly ? "record-only" : "none";
|
|
2277
|
+
suspendAutomatic(error, phase, ctx, key, disposition);
|
|
4226
2278
|
updateStatus(ctx);
|
|
4227
2279
|
return stateCommitted ? action : null;
|
|
4228
2280
|
}
|
|
4229
2281
|
};
|
|
2282
|
+
const queueAutomatic = (
|
|
2283
|
+
snapshot: ActiveContextSnapshot,
|
|
2284
|
+
ratio: number,
|
|
2285
|
+
ctx: any,
|
|
2286
|
+
phase: string,
|
|
2287
|
+
operation: () => Promise<Record<string, unknown> | null>,
|
|
2288
|
+
): Promise<Record<string, unknown> | null> => {
|
|
2289
|
+
const queued = ladder.actionQueue.then(() =>
|
|
2290
|
+
runAutomaticTransaction(snapshot, ratio, ctx, phase, operation));
|
|
2291
|
+
ladder.actionQueue = queued.catch(() => undefined);
|
|
2292
|
+
return queued;
|
|
2293
|
+
};
|
|
4230
2294
|
const attemptAutomaticRung = (
|
|
4231
2295
|
snapshot: ActiveContextSnapshot,
|
|
4232
2296
|
ratio: number,
|
|
@@ -4236,21 +2300,24 @@ export function registerActiveContext(pi: any, options: {
|
|
|
4236
2300
|
toolOnly?: boolean;
|
|
4237
2301
|
waiverRatio?: number;
|
|
4238
2302
|
} = {},
|
|
4239
|
-
): Promise<Record<string, unknown> | null> =>
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
2303
|
+
): Promise<Record<string, unknown> | null> => queueAutomatic(
|
|
2304
|
+
snapshot, ratio, ctx, phase, () => applyAutomaticRung(snapshot, ratio, rungOptions),
|
|
2305
|
+
);
|
|
2306
|
+
/**
|
|
2307
|
+
* A COMMIT, THROUGH THE SAME TRANSACTION AND THE SAME QUEUE. Two callers reach it: the
|
|
2308
|
+
* compaction boundary, which is the ordinary one, and the projection fence, which is
|
|
2309
|
+
* the emergency one. The fence cannot wait for a boundary because the request it is
|
|
2310
|
+
* holding does not fit, so it commits in place; that is the one exception, and it is
|
|
2311
|
+
* the exception that already existed.
|
|
2312
|
+
*/
|
|
2313
|
+
const attemptAutomaticCommit = (
|
|
2314
|
+
snapshot: ActiveContextSnapshot,
|
|
2315
|
+
ctx: any,
|
|
2316
|
+
trigger: string,
|
|
2317
|
+
waiverRatio: number | null,
|
|
2318
|
+
): Promise<Record<string, unknown> | null> => queueAutomatic(
|
|
2319
|
+
snapshot, waiverRatio ?? 1, ctx, trigger, () => runCommitEpoch(snapshot, trigger, true, waiverRatio),
|
|
2320
|
+
);
|
|
4254
2321
|
const projectionCandidates = (ctx: any): Array<Record<string, unknown>> => {
|
|
4255
2322
|
if (lifecycle.shuttingDown || !persistence.state) return [];
|
|
4256
2323
|
try {
|
|
@@ -4259,12 +2326,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
4259
2326
|
return [];
|
|
4260
2327
|
}
|
|
4261
2328
|
};
|
|
4262
|
-
// A same-session start/tree reload is a projection-generation mutation.
|
|
4263
|
-
// Queue it behind every context authority → preparation → commit →
|
|
4264
|
-
// projection transaction, then serialize the actual load with the action
|
|
4265
|
-
// queue. Appending to actionQueue only after the context queue drains is
|
|
4266
|
-
// deliberate: a running context may itself need actionQueue to commit its
|
|
4267
|
-
// final-rung chapter, so capturing both queues up front would deadlock.
|
|
4268
2329
|
const enqueueLifecycleLoad = async (ctx: any): Promise<void> => {
|
|
4269
2330
|
const operation = lifecycle.contextQueue.then(() => {
|
|
4270
2331
|
const loadOperation = ladder.actionQueue.then(async () => {
|
|
@@ -4290,11 +2351,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
4290
2351
|
}
|
|
4291
2352
|
};
|
|
4292
2353
|
|
|
4293
|
-
/**
|
|
4294
|
-
* State the resolved serving budget once, at startup, before anything reads it. A
|
|
4295
|
-
* budget nobody can see in the stream is how rep 15 spent a whole run on a 272,000
|
|
4296
|
-
* descriptor without a single record saying so.
|
|
4297
|
-
*/
|
|
4298
2354
|
const recordResolvedCapacity = (ctx: any): void => {
|
|
4299
2355
|
const capacity = currentCapacity(ctx);
|
|
4300
2356
|
emit("context.capacity", {
|
|
@@ -4357,14 +2413,10 @@ export function registerActiveContext(pi: any, options: {
|
|
|
4357
2413
|
const attributionChanged = async (_event: unknown, ctx: any): Promise<void> => {
|
|
4358
2414
|
lifecycle.generation += 1;
|
|
4359
2415
|
cancelPreparation();
|
|
4360
|
-
cancelBriefUpgrades();
|
|
4361
2416
|
if (persistence.state?.prepared) persistence.state = clearPrepared(persistence.state);
|
|
4362
2417
|
measurements.latestRatio = null;
|
|
4363
2418
|
measurements.lastProviderMeasurement = null;
|
|
4364
2419
|
measurements.wallInflowSteps.length = 0;
|
|
4365
|
-
// A different model or thinking level is a different tokenizer, so counts taken
|
|
4366
|
-
// under the old one describe nothing here. The generation the anchor carries would
|
|
4367
|
-
// already refuse it; dropping it releases the projection text it held as well.
|
|
4368
2420
|
measurements.projectionAnchor = null;
|
|
4369
2421
|
nativeCompaction.lastThresholdDecision = null;
|
|
4370
2422
|
updateStatus(ctx);
|
|
@@ -4393,7 +2445,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
4393
2445
|
const handleContext = async (event: { messages: unknown[] }, ctx: any) => {
|
|
4394
2446
|
if (lifecycle.shuttingDown) return { messages: event.messages };
|
|
4395
2447
|
beginMutationPass();
|
|
4396
|
-
clearCommitLatchBelowTrigger();
|
|
4397
2448
|
if (!persistence.state) persistence.state = emptyActiveContextState(ctx.sessionManager.getSessionId());
|
|
4398
2449
|
lifecycle.latestSnapshot = null;
|
|
4399
2450
|
lifecycle.latestSnapshotError = null;
|
|
@@ -4433,12 +2484,7 @@ export function registerActiveContext(pi: any, options: {
|
|
|
4433
2484
|
measurements.latestRatio = contextUsageRatio(observedMeasurement);
|
|
4434
2485
|
if (durableProviderMeasurementMatches(observedMeasurement) && measurements.latestRatio !== null) {
|
|
4435
2486
|
measurementStateChanged = accountAnchoredMeasurement(observedMeasurement);
|
|
4436
|
-
// Calibrate BEFORE the new measurement becomes the previous one: the inflow
|
|
4437
|
-
// step is the difference between them.
|
|
4438
2487
|
noteProjectionCalibration(observedMeasurement);
|
|
4439
|
-
// Anchor before this pass builds anything: `previousText` is still the
|
|
4440
|
-
// projection the provider just counted, and the projection built below is the
|
|
4441
|
-
// one that either extends it or does not.
|
|
4442
2488
|
noteProviderProjectionAnchor(observedMeasurement);
|
|
4443
2489
|
measurements.lastProviderMeasurement = observedMeasurement;
|
|
4444
2490
|
startPreparation(snapshot, measurements.latestRatio, ctx);
|
|
@@ -4478,27 +2524,14 @@ export function registerActiveContext(pi: any, options: {
|
|
|
4478
2524
|
updateStatus(ctx);
|
|
4479
2525
|
return { messages: event.messages };
|
|
4480
2526
|
}
|
|
4481
|
-
// A projection larger than the serving budget is never transmitted, whatever the
|
|
4482
|
-
// last measured ratio says about the request before it.
|
|
4483
2527
|
const budgeted = await enforceProjectionBudget(snapshot, projected, ctx);
|
|
4484
2528
|
projected = budgeted.projected;
|
|
4485
|
-
// Every projection this process BUILDS is a size reference for the calibration,
|
|
4486
|
-
// including one the fence aborted. Recording only sent projections locks an
|
|
4487
|
-
// already-large session out of calibrating at all: its first projection aborts
|
|
4488
|
-
// under the uncalibrated constant, so nothing is ever sent, so nothing ever
|
|
4489
|
-
// measures the ratio, so it aborts forever.
|
|
4490
2529
|
const reading = projectedTokenReading(projected);
|
|
4491
2530
|
measurements.lastProjectedChars = reading.chars;
|
|
4492
2531
|
measurements.lastProjectedEstimate = reading.tokens;
|
|
4493
2532
|
measurements.lastProjectedEstimateBasis = reading.basis;
|
|
4494
2533
|
measurements.lastProjectedSizeTokens = Math.ceil(reading.chars / projectionCharsPerToken());
|
|
4495
2534
|
measurements.lastProjectedEstimateCalibrated = measurements.projectionCalibrations.length > 0;
|
|
4496
|
-
// An aborted request still returns the PROJECTION. Handing back the raw branch
|
|
4497
|
-
// instead makes the aborted turn the largest message list the session ever
|
|
4498
|
-
// produced -- the corpus, folds and all -- which is the exact inverse of what a
|
|
4499
|
-
// fence is for, and it is what an abort looked like in rep12: 3,952,934 chars
|
|
4500
|
-
// against a 1,322,385-char projection of the same session. It is not noted as a
|
|
4501
|
-
// cache observation, because it was never sent.
|
|
4502
2535
|
if (budgeted.aborted) {
|
|
4503
2536
|
updateStatus(ctx);
|
|
4504
2537
|
return { messages: projected };
|
|
@@ -4550,8 +2583,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
4550
2583
|
!durableProviderMeasurementMatches(measurement) ||
|
|
4551
2584
|
measuredRatio === null) return;
|
|
4552
2585
|
const measurementStateChanged = accountAnchoredMeasurement(measurement);
|
|
4553
|
-
// The provider accepted a request: whatever the recovery lane spent belongs to an
|
|
4554
|
-
// inflow that is now behind us, and the cap resets for the next one.
|
|
4555
2586
|
curation.recoveryAttempts = 0;
|
|
4556
2587
|
curation.pendingRejection = null;
|
|
4557
2588
|
noteWallInflow(measurement);
|
|
@@ -4593,7 +2624,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
4593
2624
|
preservedShare: instrumentation.lastPreservedShare,
|
|
4594
2625
|
});
|
|
4595
2626
|
if (observation) {
|
|
4596
|
-
// The join key: this is where the stream meets provider-side telemetry.
|
|
4597
2627
|
emit("context.usage", {
|
|
4598
2628
|
provider: ownValue(message, "provider") ?? null,
|
|
4599
2629
|
model: ownValue(message, "model") ?? null,
|
|
@@ -4627,18 +2657,9 @@ export function registerActiveContext(pi: any, options: {
|
|
|
4627
2657
|
);
|
|
4628
2658
|
} catch (error) {
|
|
4629
2659
|
suspendAutomatic(error, "message-end", ctx);
|
|
4630
|
-
try { updateStatus(ctx); } catch {
|
|
2660
|
+
try { updateStatus(ctx); } catch { }
|
|
4631
2661
|
}
|
|
4632
2662
|
});
|
|
4633
|
-
/*
|
|
4634
|
-
* There was an `after_provider_response` handler here that treated `status >= 400` as
|
|
4635
|
-
* the rejection signal. It could never fire on the case it was written for. Every
|
|
4636
|
-
* provider adapter calls `onResponse` exactly once and only AFTER the request promise
|
|
4637
|
-
* resolves, so a 4xx rejects the promise and control never reaches that call at all
|
|
4638
|
-
* (verified across all seven adapters in pi-ai 0.83.0). `pendingRejection` was
|
|
4639
|
-
* therefore never set in production and the recovery branch of the projection budget
|
|
4640
|
-
* never ran. It is set by the rollback lane now, from an event that does fire.
|
|
4641
|
-
*/
|
|
4642
2663
|
pi.on("turn_end", async (_event: unknown, ctx: any) => {
|
|
4643
2664
|
beginMutationPass();
|
|
4644
2665
|
if (lifecycle.shuttingDown || !persistence.state || !persistence.persisted) return;
|
|
@@ -4662,7 +2683,7 @@ export function registerActiveContext(pi: any, options: {
|
|
|
4662
2683
|
}
|
|
4663
2684
|
suspendAutomatic(error, "turn-end", ctx);
|
|
4664
2685
|
}
|
|
4665
|
-
try { updateStatus(ctx); } catch {
|
|
2686
|
+
try { updateStatus(ctx); } catch { }
|
|
4666
2687
|
});
|
|
4667
2688
|
pi.on("session_before_tree", (event: Record<string, unknown>) => {
|
|
4668
2689
|
const preparation = ownValue(event, "preparation");
|
|
@@ -4670,15 +2691,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
4670
2691
|
return ownValue(preparation, "userWantsSummary") === true ? { cancel: true } : undefined;
|
|
4671
2692
|
});
|
|
4672
2693
|
|
|
4673
|
-
/**
|
|
4674
|
-
* pi's own overflow classifier, borrowed rather than re-implemented.
|
|
4675
|
-
*
|
|
4676
|
-
* `isContextOverflow` owns the provider-message patterns AND the silent cases (usage
|
|
4677
|
-
* past the window with a clean stop, the truncating `length` stop). Re-stating that
|
|
4678
|
-
* pattern set here would drift on the first provider that reworded its 400. The
|
|
4679
|
-
* bare specifier is tried first, for a deployment where pi-ai is hoisted; the second
|
|
4680
|
-
* form reaches pi's own nested copy, which is where a default install puts it.
|
|
4681
|
-
*/
|
|
4682
2694
|
const resolveOverflowClassifier = async (): Promise<void> => {
|
|
4683
2695
|
if (rollback.classifier || rollback.classifierSource === "unresolved") return;
|
|
4684
2696
|
const host = "@earendil-works/pi-coding-agent";
|
|
@@ -4690,7 +2702,7 @@ export function registerActiveContext(pi: any, options: {
|
|
|
4690
2702
|
if (at >= 0) {
|
|
4691
2703
|
specifiers.push(`${entry.slice(0, at + marker.length)}node_modules/@earendil-works/pi-ai/dist/compat.js`);
|
|
4692
2704
|
}
|
|
4693
|
-
} catch {
|
|
2705
|
+
} catch { }
|
|
4694
2706
|
for (const specifier of specifiers) {
|
|
4695
2707
|
try {
|
|
4696
2708
|
const module = await import(specifier) as Record<string, unknown>;
|
|
@@ -4699,7 +2711,7 @@ export function registerActiveContext(pi: any, options: {
|
|
|
4699
2711
|
rollback.classifierSource = specifier;
|
|
4700
2712
|
return;
|
|
4701
2713
|
}
|
|
4702
|
-
} catch {
|
|
2714
|
+
} catch { }
|
|
4703
2715
|
}
|
|
4704
2716
|
rollback.classifierSource = "unresolved";
|
|
4705
2717
|
};
|
|
@@ -4719,10 +2731,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
4719
2731
|
}
|
|
4720
2732
|
};
|
|
4721
2733
|
|
|
4722
|
-
/**
|
|
4723
|
-
* The disarmed answer, and the only one: say so on the record and leave the session
|
|
4724
|
-
* behaving exactly as it did before the lane existed.
|
|
4725
|
-
*/
|
|
4726
2734
|
const refuseRollback = (reason: string, trigger: string, ctx: any): void => {
|
|
4727
2735
|
emit("context.rollback", {
|
|
4728
2736
|
trigger,
|
|
@@ -4748,22 +2756,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
4748
2756
|
);
|
|
4749
2757
|
};
|
|
4750
2758
|
|
|
4751
|
-
/**
|
|
4752
|
-
* The fallback trigger, and the honest limit of it.
|
|
4753
|
-
*
|
|
4754
|
-
* `session_before_compact` never fires for a deployment that turned auto-compaction
|
|
4755
|
-
* off, so the primary trigger is deaf there. This is the second detector: pi's own
|
|
4756
|
-
* classifier over the assistant message at `message_end`, which is where the error
|
|
4757
|
-
* message first becomes visible. It records the episode and, if no compaction event
|
|
4758
|
-
* claims it, reports it.
|
|
4759
|
-
*
|
|
4760
|
-
* It does NOT roll back, and the reason is mechanical rather than cautious. The
|
|
4761
|
-
* pre-strip this lane depends on happens INSIDE `_checkCompaction`, which is exactly
|
|
4762
|
-
* the code an auto-compaction-disabled deployment never reaches, so on this path
|
|
4763
|
-
* agent state still carries the oversized window plus the error message. A bare
|
|
4764
|
-
* `branch()` there would leave the tree and agent state disagreeing, which is the
|
|
4765
|
-
* failure mode the whole design exists to avoid. Held, and reported as held.
|
|
4766
|
-
*/
|
|
4767
2759
|
const noteOverflowAtMessageEnd = (message: unknown, ctx: any): void => {
|
|
4768
2760
|
if (!rollback.classifier || !message || typeof message !== "object") return;
|
|
4769
2761
|
const window = budgetWindowFor(ctx) ?? DEFAULT_CONTEXT_WINDOW;
|
|
@@ -4774,10 +2766,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
4774
2766
|
rollback.pendingOverflow = { at: currentOrdinal(), entryId: null };
|
|
4775
2767
|
};
|
|
4776
2768
|
|
|
4777
|
-
/**
|
|
4778
|
-
* An overflow that no compaction event claimed: the auto-compaction-disabled shape.
|
|
4779
|
-
* Loud, terminal, and never silent.
|
|
4780
|
-
*/
|
|
4781
2769
|
const reportUnclaimedOverflow = (ctx: any): void => {
|
|
4782
2770
|
if (!rollback.pendingOverflow) return;
|
|
4783
2771
|
rollback.pendingOverflow = null;
|
|
@@ -4790,20 +2778,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
4790
2778
|
);
|
|
4791
2779
|
};
|
|
4792
2780
|
|
|
4793
|
-
/**
|
|
4794
|
-
* ROLLBACK, COMMIT, REPLAY.
|
|
4795
|
-
*
|
|
4796
|
-
* pi has already stripped the trailing error message from agent state by the time
|
|
4797
|
-
* this fires, so agent state IS the rolled-back window and all that remains is to
|
|
4798
|
-
* move the session leaf to match. The label goes on FIRST, because a label appends at
|
|
4799
|
-
* the current leaf and advances it, so applied after the branch it would mark the
|
|
4800
|
-
* surviving path instead of the abandoned one.
|
|
4801
|
-
*
|
|
4802
|
-
* The commit deliberately does NOT happen here. The retried request runs its own
|
|
4803
|
-
* `context` pass, and that pass folds and commits before the payload is built. A
|
|
4804
|
-
* commit here would adjudicate a snapshot taken outside a projection pass and then be
|
|
4805
|
-
* evaluated again by the retry, which is two mutations for one episode.
|
|
4806
|
-
*/
|
|
4807
2781
|
const rebuiltMessageCount = (manager: Record<string, any>): number | null => {
|
|
4808
2782
|
try {
|
|
4809
2783
|
const rebuilt = manager.buildSessionContext?.();
|
|
@@ -4828,9 +2802,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
4828
2802
|
return;
|
|
4829
2803
|
}
|
|
4830
2804
|
const branchEntries = ownValue(event, "branchEntries");
|
|
4831
|
-
// Copied, not aliased. `getBranch()` hands back the live array and the branch call
|
|
4832
|
-
// below truncates it in place, so a handler holding the original reference watches
|
|
4833
|
-
// the entries it is still reasoning about disappear underneath it.
|
|
4834
2805
|
const entries = Array.isArray(branchEntries) ? [...branchEntries] : [];
|
|
4835
2806
|
const errorEntry = findOverflowErrorEntry(entries);
|
|
4836
2807
|
if (!errorEntry) {
|
|
@@ -4842,7 +2813,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
4842
2813
|
const sessionMessagesBefore = rebuiltMessageCount(manager);
|
|
4843
2814
|
const abandoned = entries.slice(errorEntry.index);
|
|
4844
2815
|
const rolledBackBytes = bytes(abandoned.flatMap((entry: any) => sessionEntryMessages(entry) ?? []));
|
|
4845
|
-
// Lineage first. The abandoned path stays in the tree, and it says why it was left.
|
|
4846
2816
|
try {
|
|
4847
2817
|
manager.appendLabelChange(errorEntry.id, `${entryTypePrefix} overflow rollback`);
|
|
4848
2818
|
} catch (error) {
|
|
@@ -4856,8 +2826,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
4856
2826
|
refuseRollback(`the branch failed (${String(error)})`, "session_before_compact", ctx);
|
|
4857
2827
|
return;
|
|
4858
2828
|
}
|
|
4859
|
-
// The invariant, measured rather than assumed: the rolled-back window has to be
|
|
4860
|
-
// exactly the failed entry shorter, or the retry sends something nobody built.
|
|
4861
2829
|
const errorEntryMessages = (sessionEntryMessages(entries[errorEntry.index]) ?? []).length;
|
|
4862
2830
|
const sessionMessagesAfter = rebuiltMessageCount(manager);
|
|
4863
2831
|
const holds = sessionMessagesBefore === null || sessionMessagesAfter === null ||
|
|
@@ -4880,8 +2848,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
4880
2848
|
replayed: replaySkipReason === null,
|
|
4881
2849
|
replaySkipReason,
|
|
4882
2850
|
});
|
|
4883
|
-
// Exactly ONE steered message. Steering drains one at a time at the top of the
|
|
4884
|
-
// retried loop, so a second would be silently held to a turn that never comes.
|
|
4885
2851
|
let replayed = false;
|
|
4886
2852
|
if (replaySkipReason === null) {
|
|
4887
2853
|
if (typeof pi.sendMessage !== "function") {
|
|
@@ -4926,18 +2892,12 @@ export function registerActiveContext(pi: any, options: {
|
|
|
4926
2892
|
noticeChars: notice.length,
|
|
4927
2893
|
attemptOrdinal: rollback.attempts,
|
|
4928
2894
|
};
|
|
4929
|
-
// The tree moved, so the mapping is rebuilt from it. THEN the fold is armed for the
|
|
4930
|
-
// retried pass, in that order: a reload clears the recovery flags, and arming before
|
|
4931
|
-
// it would hand the retry a cleared one. The guards are waived in that pass because
|
|
4932
|
-
// a request that already did not fit has nothing left to economize.
|
|
4933
2895
|
load(ctx, true);
|
|
4934
2896
|
curation.pendingRejection = { status: 400, ordinal: currentOrdinal() };
|
|
4935
2897
|
};
|
|
4936
2898
|
|
|
4937
|
-
pi.on("session_before_compact", (event: Record<string, unknown>, ctx: any) => {
|
|
2899
|
+
pi.on("session_before_compact", async (event: Record<string, unknown>, ctx: any) => {
|
|
4938
2900
|
const reason = ownValue(event, "reason");
|
|
4939
|
-
// The overflow reason is a provider rejection pi is prepared to retry, and it is
|
|
4940
|
-
// the rollback lane's trigger. Threshold and manual compaction are unchanged.
|
|
4941
2901
|
if (reason === "overflow" && ownValue(event, "willRetry") === true) {
|
|
4942
2902
|
nativeCompaction.lastThresholdDecision = {
|
|
4943
2903
|
handled: true,
|
|
@@ -4948,7 +2908,7 @@ export function registerActiveContext(pi: any, options: {
|
|
|
4948
2908
|
};
|
|
4949
2909
|
try { recoverFromOverflow(event, ctx); }
|
|
4950
2910
|
catch (error) { suspendAutomatic(error, "overflow-rollback", ctx); }
|
|
4951
|
-
try { updateStatus(ctx); } catch {
|
|
2911
|
+
try { updateStatus(ctx); } catch { }
|
|
4952
2912
|
return { cancel: true };
|
|
4953
2913
|
}
|
|
4954
2914
|
if (reason === "manual") {
|
|
@@ -4958,16 +2918,34 @@ export function registerActiveContext(pi: any, options: {
|
|
|
4958
2918
|
reason: "manual native compaction explicitly requested by the user",
|
|
4959
2919
|
compactionReason: reason,
|
|
4960
2920
|
};
|
|
4961
|
-
try { updateStatus(ctx); } catch {
|
|
2921
|
+
try { updateStatus(ctx); } catch { }
|
|
4962
2922
|
return undefined;
|
|
4963
2923
|
}
|
|
2924
|
+
let handoff: Record<string, unknown> | null = null;
|
|
2925
|
+
try {
|
|
2926
|
+
const snapshot = authoritativeSnapshotFor(ctx);
|
|
2927
|
+
handoff = await attemptAutomaticCommit(
|
|
2928
|
+
snapshot, ctx, "compaction-boundary", measurements.latestRatio,
|
|
2929
|
+
);
|
|
2930
|
+
} catch (error) {
|
|
2931
|
+
suspendAutomatic(error, "compaction-boundary", ctx);
|
|
2932
|
+
return undefined;
|
|
2933
|
+
}
|
|
2934
|
+
// CANCELLED WHETHER OR NOT ANYTHING WAS HANDED OFF. Letting Pi compact when the
|
|
2935
|
+
// boundary found nothing eligible is a silent fallback to a lossy summary, and
|
|
2936
|
+
// losslessness is the whole claim: a crossing that reclaims nothing is a STARVED
|
|
2937
|
+
// session, and the fence, the abort and the rollback lane already own that case and
|
|
2938
|
+
// say so out loud. The decision record still names which of the two happened.
|
|
4964
2939
|
nativeCompaction.lastThresholdDecision = {
|
|
4965
2940
|
handled: true,
|
|
4966
2941
|
retry: false,
|
|
4967
|
-
reason:
|
|
2942
|
+
reason: handoff
|
|
2943
|
+
? `${contextBrand(brandNoun)} handed the prefix off losslessly instead of compacting it`
|
|
2944
|
+
: `${contextBrand(brandNoun)} blocked stock automatic compaction with nothing eligible to hand off`,
|
|
4968
2945
|
compactionReason: reason,
|
|
4969
2946
|
nativeCompactionCompleted: false,
|
|
4970
2947
|
};
|
|
2948
|
+
try { updateStatus(ctx); } catch { }
|
|
4971
2949
|
return { cancel: true };
|
|
4972
2950
|
});
|
|
4973
2951
|
pi.on("agent_settled", async (_event: unknown, ctx: any) => {
|
|
@@ -4981,20 +2959,12 @@ export function registerActiveContext(pi: any, options: {
|
|
|
4981
2959
|
const snapshot = authoritativeSnapshotFor(ctx);
|
|
4982
2960
|
startPreparation(snapshot, measurements.latestRatio, ctx);
|
|
4983
2961
|
} catch {
|
|
4984
|
-
// The next authoritative context event will retry mapping.
|
|
4985
2962
|
}
|
|
4986
2963
|
try { await recoverNativeReceipts(ctx); }
|
|
4987
2964
|
catch (error) { safeNotify(ctx, `Native compaction receipt recovery remains pending: ${String(error)}`, "error"); }
|
|
4988
2965
|
updateStatus(ctx);
|
|
4989
2966
|
});
|
|
4990
2967
|
|
|
4991
|
-
/**
|
|
4992
|
-
* Admission control. A refusal that names no next action is denial, not governance,
|
|
4993
|
-
* so every refusal carries at least one CONSTRUCTIBLE alternative: a bounded slice
|
|
4994
|
-
* sized to the headroom we actually have, a child fold that is smaller by
|
|
4995
|
-
* construction, and the commit that frees the room. The exact stored size of the
|
|
4996
|
-
* target is known, so this is arithmetic, not a guess.
|
|
4997
|
-
*/
|
|
4998
2968
|
const admit = (input: {
|
|
4999
2969
|
action: "peek" | "expand";
|
|
5000
2970
|
ctx: any;
|
|
@@ -5021,8 +2991,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
5021
2991
|
alternatives.push({ action: "peek", id: child });
|
|
5022
2992
|
}
|
|
5023
2993
|
if (!alternatives.length) {
|
|
5024
|
-
// The floor: folding is always available, and it is the action that creates
|
|
5025
|
-
// the room this read needs.
|
|
5026
2994
|
alternatives.push({ action: "status", detail: "fold_candidates" });
|
|
5027
2995
|
}
|
|
5028
2996
|
throw new Error(
|
|
@@ -5062,8 +3030,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
5062
3030
|
}
|
|
5063
3031
|
const statusOffset = boundedInteger(params.offset, 0, 0, 1_000_000, "offset");
|
|
5064
3032
|
const statusLimit = boundedInteger(params.limit, 40, 1, 100, "limit");
|
|
5065
|
-
// Paging is explicit and agent-driven: the full tree is reachable, it just
|
|
5066
|
-
// stops riding along on every request that wanted a count.
|
|
5067
3033
|
const paged = detail === "folds" || detail === "objects";
|
|
5068
3034
|
const accounting = markAccounting(snapshot, persistence.state);
|
|
5069
3035
|
return toolPayload(boundStatusPayload({
|
|
@@ -5085,23 +3051,14 @@ export function registerActiveContext(pi: any, options: {
|
|
|
5085
3051
|
available: true,
|
|
5086
3052
|
automatic: {
|
|
5087
3053
|
pressureRatio: measurements.latestRatio,
|
|
5088
|
-
// Status is a READ of the slate, never a delivery of one. `status` is a
|
|
5089
|
-
// read-only context action and the tool-batch safety scan is built on
|
|
5090
|
-
// read-only actions not writing durable state, so asking must not issue a
|
|
5091
|
-
// suggestion, spend the precision budget, or move a suppression counter. An
|
|
5092
|
-
// agent that asks gets the answer; only the push carriers are metered.
|
|
5093
|
-
surfacing: statusSurfacing(snapshot),
|
|
5094
3054
|
refoldRatio: snapshot.policy.refoldRatio,
|
|
5095
3055
|
chapterPrepareRatio: snapshot.policy.prepareRatio,
|
|
5096
3056
|
hardFenceRatio: hardFenceRatio(snapshot),
|
|
5097
|
-
// The declared policy, reported. No action reads it back as a setting.
|
|
5098
3057
|
thresholds: { ...snapshot.thresholds },
|
|
5099
3058
|
zones: {
|
|
5100
3059
|
freshBoundary: snapshot.freshBoundary,
|
|
5101
3060
|
budgetTokens: snapshot.budgetTokens,
|
|
5102
3061
|
},
|
|
5103
|
-
// What THIS runtime withheld, which is nothing once the deployment declared an
|
|
5104
|
-
// already-net budget. One number, from the same accounting the fence divides by.
|
|
5105
3062
|
responseReserve: currentCapacity(ctx).outputReservation,
|
|
5106
3063
|
windowSource: snapshot.windowSource,
|
|
5107
3064
|
capacity: {
|
|
@@ -5130,13 +3087,11 @@ export function registerActiveContext(pi: any, options: {
|
|
|
5130
3087
|
overBudgetReduction: ladder.overBudgetReduction ? clone(ladder.overBudgetReduction) : null,
|
|
5131
3088
|
curation: {
|
|
5132
3089
|
occupancyThreshold: thresholds.maxTarget,
|
|
5133
|
-
signals: curation.lastSignals ? { ...curation.lastSignals } : null,
|
|
5134
3090
|
contextCalls: curation.contextCalls,
|
|
5135
3091
|
receipts: curation.receipts.map((receipt) => ({ ...receipt })),
|
|
5136
3092
|
},
|
|
5137
3093
|
recovery: {
|
|
5138
3094
|
attempts: curation.recoveryAttempts,
|
|
5139
|
-
maxAttempts: OVERFLOW_RECOVERY_MAX_ATTEMPTS,
|
|
5140
3095
|
pendingRejection: curation.pendingRejection ? { ...curation.pendingRejection } : null,
|
|
5141
3096
|
last: curation.lastRecovery ? clone(curation.lastRecovery) : null,
|
|
5142
3097
|
rollback: {
|
|
@@ -5152,9 +3107,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
5152
3107
|
projectionCharsPerToken: projectionCharsPerToken(),
|
|
5153
3108
|
projectionEstimatorErrorShare: estimatorErrorShare(),
|
|
5154
3109
|
projectionExpectedInflowTokens: expectedInflowTokens(),
|
|
5155
|
-
// What the fence actually weighed, not a second arithmetic over the same bytes:
|
|
5156
|
-
// once occupancy is anchored, re-deriving it here from chars alone would report
|
|
5157
|
-
// a number no decision was made on.
|
|
5158
3110
|
projectionMarginTokens: measurements.lastProjectedEstimate === null
|
|
5159
3111
|
? null
|
|
5160
3112
|
: projectionMarginTokens(measurements.lastProjectedEstimate, currentCapacity(ctx).window),
|
|
@@ -5175,7 +3127,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
5175
3127
|
...ledgerSummary(instrumentation.ledger),
|
|
5176
3128
|
projectionRecords: clone(instrumentation.ledger.records),
|
|
5177
3129
|
cacheObservations: clone(instrumentation.ledger.observations),
|
|
5178
|
-
// THE stream, as the durable entries carry it. One shape, one convention.
|
|
5179
3130
|
events: clone(instrumentation.ledger.events),
|
|
5180
3131
|
},
|
|
5181
3132
|
peek: {
|
|
@@ -5188,32 +3139,20 @@ export function registerActiveContext(pi: any, options: {
|
|
|
5188
3139
|
},
|
|
5189
3140
|
...(detail === "fold_candidates" ? {
|
|
5190
3141
|
candidates: foldCandidatesDetail(snapshot, persistence.state, measurements.latestRatio, {
|
|
5191
|
-
summarizerAvailable: Boolean(options.summarizeContextSpan),
|
|
5192
3142
|
generation: lifecycle.generation,
|
|
5193
3143
|
measurementFresh: Boolean(measurements.lastProviderMeasurement &&
|
|
5194
3144
|
durableProviderMeasurementMatches(measurements.lastProviderMeasurement)),
|
|
5195
3145
|
automaticFailure: ladder.automaticFailure !== null,
|
|
5196
3146
|
preparing: Boolean(ladder.preparing),
|
|
5197
|
-
failedPreparationIds: ladder.failedPreparations,
|
|
5198
3147
|
}),
|
|
5199
3148
|
} : {}),
|
|
5200
3149
|
...(detail === "tree" ? { tree: foldTreeDetail(snapshot, persistence.state).slice(statusOffset) } : {}),
|
|
5201
3150
|
}, typeof detail === "string" ? detail : null, statusOffset));
|
|
5202
3151
|
}
|
|
5203
|
-
// Peeking or expanding a surfaced fold is the ACTED label, joined to the offer that
|
|
5204
|
-
// named it. It stays ephemeral: the ledger rides the next persisted state, never an
|
|
5205
|
-
// entry of its own, and the used/ignored grades land when the window closes.
|
|
5206
|
-
const noteSurfacingAccept = (id: string): void => {
|
|
5207
|
-
if (!persistence.state) return;
|
|
5208
|
-
persistence.state = noteSurfacingAction(persistence.state, id, markOrdinal(snapshot));
|
|
5209
|
-
};
|
|
5210
3152
|
if (action === "peek") {
|
|
5211
3153
|
const id = String(params.id ?? "").trim();
|
|
5212
3154
|
if (!id) throw new Error("peek requires id");
|
|
5213
3155
|
const offset = boundedInteger(params.offset, 0, 0, 1_000_000_000, "offset");
|
|
5214
|
-
// The DEFAULT is the bounded index view. Widening is an explicit argument, so
|
|
5215
|
-
// the default path cannot overfill a window: measured 2026-08-06, 14 raw peek
|
|
5216
|
-
// results held 1.9M characters, 82 percent of everything still unfolded.
|
|
5217
3156
|
const sliceBytes = boundedInteger(
|
|
5218
3157
|
params.bytes,
|
|
5219
3158
|
PEEK_DEFAULT_MAX_BYTES,
|
|
@@ -5239,7 +3178,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
5239
3178
|
offset,
|
|
5240
3179
|
toolName,
|
|
5241
3180
|
}));
|
|
5242
|
-
noteSurfacingAccept(id);
|
|
5243
3181
|
return payload;
|
|
5244
3182
|
}
|
|
5245
3183
|
if (action === "rebrief") {
|
|
@@ -5262,15 +3200,11 @@ export function registerActiveContext(pi: any, options: {
|
|
|
5262
3200
|
brief,
|
|
5263
3201
|
previousBrief: previous,
|
|
5264
3202
|
durableRevision: persistence.state.revision,
|
|
5265
|
-
activation: "the fold's placeholder
|
|
3203
|
+
activation: "the fold's placeholder and index rows now carry your brief; " +
|
|
5266
3204
|
"the exact source and its fold record are untouched.",
|
|
5267
3205
|
});
|
|
5268
3206
|
}
|
|
5269
3207
|
if (action === "reboundary") {
|
|
5270
|
-
// One verb, both directions. `ids` names the span you want to BE one fold: every
|
|
5271
|
-
// fold overlapping it dissolves and the span is re-cut as one, which merges N
|
|
5272
|
-
// adjacent folds when the span covers them and splits one fold when the span sits
|
|
5273
|
-
// inside it. `id` alone is the plain dissolve, which returns a span to raw.
|
|
5274
3208
|
if (params.ids !== undefined) {
|
|
5275
3209
|
const requestedIds = stringIds(params.ids);
|
|
5276
3210
|
const snapped = snapToFoldBoundaries(snapshot, persistence.state, requestedIds);
|
|
@@ -5297,10 +3231,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
5297
3231
|
const created: Array<Record<string, unknown>> = [];
|
|
5298
3232
|
const correctedBriefs: Record<string, string> = {};
|
|
5299
3233
|
for (const part of splitCandidateBySize(snapshot, persistence.state, recut)) {
|
|
5300
|
-
// A re-cut that lands back on a span this session already folded meets its own
|
|
5301
|
-
// immutable record. The record wins -- rewriting it would report a conflicting
|
|
5302
|
-
// durable fold and suspend automatic management -- and the supplied brief
|
|
5303
|
-
// becomes a correction beside it, which is what `rebrief` writes anyway.
|
|
5304
3234
|
const durable = persistence.persistedFoldRecords.get(foldIdFor(part.kind, part.parts));
|
|
5305
3235
|
const { preparedFold, nextState } = await prepareAndCommitExplicit({
|
|
5306
3236
|
snapshot, candidate: part, brief: durable ? durable.fold.brief : supplied, ctx, signal,
|
|
@@ -5375,8 +3305,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
5375
3305
|
if (action === "expand" || action === "refold") {
|
|
5376
3306
|
const id = String(params.id ?? "").trim();
|
|
5377
3307
|
if (!id) throw new Error(`${action} requires id`);
|
|
5378
|
-
// An expand with marks pending opens the commit epoch: the restore and the
|
|
5379
|
-
// batch of folds then cost one rewrite between them instead of two.
|
|
5380
3308
|
let epochApplied: unknown[] = [];
|
|
5381
3309
|
if (action === "expand" && pendingMarks(persistence.state).length) {
|
|
5382
3310
|
const result = await commitPendingMarks({
|
|
@@ -5397,7 +3325,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
5397
3325
|
requestedBytes: Math.max(0, expanding.sourceChars - expanding.placeholderChars),
|
|
5398
3326
|
children: childFoldIds(expanding),
|
|
5399
3327
|
});
|
|
5400
|
-
noteSurfacingAccept(id);
|
|
5401
3328
|
}
|
|
5402
3329
|
let next = setFoldProjectionState(persistence.state, id, action === "expand" ? "expanded" : "folded");
|
|
5403
3330
|
if (action === "expand") next = withExpandLease(next, id);
|
|
@@ -5426,9 +3353,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
5426
3353
|
const refsBefore = persistence.state.protected.length;
|
|
5427
3354
|
const nextProtection = protectEvidence(snapshot, persistence.state, ids, action === "protect");
|
|
5428
3355
|
if (action === "protect") {
|
|
5429
|
-
// The pin ceiling: protect is a promise to hold bytes raw through every fold,
|
|
5430
|
-
// and a promise with no mass bound can pin the window solid. The refusal is
|
|
5431
|
-
// corrective, not denial: it names the cap and the release valve.
|
|
5432
3356
|
const capacity = servingCapacity(snapshot.contextWindow);
|
|
5433
3357
|
const prospectiveShare = capacity.budgetTokens > 0
|
|
5434
3358
|
? estimatedTokens(explicitProtectedMass(snapshot, nextProtection).bytes) / capacity.budgetTokens
|
|
@@ -5482,9 +3406,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
5482
3406
|
});
|
|
5483
3407
|
}
|
|
5484
3408
|
if (action === "fold") {
|
|
5485
|
-
// One call, several {span, brief} pairs. Marking is free and committing is not,
|
|
5486
|
-
// so the shape that should be cheapest to express is the BATCH: an agent that
|
|
5487
|
-
// must spend one call per span curates less than one that spends one call.
|
|
5488
3409
|
const requested = batchedMarkRequests(params);
|
|
5489
3410
|
const resolved: Array<{
|
|
5490
3411
|
candidate: FoldCandidate;
|
|
@@ -5527,33 +3448,28 @@ export function registerActiveContext(pi: any, options: {
|
|
|
5527
3448
|
}
|
|
5528
3449
|
}
|
|
5529
3450
|
const corrections = resolved.flatMap((item) => item.corrections);
|
|
5530
|
-
// Resolve every brief now, while the source is in hand, so the commit epoch
|
|
5531
|
-
// itself stays deterministic and free of provider calls. A span that is not
|
|
5532
|
-
// eligible YET cannot go through preparation at all, so it takes the
|
|
5533
|
-
// deterministic brief: the mark is a decision, and refusing to record it
|
|
5534
|
-
// because the span is momentarily fresh is exactly the drop being fixed.
|
|
5535
3451
|
const marks: Array<Record<string, unknown>> = [];
|
|
5536
3452
|
let staged = persistence.state;
|
|
5537
3453
|
for (const item of resolved) {
|
|
5538
3454
|
const { candidate } = item;
|
|
5539
|
-
// ACCEPT AND HOLD. A span that is still fresh or protected is not a refusal:
|
|
5540
|
-
// the mark is a standing decision, it is recorded now, and it folds at the
|
|
5541
|
-
// first commit after the span ages out. Refusal is reserved for a mark that
|
|
5542
|
-
// cannot be constructed at all.
|
|
5543
|
-
// A REPEATED DECISION IS INERT. The fold id is derived from the span, so a
|
|
5544
|
-
// span the ladder already prepared carries that exact id in the forest
|
|
5545
|
-
// already, and preparing it a second time throws "Prepared fold already
|
|
5546
|
-
// exists" out of the tool call. Under the old cadence commits were frequent
|
|
5547
|
-
// enough to drain prepared folds before a second decision could land on one;
|
|
5548
|
-
// the quiet runtime commits far less often, so the collision became reachable
|
|
5549
|
-
// in ordinary use -- the ladder marks a span, the agent then folds the same
|
|
5550
|
-
// span, and the agent's call fails. Marking is a standing decision about a
|
|
5551
|
-
// span, and deciding the same thing twice has to be a no-op.
|
|
5552
3455
|
const alreadyPrepared = staged.folds.some((fold) =>
|
|
5553
3456
|
fold.id === foldIdFor(candidate.kind, candidate.parts));
|
|
5554
3457
|
const deferred = refsProtected(candidate.sourceRefs, staged, snapshot) ||
|
|
5555
3458
|
(candidate.kind === "tool-result" &&
|
|
5556
3459
|
toolRefsProtected(candidate.sourceRefs, staged, snapshot));
|
|
3460
|
+
const suppliedComplaint = item.brief === undefined
|
|
3461
|
+
? null
|
|
3462
|
+
: briefContractComplaint(item.brief.trim(), snapshot.policy.maxBriefChars, snapshot.toolName);
|
|
3463
|
+
if (suppliedComplaint !== null) {
|
|
3464
|
+
marks.push({
|
|
3465
|
+
id: foldIdFor(candidate.kind, candidate.parts),
|
|
3466
|
+
kind: candidate.kind,
|
|
3467
|
+
ok: false,
|
|
3468
|
+
deferred: false,
|
|
3469
|
+
reason: `Supplied brief rejected. ${suppliedComplaint}`,
|
|
3470
|
+
});
|
|
3471
|
+
continue;
|
|
3472
|
+
}
|
|
5557
3473
|
const briefed = !deferred && !alreadyPrepared
|
|
5558
3474
|
? await prepareFold({
|
|
5559
3475
|
candidate,
|
|
@@ -5561,7 +3477,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
5561
3477
|
state: staged,
|
|
5562
3478
|
generation: lifecycle.generation,
|
|
5563
3479
|
brief: item.brief,
|
|
5564
|
-
summarize: observedSummarize,
|
|
5565
3480
|
ctx,
|
|
5566
3481
|
signal,
|
|
5567
3482
|
})
|
|
@@ -5598,18 +3513,12 @@ export function registerActiveContext(pi: any, options: {
|
|
|
5598
3513
|
await persistManual(staged, action, ctx);
|
|
5599
3514
|
updateStatus(ctx);
|
|
5600
3515
|
const accounting = markAccounting(snapshot, persistence.state);
|
|
5601
|
-
// MECHANISM 3. The projection is byte-frozen between fold events, so this reply
|
|
5602
|
-
// is the only cache-free channel left: it carries the whole picture, not just
|
|
5603
|
-
// an acknowledgement of the spans this call named.
|
|
5604
3516
|
const held = pendingMarks(persistence.state).map((mark) => ({
|
|
5605
3517
|
id: mark.id,
|
|
5606
3518
|
kind: mark.kind,
|
|
5607
3519
|
tokens: estimatedTokens(markFreedBytes(snapshot, persistence.state!, mark)),
|
|
5608
3520
|
}));
|
|
5609
3521
|
const remainder = unmarkedRemainder(snapshot, persistence.state, projectionCharsPerToken());
|
|
5610
|
-
// The single-span shape is the head of the batched one: one call carrying one
|
|
5611
|
-
// span answers exactly as it always did, and a batch adds fields rather than
|
|
5612
|
-
// replacing them.
|
|
5613
3522
|
const only = marks.length === 1 && marks[0].ok === true ? marks[0] : null;
|
|
5614
3523
|
return toolPayload({
|
|
5615
3524
|
version: 1,
|
|
@@ -5628,7 +3537,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
5628
3537
|
}
|
|
5629
3538
|
: {}),
|
|
5630
3539
|
marks,
|
|
5631
|
-
// Never silently reinterpreted: a span the runtime moved says so, here.
|
|
5632
3540
|
corrections,
|
|
5633
3541
|
argumentsSha256: executionArgumentsSha256,
|
|
5634
3542
|
durableRevision: persistence.state.revision,
|
|
@@ -5644,9 +3552,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
5644
3552
|
unmarkedTokens: remainder.tokens,
|
|
5645
3553
|
unmarkedShare: remainder.share,
|
|
5646
3554
|
unmarkedCandidates: remainder.candidates,
|
|
5647
|
-
// The acknowledgement rides the tool result, so it PERSISTS in context the way
|
|
5648
|
-
// every tool result does. Default on;
|
|
5649
|
-
// `guidance.actionResponses: false` returns the accounting without the prose.
|
|
5650
3555
|
...(guidance.actionResponses ? {
|
|
5651
3556
|
awareness: markAwarenessText({ held, remainder, toolName, brandNoun }),
|
|
5652
3557
|
activation: "accepted as pending marks; no context bytes moved, and nothing else in your " +
|
|
@@ -5660,14 +3565,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
5660
3565
|
}
|
|
5661
3566
|
throw new Error(`Unknown ${toolName} action '${action}'`);
|
|
5662
3567
|
};
|
|
5663
|
-
/**
|
|
5664
|
-
* Every context-management call, accepted AND refused, in the durable stream.
|
|
5665
|
-
*
|
|
5666
|
-
* A rejected call is the more informative of the two: an agent whose spans keep being
|
|
5667
|
-
* refused looks identical, from fold records alone, to an agent that never tried to
|
|
5668
|
-
* curate at all. The exact validation text is recorded verbatim, because "it failed"
|
|
5669
|
-
* is not a finding and "ids must contain 1-64 values" is.
|
|
5670
|
-
*/
|
|
5671
3568
|
const requestedMarkCount = (params: Record<string, unknown>): number => {
|
|
5672
3569
|
const batched = denseOwnArrayValues(params?.marks);
|
|
5673
3570
|
if (batched) return batched.length;
|
|
@@ -5683,23 +3580,17 @@ export function registerActiveContext(pi: any, options: {
|
|
|
5683
3580
|
): Promise<unknown> => {
|
|
5684
3581
|
const operation = ladder.actionQueue.then(async () => {
|
|
5685
3582
|
const action = String(params?.action ?? "");
|
|
5686
|
-
// The gate's engagement signal, counted BEFORE the call can fail: an agent that
|
|
5687
|
-
// reached for a context verb engaged, whether or not the span was valid.
|
|
5688
3583
|
curation.contextCalls += 1;
|
|
5689
3584
|
const attempt = (ok: boolean, error: string | null, corrections: Array<Record<string, unknown>>) => {
|
|
5690
3585
|
const record = emit("context.attempt", {
|
|
5691
3586
|
action,
|
|
5692
3587
|
ok,
|
|
5693
3588
|
error,
|
|
5694
|
-
// The join key against the worker's own tool-call log: one logical row per
|
|
5695
|
-
// model-emitted invocation, matched by id and never by order or clock.
|
|
5696
3589
|
tool_call_id: toolCallId,
|
|
5697
3590
|
marks_requested: requestedMarkCount(params),
|
|
5698
3591
|
corrections_applied: corrections.length,
|
|
5699
3592
|
arguments_sha256: sha256Value(params ?? {}),
|
|
5700
3593
|
});
|
|
5701
|
-
// One record per correction, referencing its attempt: nothing nests, and a
|
|
5702
|
-
// reader counting corrections never has to unpack an array to do it.
|
|
5703
3594
|
for (const correction of corrections) {
|
|
5704
3595
|
const from = denseOwnArrayValues(ownValue(correction, "from")) ?? [];
|
|
5705
3596
|
const to = denseOwnArrayValues(ownValue(correction, "to")) ?? [];
|
|
@@ -5751,15 +3642,6 @@ export function registerActiveContext(pi: any, options: {
|
|
|
5751
3642
|
if (!lifecycle.latestSnapshot) lifecycle.latestSnapshot = snapshot;
|
|
5752
3643
|
const divider = args.indexOf(" -- ");
|
|
5753
3644
|
const selector = (divider >= 0 ? args.slice(0, divider) : args).trim();
|
|
5754
|
-
// THE USER COMMIT. One word, and the only commit verb any caller has: the model
|
|
5755
|
-
// tool still has no commit action, because a verb the runtime is entitled to
|
|
5756
|
-
// overrule is surface without function. A user is not overruled.
|
|
5757
|
-
//
|
|
5758
|
-
// Below maxTarget it applies the eligible marks in hand and stops -- no automatic
|
|
5759
|
-
// top-up, because the user asked to bank the curation they made, not to have the
|
|
5760
|
-
// runtime pick more. At or above maxTarget it is the ordinary event: the planner
|
|
5761
|
-
// may top up toward minTarget. Neither path touches freshness, pins, exactness or
|
|
5762
|
-
// the provider fence.
|
|
5763
3645
|
if (selector === "commit") {
|
|
5764
3646
|
const capacity = servingCapacity(snapshot.contextWindow);
|
|
5765
3647
|
const occupancy = capacity.usedTokens !== null && capacity.budgetTokens > 0
|
|
@@ -5767,7 +3649,16 @@ export function registerActiveContext(pi: any, options: {
|
|
|
5767
3649
|
: null;
|
|
5768
3650
|
const topUp = occupancy !== null && occupancy >= thresholds.maxTarget;
|
|
5769
3651
|
const committed = await runCommitEpoch(snapshot, "user-command", topUp, measurements.latestRatio, true);
|
|
5770
|
-
|
|
3652
|
+
// Until this lands the epoch's folds are in the event stream and nowhere else.
|
|
3653
|
+
// Swallowing the failure here announced a commit that does not exist and left the
|
|
3654
|
+
// session computing folds it would keep throwing away, which is the dead session
|
|
3655
|
+
// gate 122 was built for, reached through the user's own command instead.
|
|
3656
|
+
try { await persist(ctx); }
|
|
3657
|
+
catch (error) {
|
|
3658
|
+
suspendAutomatic(error, "user-command", ctx);
|
|
3659
|
+
updateStatus(ctx);
|
|
3660
|
+
throw error;
|
|
3661
|
+
}
|
|
5771
3662
|
updateStatus(ctx);
|
|
5772
3663
|
safeNotify(
|
|
5773
3664
|
ctx,
|
|
@@ -5826,7 +3717,7 @@ export function registerActiveContext(pi: any, options: {
|
|
|
5826
3717
|
ladder.actionQueue = operation.catch(() => undefined);
|
|
5827
3718
|
try { await operation; }
|
|
5828
3719
|
catch (error) {
|
|
5829
|
-
safeNotify(ctx, `Context
|
|
3720
|
+
safeNotify(ctx, `Context command failed: ${error instanceof Error ? error.message : String(error)}`, "error");
|
|
5830
3721
|
}
|
|
5831
3722
|
};
|
|
5832
3723
|
|
|
@@ -5856,12 +3747,11 @@ export function registerActiveContext(pi: any, options: {
|
|
|
5856
3747
|
lifecycle.generation += 1;
|
|
5857
3748
|
lifecycle.shuttingDown = true;
|
|
5858
3749
|
cancelPreparation();
|
|
5859
|
-
cancelBriefUpgrades();
|
|
5860
3750
|
persistence.state = null;
|
|
5861
3751
|
persistence.persisted = null;
|
|
5862
3752
|
lifecycle.latestSnapshot = null;
|
|
5863
3753
|
curation.receipts = [];
|
|
5864
|
-
try { ctx.ui?.setStatus?.(entryTypePrefix, undefined); } catch {
|
|
3754
|
+
try { ctx.ui?.setStatus?.(entryTypePrefix, undefined); } catch { }
|
|
5865
3755
|
});
|
|
5866
3756
|
|
|
5867
3757
|
return { projectionCandidates };
|